@desplega.ai/agent-swarm 2.4.4 → 2.5.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-thq04adh.js → acp-adapter-11gp67nr.js} +8 -6
- package/dist/{actions-j6ywh6x5.js → actions-8p2kze8j.js} +24 -22
- package/dist/{amp-adapter-k8b5xmj8.js → amp-adapter-z4qvhacj.js} +2 -2
- package/dist/{anthropic-messages-g6pqmqk4.js → anthropic-messages-txs190dd.js} +17 -20
- package/dist/{app-rb7vc02s.js → app-0qp0a2gk.js} +3 -3
- package/dist/{assistant-ftta1zjf.js → assistant-jedd3ktm.js} +27 -25
- package/dist/{azure-openai-responses-9dbmj346.js → azure-openai-responses-9kfwhmn7.js} +18 -13
- package/dist/{boot-reembed-xns7h5h9.js → boot-reembed-4xwq5tg0.js} +1 -1
- package/dist/{boot-reembed-srp6v6af.js → boot-reembed-n4wq5dd2.js} +1 -1
- package/dist/{boot-scrub-sweep-3qqrh2mz.js → boot-scrub-sweep-69h90xcq.js} +4 -4
- package/dist/{boot-seeds-9ks8hce7.js → boot-seeds-3vn0m9sg.js} +4 -4
- package/dist/{claude-adapter-k5ksyres.js → claude-adapter-221mvad5.js} +8 -6
- package/dist/{claude-managed-adapter-h2y889ge.js → claude-managed-adapter-gt6pa35d.js} +2 -2
- package/dist/{claude-managed-setup-n5a5eqks.js → claude-managed-setup-c3hp36ph.js} +4 -4
- package/dist/{claude-sdk-session-2j3j4kwn.js → claude-sdk-session-8thjsk5e.js} +9729 -9628
- package/dist/{cli-b9fcxxev.js → cli-007cnhm1.js} +50 -4
- package/dist/{cli-j4rqdf2n.js → cli-03vpq45j.js} +2 -2
- package/dist/{cli-amyhm3gj.js → cli-0y6f0ckq.js} +1 -1
- package/dist/{cli-4dg55wsk.js → cli-0z3vjrpm.js} +1 -1
- package/dist/{cli-pccfmtb5.js → cli-179xcyky.js} +11 -11
- package/dist/{cli-3r8samv3.js → cli-1f0gp9vz.js} +3 -2
- package/dist/{cli-hj3debgw.js → cli-1pr672hz.js} +3 -3
- package/dist/{cli-0bqteszy.js → cli-2nf3b08t.js} +328 -25
- package/dist/{cli-t7cyef5g.js → cli-36j4jnk3.js} +4 -4
- package/dist/{cli-er83cjbn.js → cli-3qv8naq8.js} +527 -231
- package/dist/cli-3zwye9sn.js +1193 -0
- package/dist/{cli-60cgh1rv.js → cli-459fk97x.js} +7 -7
- package/dist/{cli-xj0re5tm.js → cli-4w24f7z3.js} +1 -1
- package/dist/{cli-v3yr2gbg.js → cli-5kzk3472.js} +2198 -968
- package/dist/{cli-h7gc0b4r.js → cli-5m84zd2k.js} +55 -18
- package/dist/{cli-w6nadkh4.js → cli-5rvg1ddk.js} +5 -5
- package/dist/{cli-dvkmp9bt.js → cli-6ctwrf1z.js} +20 -10
- package/dist/{cli-9qtjfd60.js → cli-6rg2jn2y.js} +3 -2
- package/dist/{cli-2v045xyf.js → cli-7bwtc6yx.js} +3 -3
- package/dist/{cli-3cgngyd8.js → cli-7g40g8hq.js} +35 -17
- package/dist/{cli-q6r7t299.js → cli-92mqenkc.js} +2379 -154
- package/dist/{cli-cynvc9fj.js → cli-9ce2xw98.js} +8 -8
- package/dist/{cli-gqvh8d8a.js → cli-bgdtv62r.js} +36 -22
- package/dist/{cli-wxbn3yfx.js → cli-cz5nmdxt.js} +2 -2
- package/dist/{cli-wt645v7q.js → cli-dabehxhp.js} +1 -1
- package/dist/{cli-vfha21c9.js → cli-e5bxnjps.js} +2 -2
- package/dist/{cli-p20r8w42.js → cli-fcc5qe3f.js} +763 -268
- package/dist/{cli-1ephgwnq.js → cli-fes51ya2.js} +3 -3
- package/dist/{cli-1d70crsy.js → cli-ftmq4nvk.js} +2 -2
- package/dist/{cli-qkhc5aqy.js → cli-fy1jyyym.js} +13 -13
- package/dist/{cli-s95dknxh.js → cli-g1cw7xtt.js} +1 -1
- package/dist/{cli-qje9k2nh.js → cli-g9ypd5en.js} +12 -2
- package/dist/{cli-74c1wpm0.js → cli-gpv60278.js} +1 -1
- package/dist/{cli-pemywhfq.js → cli-ha9rmwgq.js} +2 -1
- package/dist/{cli-bnz0r3kc.js → cli-j1eghqfm.js} +5 -5
- package/dist/{cli-2zqpkvtt.js → cli-jpxsrxcb.js} +1 -1
- package/dist/{cli-ejs3m1sx.js → cli-kxpfx2xr.js} +1 -1
- package/dist/{cli-37yb7307.js → cli-mbfj11fq.js} +1 -1
- package/dist/{cli-pnp2j5zg.js → cli-mmhj8vd7.js} +2 -2
- package/dist/{cli-xf234ws8.js → cli-n4c8e3s5.js} +2 -0
- package/dist/{cli-a0kcty51.js → cli-n6kmqngw.js} +2 -2
- package/dist/{cli-te6b5wvg.js → cli-nnvjb17s.js} +3 -3
- package/dist/{cli-1dmcq2eh.js → cli-nzfnt2gs.js} +1 -1
- package/dist/{cli-625tb7aq.js → cli-p859hcc8.js} +2 -2
- package/dist/{cli-smvcw9z5.js → cli-pasyvjmm.js} +1 -1
- package/dist/cli-pkaqk57w.js +128 -0
- package/dist/{cli-8nyhf7za.js → cli-qqzmjxkf.js} +129 -105
- package/dist/{cli-zrwpctp4.js → cli-r25qpaxt.js} +161 -63
- package/dist/{cli-nc35pxb7.js → cli-rh5smt8t.js} +19 -4
- package/dist/{cli-qwkcwjdn.js → cli-rycdaj0a.js} +1 -1
- package/dist/{cli-esxerzg7.js → cli-s4sntwt4.js} +2 -2
- package/dist/{cli-b8k7epj1.js → cli-segs7hx9.js} +8 -8
- package/dist/{cli-36ckw0wx.js → cli-stxdrqs2.js} +2 -2
- package/dist/{cli-vdm20wej.js → cli-sy8f9705.js} +4 -4
- package/dist/{cli-1m17dc9d.js → cli-ta8bd7th.js} +2 -2
- package/dist/{cli-2y91wnbh.js → cli-we0ra057.js} +222 -10
- package/dist/{cli-55qmbp6z.js → cli-x21dqn18.js} +5 -5
- package/dist/{cli-dqb4n629.js → cli-x6vf4sad.js} +11 -11
- package/dist/{cli-d2mb5s8a.js → cli-x77gen2p.js} +680 -440
- package/dist/{cli-vq97g1jq.js → cli-xawhpgc9.js} +95 -195
- package/dist/{cli-82d3nkc3.js → cli-xd4eqtr2.js} +2 -2
- package/dist/{cli-9fhkkpj5.js → cli-xt768r5m.js} +2 -2
- package/dist/{cli-zrk6jxv8.js → cli-y4x8ww6s.js} +2 -2
- package/dist/{cli-xc27fwzg.js → cli-y9jvxfhk.js} +3 -3
- package/dist/{cli-44159k28.js → cli-z63a269p.js} +2 -2
- package/dist/{cli-sm8rses9.js → cli-zfdp01hn.js} +8 -5
- package/dist/cli.js +31 -29
- package/dist/{cloudflare-workers-ai-system-one-ch2wxfcp.js → cloudflare-workers-ai-system-one-43v9qkjk.js} +7 -2
- package/dist/{codex-adapter-968xwfy7.js → codex-adapter-tcg1q3jk.js} +6 -4
- package/dist/{codex-login-m819c41r.js → codex-login-rn7c5gxt.js} +1 -1
- package/dist/{codex-session-runner-ht4z99fa.js → codex-session-runner-xfx4wd9x.js} +7 -5
- package/dist/{commands-pcymaeg9.js → commands-ew83nj70.js} +2 -2
- package/dist/{controller-fpjyk46r.js → controller-gnq1k1mf.js} +4 -4
- package/dist/{controllers-connect-mwzx1fh6.js → controllers-connect-tw1dan9r.js} +26 -24
- package/dist/{cursor-adapter-cp4s2e2f.js → cursor-adapter-yq5xgfcg.js} +1 -1
- package/dist/{db-xsjg9jqq.js → db-7gbc53fd.js} +14 -2
- package/dist/{db-sgd12bt9.js → db-t86xrnes.js} +3 -3
- package/dist/{devin-adapter-zgqadvnm.js → devin-adapter-4ycy4vrf.js} +1 -1
- package/dist/{dispatcher-x0jfw2c9.js → dispatcher-64bdyhps.js} +23 -21
- package/dist/{docker-fkgtr75t.js → docker-nfy2gc8t.js} +2 -2
- package/dist/{dsh-adapter-jrq7ma7h.js → dsh-adapter-tvhc4k2d.js} +1 -1
- package/dist/{e2b-16d6ch39.js → e2b-9gkdhm3c.js} +2 -2
- package/dist/{e2b-zn0b4w5w.js → e2b-cjtf8k1z.js} +2 -2
- package/dist/{easter-egg-3d-5cgynaja.js → easter-egg-3d-e4qv8dxj.js} +1 -1
- package/dist/{event-bus-9vpk8gmq.js → event-bus-3z78ep5n.js} +1 -1
- package/dist/{execute-t4e6m3rw.js → execute-0xpz8950.js} +85 -23
- package/dist/{external-channel-8njj4h3b.js → external-channel-e9nw24zv.js} +2 -2
- package/dist/{google-generative-ai-bmq8gkf3.js → google-generative-ai-mk9vcpe1.js} +15 -15
- package/dist/{google-vertex-b1rvp52s.js → google-vertex-q580kz8k.js} +20 -20
- package/dist/{grok-adapter-kkr97v2f.js → grok-adapter-v82srhrx.js} +9 -7
- package/dist/handlers-c9ajzhbn.js +41 -0
- package/dist/{heartbeat-wd03zhn0.js → heartbeat-2vqs1319.js} +26 -24
- package/dist/{hook-xzj2ts2p.js → hook-01nk13wx.js} +7 -5
- package/dist/{hook-f0vghj42.js → hook-jzpheegg.js} +9 -7
- package/dist/{http-4hwxv62p.js → http-fc1k6ec8.js} +859 -252
- package/dist/index-07ef7nkm.js +42 -0
- package/dist/{index-g6tbb6ve.js → index-31ceyf42.js} +1 -1
- package/dist/index-7yts8n9s.js +43 -0
- package/dist/{index-c4ykd0f1.js → index-dr94zbnh.js} +13348 -11611
- package/dist/index-q3t40ryv.js +46 -0
- package/dist/{index-ww2vznkp.js → index-sepja052.js} +1 -1
- package/dist/{index-97q4xn03.js → index-zdycv3ja.js} +135 -79
- package/dist/{index-g216q6jr.js → index-zsyx51ap.js} +1 -4
- package/dist/{keepalive-hrh7xnr9.js → keepalive-5nh2rhmr.js} +4 -4
- package/dist/lead-aayq64jn.js +79 -0
- package/dist/{llama-cpp-classify-vv43gswy.js → llama-cpp-classify-ee7vmd7q.js} +4 -1
- package/dist/{maintenance-e8wnwnqx.js → maintenance-hjhzdt3k.js} +1 -1
- package/dist/{mistral-conversations-1medfwv7.js → mistral-conversations-sd0xf1tn.js} +13 -13
- package/dist/{node-3v3f08f7.js → node-jzjj7r9x.js} +10 -9
- package/dist/{oauth-refresh-sweep-zm0yj4sj.js → oauth-refresh-sweep-y3n1knmd.js} +3 -3
- package/dist/{onboard-dtwcq603.js → onboard-1zq3m7db.js} +2 -2
- package/dist/{openai-codex-responses-de9pvs4d.js → openai-codex-responses-79s8xnbh.js} +29 -23
- package/dist/{openai-completions-mtqge3nf.js → openai-completions-tdbyts0x.js} +21 -20
- package/dist/{openai-decisions-ygk4kkxd.js → openai-decisions-bqhnjyfz.js} +2 -1
- package/dist/{openai-responses-dkyng71g.js → openai-responses-dm3t5vqp.js} +26 -17
- package/dist/{opencode-adapter-x4tqzhkh.js → opencode-adapter-e56086m1.js} +7 -5
- package/dist/{openrouter-images-wvv7camj.js → openrouter-images-ssttr1y4.js} +6 -3
- package/dist/{otel-impl-w3fbhpn0.js → otel-impl-h9yyza47.js} +2 -2
- package/dist/{pi-messages-nhh1pkz2.js → pi-messages-3nf8bq1a.js} +6 -4
- package/dist/{pi-mono-adapter-mcfkn9vf.js → pi-mono-adapter-tzps8f38.js} +9 -37
- package/dist/{pricing-refresh-j38dehfs.js → pricing-refresh-nevhat8h.js} +3 -3
- package/dist/{rbac-roles-ae8yttq4.js → rbac-roles-4y6ngvzq.js} +1 -1
- package/dist/{rbac-roles-ejxx9b35.js → rbac-roles-exbg8xb6.js} +2 -2
- package/dist/{registry-rxw10xzg.js → registry-ajhexe44.js} +1 -1
- package/dist/{render-v2-n16n0phe.js → render-v2-cfsh13z3.js} +3 -3
- package/dist/{resume-z0fgjc38.js → resume-5s2t0dbs.js} +10 -8
- package/dist/{runtime-xe3aqy4x.js → runtime-bt5pkpm1.js} +1 -1
- package/dist/{session-access-cv3mq6mc.js → session-access-dnbyp3cn.js} +6 -4
- package/dist/{setup-mafhj3yt.js → setup-1wj1y77j.js} +1 -1
- package/dist/{typesafe-system-one-2hgpayzy.js → typesafe-system-one-bre38hvs.js} +4 -2
- package/dist/{virtual-modules-4r3zkc20.js → virtual-modules-mnfbh5mr.js} +8 -2
- package/dist/{work-objects-8b86mgjp.js → work-objects-7mg6216g.js} +26 -24
- package/dist/worker-nwb789mg.js +79 -0
- package/dist/{x-y45mthwn.js → x-pbpezr0s.js} +2 -2
- package/openapi.json +793 -6
- package/package.json +11 -11
- package/src/apps/catalog.generated.json +206 -4
- package/src/apps/page-validator.ts +13 -1
- package/src/assets/key.ts +17 -0
- package/src/azure-devops/handlers.ts +1 -1
- package/src/be/chunking.ts +42 -9
- package/src/be/db/approvals.ts +18 -0
- package/src/be/db/session-moves.ts +164 -0
- package/src/be/db/session-tree.ts +384 -0
- package/src/be/db/tasks/draft-text.ts +15 -0
- package/src/be/db/tasks/read.ts +54 -5
- package/src/be/db/workflow-runs.ts +96 -0
- package/src/be/db.ts +85 -237
- package/src/be/memory/index-content.ts +36 -17
- package/src/be/memory/providers/openai-embedding.ts +115 -61
- package/src/be/memory/providers/sqlite-store.ts +69 -23
- package/src/be/memory/reranker.ts +31 -1
- package/src/be/memory/search-candidates.ts +47 -0
- package/src/be/memory/types.ts +14 -0
- package/src/be/onboarding.ts +3 -0
- package/src/be/scripts/typecheck.ts +2 -0
- package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +106 -54
- package/src/be/seed-scripts/index.ts +2 -2
- package/src/be/skill-sync.ts +43 -2
- package/src/be/swarm-config-guard.ts +25 -1
- package/src/be/task-attachment-store.ts +30 -0
- package/src/bitbucket/api.ts +81 -0
- package/src/bitbucket/auth.ts +64 -0
- package/src/bitbucket/handlers.ts +434 -0
- package/src/bitbucket/index.ts +28 -0
- package/src/bitbucket/templates.ts +134 -0
- package/src/bitbucket/types.ts +65 -0
- package/src/commands/runner.ts +55 -3
- package/src/e2b/env.ts +1 -0
- package/src/extensions/catalog.generated.json +4 -4
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +9 -2
- package/src/extensions/post-bridge.ts +1 -0
- package/src/fs/provider.ts +13 -1
- package/src/github/handlers.ts +6 -6
- package/src/gitlab/handlers.ts +5 -5
- package/src/heartbeat/heartbeat.ts +31 -11
- package/src/http/assets.ts +14 -2
- package/src/http/core.ts +12 -0
- package/src/http/fs.ts +54 -34
- package/src/http/index.ts +4 -0
- package/src/http/mcp-body.ts +37 -0
- package/src/http/mcp-user.ts +3 -5
- package/src/http/mcp.ts +6 -7
- package/src/http/memory.ts +26 -15
- package/src/http/onboarding.ts +1 -0
- package/src/http/page-proxy.ts +61 -5
- package/src/http/pages-public.ts +27 -44
- package/src/http/pages.ts +4 -4
- package/src/http/script-runs.ts +2 -0
- package/src/http/sessions.ts +366 -12
- package/src/http/skills.ts +48 -1
- package/src/http/tasks.ts +1 -1
- package/src/http/users.ts +13 -1
- package/src/http/webhooks.ts +141 -0
- package/src/integrations/chat-sdk/adapters.ts +24 -4
- package/src/integrations/chat-sdk/attachments.ts +90 -12
- package/src/integrations/chat-sdk/facts.ts +138 -0
- package/src/integrations/chat-sdk/inbound.ts +156 -8
- package/src/integrations/chat-sdk/limits.ts +24 -0
- package/src/integrations/chat-sdk/outbound-files.ts +186 -0
- package/src/integrations/chat-sdk/outbound.ts +272 -5
- package/src/integrations/chat-sdk/relay.ts +4 -1
- package/src/integrations/chat-sdk/router.ts +28 -2
- package/src/integrations/chat-sdk/steering.ts +9 -0
- package/src/integrations/chat-sdk/templates.ts +14 -0
- package/src/integrations/chat-sdk/thread-buffer.ts +4 -0
- package/src/mcp-limits.ts +20 -0
- package/src/pages/browser-sdk.ts +8 -3
- package/src/pages/feedback-overlay.ts +1 -1
- package/src/providers/acp-targets.ts +1 -1
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +106 -119
- package/src/providers/codex-agents-md.ts +35 -229
- package/src/providers/cursor-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +5 -53
- package/src/realtime/transport.ts +1 -2
- package/src/script-workflows/workflow-ctx.ts +1 -0
- package/src/scripts-runtime/types/stdlib.d.ts +2 -0
- package/src/scripts-runtime/types/swarm-extension.d.ts +14 -4
- package/src/scripts-runtime/types/swarm-sdk.d.ts +2 -0
- package/src/server-user.ts +2 -0
- package/src/server.ts +2 -0
- package/src/slack/assistant.ts +2 -2
- package/src/slack/files.ts +2 -0
- package/src/slack/handlers.test.ts +158 -0
- package/src/slack/handlers.ts +23 -32
- package/src/slack/inbound-files.ts +39 -6
- package/src/slack/thread-buffer.ts +2 -1
- package/src/tasks/context-key.ts +61 -1
- package/src/tasks/task-terminal-effects.ts +21 -17
- package/src/tests/apps-card-grid.test.ts +197 -0
- package/src/tests/apps-catalog-additions.test.ts +120 -0
- package/src/tests/apps-tree.test.ts +129 -0
- package/src/tests/bitbucket-auth.test.ts +156 -0
- package/src/tests/bitbucket-handlers.test.ts +425 -0
- package/src/tests/bitbucket-webhook.test.ts +220 -0
- package/src/tests/chat-sdk-attachments-reply.test.ts +52 -0
- package/src/tests/chat-sdk-config.test.ts +11 -0
- package/src/tests/chat-sdk-facts.test.ts +23 -0
- package/src/tests/chat-sdk-inbound.test.ts +597 -4
- package/src/tests/chat-sdk-outbound-files.test.ts +652 -0
- package/src/tests/chat-sdk-telegram-markdown.test.ts +92 -0
- package/src/tests/codex-adapter.test.ts +167 -242
- package/src/tests/config-api-only-keys.test.ts +9 -0
- package/src/tests/context-key.test.ts +50 -0
- package/src/tests/core-auth.test.ts +5 -0
- package/src/tests/cors-allowlist.test.ts +1 -1
- package/src/tests/db-task-read-contract.test.ts +7 -0
- package/src/tests/db-task-write-contract.test.ts +6 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/extensions-example-slack-reply-gate.test.ts +262 -91
- package/src/tests/fixtures/bitbucket/pullrequest-comment-created-inline.json +174 -0
- package/src/tests/fixtures/bitbucket/pullrequest-comment-created.json +170 -0
- package/src/tests/fixtures/bitbucket/pullrequest-created-mention.json +132 -0
- package/src/tests/fixtures/bitbucket/pullrequest-created-reviewer.json +140 -0
- package/src/tests/fixtures/bitbucket/pullrequest-fulfilled.json +134 -0
- package/src/tests/fixtures/bitbucket/pullrequest-rejected.json +133 -0
- package/src/tests/generate-mcp-docs.test.ts +101 -0
- package/src/tests/gitlab-handlers.test.ts +3 -3
- package/src/tests/gitlab-vcs-db.test.ts +4 -4
- package/src/tests/heartbeat-checklist.test.ts +33 -0
- package/src/tests/heartbeat-jev-gate-credentials.test.ts +44 -0
- package/src/tests/heartbeat-jev-gate.test.ts +242 -1
- package/src/tests/integration-disable-flags.test.ts +36 -0
- package/src/tests/kv-page-proxy.test.ts +91 -5
- package/src/tests/launch-password-rejection.test.ts +2 -2
- package/src/tests/mcp-input-caps.test.ts +294 -0
- package/src/tests/mcp-tools.test.ts +1 -1
- package/src/tests/memory-chunked-search-starvation.test.ts +200 -0
- package/src/tests/memory-embedding-input-cap.test.ts +114 -0
- package/src/tests/memory.test.ts +14 -0
- package/src/tests/onboarding.test.ts +12 -0
- package/src/tests/page-guest-session.test.ts +19 -8
- package/src/tests/page-proxy-authed.test.ts +1 -1
- package/src/tests/page-proxy.test.ts +17 -10
- package/src/tests/page-session.test.ts +102 -1
- package/src/tests/pages-authed-mode.test.ts +82 -12
- package/src/tests/pages-password-mode.test.ts +5 -5
- package/src/tests/pi-codemode.test.ts +8 -2
- package/src/tests/preload-env.test.ts +57 -0
- package/src/tests/preload.ts +21 -0
- package/src/tests/prompt-template-remaining.test.ts +38 -0
- package/src/tests/realtime-transport.test.ts +13 -7
- package/src/tests/run-root-coverage.test.ts +39 -0
- package/src/tests/runner-bitbucket-vcs.test.ts +191 -0
- package/src/tests/runner-skills-refresh.test.ts +58 -0
- package/src/tests/script-runs-http.test.ts +84 -0
- package/src/tests/scripts-http.test.ts +10 -1
- package/src/tests/secret-scrubber.test.ts +15 -0
- package/src/tests/sessions-move.test.ts +576 -0
- package/src/tests/sessions-tree.test.ts +493 -0
- package/src/tests/skill-files-http.test.ts +50 -0
- package/src/tests/skills-bundle.test.ts +99 -0
- package/src/tests/swarm-config-export-to-env.test.ts +23 -3
- package/src/tests/task-audio-transcription.test.ts +299 -0
- package/src/tests/task-completion-memory-chunking.test.ts +434 -0
- package/src/tests/task-terminal-secret-scrub.test.ts +6 -1
- package/src/tests/transcription.test.ts +299 -0
- package/src/tests/vcs-bitbucket.test.ts +91 -0
- package/src/tests/vcs-provider-isolation.test.ts +123 -0
- package/src/tests/vcs-provider.test.ts +16 -1
- package/src/tests/vcs-tracking.test.ts +3 -3
- package/src/tests/workflow-foreach-bodies.test.ts +637 -0
- package/src/tests/workflow-foreach-concurrency.test.ts +342 -0
- package/src/tests/workflow-foreach-retry.test.ts +537 -0
- package/src/tests/workflow-foreach.test.ts +44 -23
- package/src/tests/workflow-step-claims.test.ts +6 -6
- package/src/tests/workflow-system-one-decision-providers.test.ts +98 -1
- package/src/tests/workflow-system-one-decision.test.ts +2 -1
- package/src/tests/workflow-tla-races.test.ts +2 -2
- package/src/tools/chat-access.ts +10 -0
- package/src/tools/chat-post.ts +31 -11
- package/src/tools/chat-reply.ts +32 -10
- package/src/tools/memory-search.ts +3 -1
- package/src/tools/script-delete.ts +1 -1
- package/src/tools/system-one-decide.ts +17 -4
- package/src/tools/workflows/create-workflow.ts +5 -3
- package/src/types.ts +9 -2
- package/src/utils/config-env-export.ts +4 -1
- package/src/utils/page-session.ts +133 -20
- package/src/utils/secret-scrubber.ts +4 -0
- package/src/utils/skills-refresh.ts +83 -43
- package/src/utils/transcription.ts +246 -0
- package/src/vcs/bitbucket.ts +58 -0
- package/src/vcs/index.ts +2 -0
- package/src/vcs/types.ts +2 -2
- package/src/workflows/approval-resolution.ts +4 -1
- package/src/workflows/checkpoint.ts +9 -1
- package/src/workflows/constants.ts +3 -0
- package/src/workflows/definition.ts +48 -17
- package/src/workflows/engine.ts +25 -15
- package/src/workflows/executors/foreach.ts +45 -99
- package/src/workflows/executors/system-one-decision.ts +28 -7
- package/src/workflows/executors/system-one-providers.ts +39 -4
- package/src/workflows/foreach-body.ts +146 -0
- package/src/workflows/foreach-driver.ts +552 -0
- package/src/workflows/foreach-join.ts +206 -7
- package/src/workflows/node-timeout.ts +21 -0
- package/src/workflows/recovery.ts +68 -3
- package/src/workflows/resume.ts +221 -43
- package/src/workflows/retry-poller.ts +39 -24
- package/src/workflows/step-input.ts +59 -0
- package/src/workflows/task-step-routing.ts +114 -56
- package/templates/extensions/slack-reply-gate/README.md +24 -22
- package/templates/extensions/slack-reply-gate/hooks.ts +146 -95
- package/templates/extensions/slack-reply-gate/manifest.json +2 -2
- package/templates/skills/apps/SKILL.md +28 -6
- package/templates/skills/apps/content.md +28 -6
- package/templates/skills/chat-sdk/content.md +6 -2
- package/templates/skills/code-quality/content.md +2 -1
- package/templates/skills/heartbeat-runbook/content.md +5 -5
- package/templates/skills/pages/content.md +6 -4
- package/templates/skills/slack-interaction/content.md +5 -0
- package/templates/skills/swarm-scripts/SKILL.md +2 -2
- package/templates/skills/swarm-scripts/content.md +2 -2
- package/templates/skills/work-on-task/SKILL.md +1 -1
- package/templates/skills/work-on-task/content.md +1 -1
- package/dist/cli-51y09c6p.js +0 -2657
- package/dist/cli-yw91rnmw.js +0 -13
- package/dist/handlers-0pvee9nw.js +0 -39
- package/dist/index-gdh8jrdm.js +0 -43
- package/dist/index-hg0x2cdv.js +0 -40
- package/dist/index-thm0y6a8.js +0 -41
- package/dist/lead-ee8vazz2.js +0 -77
- package/dist/worker-16s75hm5.js +0 -77
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
bedrockProviderModule
|
|
3
|
-
} from "./cli-
|
|
3
|
+
} from "./cli-6ctwrf1z.js";
|
|
4
|
+
import"./cli-2nf3b08t.js";
|
|
4
5
|
import"./cli-kvzgak7c.js";
|
|
5
6
|
import"./cli-445c1vp7.js";
|
|
6
7
|
import"./cli-qz93y3x5.js";
|
|
@@ -42,6 +43,9 @@ import {
|
|
|
42
43
|
recordUnmappedIdentity,
|
|
43
44
|
MAX_TASK_ATTACHMENT_BYTES,
|
|
44
45
|
recordTaskAttachmentUpload,
|
|
46
|
+
scopeFromAttachment,
|
|
47
|
+
backingProviderId,
|
|
48
|
+
transcribeAudioNotes,
|
|
45
49
|
handleWebhookTrigger,
|
|
46
50
|
verifyHmacSignature,
|
|
47
51
|
WebhookError,
|
|
@@ -126,6 +130,16 @@ import {
|
|
|
126
130
|
getTelemetryIngestAnchor,
|
|
127
131
|
initApiTelemetry,
|
|
128
132
|
syncTelemetryEnabled,
|
|
133
|
+
isBitbucketEnabled,
|
|
134
|
+
initBitbucket,
|
|
135
|
+
verifyBitbucketWebhook,
|
|
136
|
+
isDuplicateDelivery,
|
|
137
|
+
markDeliveryProcessed,
|
|
138
|
+
pullRequestEventOf,
|
|
139
|
+
pullRequestCommentEventOf,
|
|
140
|
+
handlePullRequestCreated2,
|
|
141
|
+
handlePullRequestCommentCreated,
|
|
142
|
+
handlePullRequestClosed,
|
|
129
143
|
handlePullRequest,
|
|
130
144
|
handleIssue,
|
|
131
145
|
handleComment,
|
|
@@ -168,14 +182,18 @@ import {
|
|
|
168
182
|
handleAgentSessionPrompted,
|
|
169
183
|
isLinearEnabled,
|
|
170
184
|
initLinear,
|
|
171
|
-
|
|
185
|
+
isCookieSafePageId,
|
|
186
|
+
extractPageSession,
|
|
187
|
+
extractLegacyPageSession,
|
|
172
188
|
extractAndVerifyCookie,
|
|
173
|
-
|
|
189
|
+
pageSessionSetCookies,
|
|
174
190
|
resolveHttpRequestAuth,
|
|
175
191
|
loadGlobalConfigsIntoEnv,
|
|
176
192
|
reloadGlobalConfigsAndIntegrations,
|
|
177
193
|
scheduleIntegrationsReload,
|
|
178
194
|
handleCore,
|
|
195
|
+
MCP_MAX_TOOL_INPUT_ELEMENTS,
|
|
196
|
+
MCP_MAX_REQUEST_BODY_BYTES,
|
|
179
197
|
userCtx,
|
|
180
198
|
requestPrincipal,
|
|
181
199
|
agentFirstPrincipal,
|
|
@@ -205,6 +223,9 @@ import {
|
|
|
205
223
|
hydrateOAuthAppFromPreset,
|
|
206
224
|
scopedResourceScopeIdSchema,
|
|
207
225
|
resolveScopedResourceId,
|
|
226
|
+
MemoryKeyError,
|
|
227
|
+
refreshLinks,
|
|
228
|
+
indexMemoryContent,
|
|
208
229
|
dedupeMemoryDocumentIds,
|
|
209
230
|
recordMemoryAccesses,
|
|
210
231
|
recordRetrievals,
|
|
@@ -221,12 +242,9 @@ import {
|
|
|
221
242
|
reservedRoomKeyError,
|
|
222
243
|
updateMcpServerChecked,
|
|
223
244
|
updateTouchesStdioExecution,
|
|
224
|
-
MemoryKeyError,
|
|
225
|
-
refreshLinks,
|
|
226
245
|
canReadMemory,
|
|
227
246
|
getLinksForMemory,
|
|
228
247
|
expandCandidatesWithGraph,
|
|
229
|
-
indexMemoryContent,
|
|
230
248
|
isApiDraining,
|
|
231
249
|
drainApi,
|
|
232
250
|
markKapsoMessageRead,
|
|
@@ -283,7 +301,7 @@ import {
|
|
|
283
301
|
hasCapability,
|
|
284
302
|
getEnabledCapabilities,
|
|
285
303
|
createServer
|
|
286
|
-
} from "./cli-
|
|
304
|
+
} from "./cli-5kzk3472.js";
|
|
287
305
|
import {
|
|
288
306
|
chatThreadOfContextKey,
|
|
289
307
|
agentContextKey,
|
|
@@ -378,6 +396,7 @@ import {
|
|
|
378
396
|
listApprovalRequestSummaries2,
|
|
379
397
|
getPendingApprovalVoteState2,
|
|
380
398
|
recordApprovalVotes2,
|
|
399
|
+
getPendingApprovalsForTasks2,
|
|
381
400
|
computeContentHash2,
|
|
382
401
|
getTaskById2,
|
|
383
402
|
TASK_LINEAGE_LIMITS2,
|
|
@@ -397,6 +416,15 @@ import {
|
|
|
397
416
|
deleteModelProfile,
|
|
398
417
|
taskProfileError,
|
|
399
418
|
init_model_profiles,
|
|
419
|
+
AssetKeyMovesBlockedError2,
|
|
420
|
+
assertAssetKeyMovesAllowed2,
|
|
421
|
+
moveSessionChains,
|
|
422
|
+
listPrefixRoots,
|
|
423
|
+
listSharedPrefixRoots,
|
|
424
|
+
init_session_moves,
|
|
425
|
+
getSessionReadState,
|
|
426
|
+
getSessionTree,
|
|
427
|
+
init_session_tree,
|
|
400
428
|
setHumanFreeQueueListener,
|
|
401
429
|
drainHumanFreeReclassifyQueue,
|
|
402
430
|
init_human_free,
|
|
@@ -480,6 +508,8 @@ import {
|
|
|
480
508
|
init_key_paths,
|
|
481
509
|
rerank,
|
|
482
510
|
init_reranker,
|
|
511
|
+
searchMemoryCandidates,
|
|
512
|
+
init_search_candidates,
|
|
483
513
|
getEmbeddingProvider2,
|
|
484
514
|
getMemoryStore2,
|
|
485
515
|
init_memory,
|
|
@@ -720,38 +750,40 @@ import {
|
|
|
720
750
|
countScriptRunJournalSteps2,
|
|
721
751
|
countScriptRunJournalAgentTaskSteps2,
|
|
722
752
|
init_db3
|
|
723
|
-
} from "./cli-
|
|
753
|
+
} from "./cli-fcc5qe3f.js";
|
|
724
754
|
import {
|
|
725
755
|
getUserGrant2,
|
|
726
756
|
init_rbac_roles
|
|
727
|
-
} from "./cli-
|
|
728
|
-
import"./cli-
|
|
757
|
+
} from "./cli-0z3vjrpm.js";
|
|
758
|
+
import"./cli-36j4jnk3.js";
|
|
729
759
|
import {
|
|
730
760
|
normalizeModelKey
|
|
731
|
-
} from "./cli-
|
|
761
|
+
} from "./cli-0y6f0ckq.js";
|
|
732
762
|
import {
|
|
733
763
|
upsertModelCatalogOverlay,
|
|
734
764
|
deleteModelCatalogOverlay,
|
|
735
765
|
loadModelsCatalog,
|
|
736
766
|
requestModelCatalogRefresh2
|
|
737
|
-
} from "./cli-
|
|
767
|
+
} from "./cli-y9jvxfhk.js";
|
|
738
768
|
import {
|
|
739
769
|
AMP_ESTIMATE_MODELS,
|
|
740
770
|
ampBillsCacheWrites,
|
|
741
771
|
resolveAmpModel
|
|
742
|
-
} from "./cli-
|
|
772
|
+
} from "./cli-jpxsrxcb.js";
|
|
743
773
|
import {
|
|
744
774
|
automationIntegrationFixUrl,
|
|
745
775
|
preflightAutomation,
|
|
746
776
|
getAutomationSetupStates,
|
|
747
777
|
listEnabledAutomationPreflightInputs,
|
|
778
|
+
createApprovalFollowUpTask,
|
|
748
779
|
validateJsonSchema,
|
|
749
780
|
workflowSaveWarnings,
|
|
750
|
-
retryFailedRun2,
|
|
751
|
-
cancelWorkflowRun2,
|
|
752
781
|
TriggerSchemaError,
|
|
753
|
-
startWorkflowExecution2
|
|
754
|
-
|
|
782
|
+
startWorkflowExecution2,
|
|
783
|
+
generateEdges,
|
|
784
|
+
definitionNodeIds,
|
|
785
|
+
validateDefinition
|
|
786
|
+
} from "./cli-92mqenkc.js";
|
|
755
787
|
import {
|
|
756
788
|
require_dist2,
|
|
757
789
|
isInvalidClientError,
|
|
@@ -831,14 +863,14 @@ import {
|
|
|
831
863
|
typecheckWithAmbient,
|
|
832
864
|
typecheckScript,
|
|
833
865
|
parseSkillContent
|
|
834
|
-
} from "./cli-
|
|
866
|
+
} from "./cli-we0ra057.js";
|
|
835
867
|
import {
|
|
836
868
|
buildAuthorizationUrl,
|
|
837
869
|
exchangeAuthorizationCode,
|
|
838
870
|
oauthAppRowToProviderConfig,
|
|
839
871
|
forceRefreshAuthorizationOrThrow,
|
|
840
872
|
forceRefreshTokenOrThrow
|
|
841
|
-
} from "./cli-
|
|
873
|
+
} from "./cli-s4sntwt4.js";
|
|
842
874
|
import {
|
|
843
875
|
signalProcessGroup,
|
|
844
876
|
detachedProcessGroup,
|
|
@@ -866,15 +898,9 @@ import {
|
|
|
866
898
|
getSlackApp2,
|
|
867
899
|
startSlackApp2,
|
|
868
900
|
stopSlackApp2
|
|
869
|
-
} from "./cli-
|
|
870
|
-
import"./cli-
|
|
871
|
-
import
|
|
872
|
-
createApprovalFollowUpTask,
|
|
873
|
-
generateEdges,
|
|
874
|
-
definitionNodeIds,
|
|
875
|
-
validateDefinition
|
|
876
|
-
} from "./cli-q6r7t299.js";
|
|
877
|
-
import"./cli-8nyhf7za.js";
|
|
901
|
+
} from "./cli-fy1jyyym.js";
|
|
902
|
+
import"./cli-ftmq4nvk.js";
|
|
903
|
+
import"./cli-qqzmjxkf.js";
|
|
878
904
|
import {
|
|
879
905
|
_fetchHolder,
|
|
880
906
|
CLIENT_ID,
|
|
@@ -885,11 +911,15 @@ import {
|
|
|
885
911
|
codexOAuthKeyForSlot,
|
|
886
912
|
loadAllCodexOAuthSlots,
|
|
887
913
|
getValidCodexOAuth
|
|
888
|
-
} from "./cli-
|
|
914
|
+
} from "./cli-kxpfx2xr.js";
|
|
889
915
|
import {
|
|
890
916
|
workflowEventBus2,
|
|
891
917
|
init_event_bus
|
|
892
|
-
} from "./cli-
|
|
918
|
+
} from "./cli-dabehxhp.js";
|
|
919
|
+
import {
|
|
920
|
+
retryFailedRun2,
|
|
921
|
+
cancelWorkflowRun2
|
|
922
|
+
} from "./cli-3zwye9sn.js";
|
|
893
923
|
import {
|
|
894
924
|
duplicateGuardedQueryKey,
|
|
895
925
|
warnIfCorsAllowsAnyOrigin,
|
|
@@ -901,6 +931,7 @@ import {
|
|
|
901
931
|
wireHttpSpanLifecycle,
|
|
902
932
|
safeRequestUrlForLog,
|
|
903
933
|
agentWithCapacity,
|
|
934
|
+
RequestBodyTooLargeError,
|
|
904
935
|
parseBody,
|
|
905
936
|
BODY_TOO_LARGE,
|
|
906
937
|
enforceContentLengthCap,
|
|
@@ -926,13 +957,14 @@ import {
|
|
|
926
957
|
canReadSession2,
|
|
927
958
|
readableByFor2,
|
|
928
959
|
SESSION_WRITE_DENIED2,
|
|
960
|
+
hasSessionWrite2,
|
|
929
961
|
denyParentAttach2,
|
|
930
962
|
runtimeInstanceHeader,
|
|
931
963
|
bodyTask,
|
|
932
964
|
bodyListTask,
|
|
933
965
|
describeRequestRoute,
|
|
934
966
|
route
|
|
935
|
-
} from "./cli-
|
|
967
|
+
} from "./cli-9ce2xw98.js";
|
|
936
968
|
import {
|
|
937
969
|
isMultiRuntimeEnabled
|
|
938
970
|
} from "./cli-e8xyjvfc.js";
|
|
@@ -942,7 +974,7 @@ import {
|
|
|
942
974
|
agentFsRequestTimeoutMs,
|
|
943
975
|
AgentFsProvider,
|
|
944
976
|
getFileStorageProvider2
|
|
945
|
-
} from "./cli-
|
|
977
|
+
} from "./cli-g9ypd5en.js";
|
|
946
978
|
import {
|
|
947
979
|
RuntimePoolError2,
|
|
948
980
|
mintControllerToken,
|
|
@@ -963,13 +995,13 @@ import {
|
|
|
963
995
|
resolvePoolDelivery2,
|
|
964
996
|
createPoolAgent2,
|
|
965
997
|
init_db
|
|
966
|
-
} from "./cli-
|
|
998
|
+
} from "./cli-5rvg1ddk.js";
|
|
967
999
|
import {
|
|
968
1000
|
RATING_MODEL_MAX_LENGTH,
|
|
969
1001
|
REFERENCES_SOURCE_MAX_LENGTH,
|
|
970
1002
|
sanitizeReferencesSource
|
|
971
1003
|
} from "./cli-3h26v7yj.js";
|
|
972
|
-
import"./cli-
|
|
1004
|
+
import"./cli-pasyvjmm.js";
|
|
973
1005
|
import {
|
|
974
1006
|
startPoolRouting,
|
|
975
1007
|
stopPoolRouting,
|
|
@@ -979,8 +1011,8 @@ import {
|
|
|
979
1011
|
checkHeartbeatChecklist2,
|
|
980
1012
|
runHeartbeatSweep2,
|
|
981
1013
|
stopHeartbeat2
|
|
982
|
-
} from "./cli-
|
|
983
|
-
import"./cli-
|
|
1014
|
+
} from "./cli-7g40g8hq.js";
|
|
1015
|
+
import"./cli-xd4eqtr2.js";
|
|
984
1016
|
import {
|
|
985
1017
|
import_websocket,
|
|
986
1018
|
import_websocket_server
|
|
@@ -989,7 +1021,7 @@ import {
|
|
|
989
1021
|
runtimeSupervisorStatus,
|
|
990
1022
|
startRuntimeSupervisor,
|
|
991
1023
|
stopRuntimeSupervisor
|
|
992
|
-
} from "./cli-
|
|
1024
|
+
} from "./cli-x6vf4sad.js";
|
|
993
1025
|
import"./cli-ns59dc6r.js";
|
|
994
1026
|
import {
|
|
995
1027
|
ACP_TARGET_IDS
|
|
@@ -1009,14 +1041,14 @@ import {
|
|
|
1009
1041
|
memoryRelevance,
|
|
1010
1042
|
isUnknownModelError,
|
|
1011
1043
|
writeSkillsToFilesystem
|
|
1012
|
-
} from "./cli-
|
|
1044
|
+
} from "./cli-4w24f7z3.js";
|
|
1013
1045
|
import {
|
|
1014
1046
|
inviteEmailToSharedOrg,
|
|
1015
1047
|
ensureAgentFsCredentialsForAgent
|
|
1016
|
-
} from "./cli-
|
|
1048
|
+
} from "./cli-1pr672hz.js";
|
|
1017
1049
|
import {
|
|
1018
1050
|
Anthropic
|
|
1019
|
-
} from "./cli-
|
|
1051
|
+
} from "./cli-3qv8naq8.js";
|
|
1020
1052
|
import {
|
|
1021
1053
|
isCodexOAuthConfigKey
|
|
1022
1054
|
} from "./cli-j4bwnpkm.js";
|
|
@@ -1052,12 +1084,13 @@ import {
|
|
|
1052
1084
|
revokeAgentTokens,
|
|
1053
1085
|
fingerprintApiKey,
|
|
1054
1086
|
init_users
|
|
1055
|
-
} from "./cli-
|
|
1087
|
+
} from "./cli-xt768r5m.js";
|
|
1056
1088
|
import"./cli-v41kn3cd.js";
|
|
1057
1089
|
import"./cli-wgtk7xxr.js";
|
|
1090
|
+
import"./cli-1f0gp9vz.js";
|
|
1058
1091
|
import {
|
|
1059
1092
|
setBedrockProviderModule
|
|
1060
|
-
} from "./cli-
|
|
1093
|
+
} from "./cli-rycdaj0a.js";
|
|
1061
1094
|
import {
|
|
1062
1095
|
isRbacEnabled,
|
|
1063
1096
|
decideToolAdmission,
|
|
@@ -1068,6 +1101,11 @@ import {
|
|
|
1068
1101
|
init_rbac
|
|
1069
1102
|
} from "./cli-ba8ne1es.js";
|
|
1070
1103
|
import {
|
|
1104
|
+
ASSET_KEY_MAX_LENGTH,
|
|
1105
|
+
normalizeAssetKey,
|
|
1106
|
+
defaultAssetKey,
|
|
1107
|
+
parseAssetKey,
|
|
1108
|
+
init_key,
|
|
1071
1109
|
isSafeBundlePath,
|
|
1072
1110
|
init_bundle_path,
|
|
1073
1111
|
init_zod_openapi,
|
|
@@ -1208,12 +1246,12 @@ import {
|
|
|
1208
1246
|
KvKeySchema,
|
|
1209
1247
|
KvEntrySchema,
|
|
1210
1248
|
init_types
|
|
1211
|
-
} from "./cli-
|
|
1249
|
+
} from "./cli-rh5smt8t.js";
|
|
1212
1250
|
import {
|
|
1213
1251
|
IDENTITY_FIELD_BUDGETS,
|
|
1214
1252
|
IdentityFieldBudgetError,
|
|
1215
1253
|
init_identity_field_budget
|
|
1216
|
-
} from "./cli-
|
|
1254
|
+
} from "./cli-gpv60278.js";
|
|
1217
1255
|
import {
|
|
1218
1256
|
getSlackConfiguration,
|
|
1219
1257
|
init_config
|
|
@@ -1281,7 +1319,7 @@ import {
|
|
|
1281
1319
|
import {
|
|
1282
1320
|
init_package,
|
|
1283
1321
|
package_default
|
|
1284
|
-
} from "./cli-
|
|
1322
|
+
} from "./cli-179xcyky.js";
|
|
1285
1323
|
import {
|
|
1286
1324
|
realtimeBus,
|
|
1287
1325
|
init_bus
|
|
@@ -1310,7 +1348,7 @@ import {
|
|
|
1310
1348
|
scrubObject,
|
|
1311
1349
|
registerVolatileSecret,
|
|
1312
1350
|
init_secret_scrubber
|
|
1313
|
-
} from "./cli-
|
|
1351
|
+
} from "./cli-n4c8e3s5.js";
|
|
1314
1352
|
import {
|
|
1315
1353
|
isAlias,
|
|
1316
1354
|
init_src2
|
|
@@ -2463,8 +2501,7 @@ async function authenticate(req) {
|
|
|
2463
2501
|
const page = await getPage2(pageId);
|
|
2464
2502
|
if (!page)
|
|
2465
2503
|
throw new Error("Page not found");
|
|
2466
|
-
const
|
|
2467
|
-
const session = cookie?.pageId === pageId ? cookie : null;
|
|
2504
|
+
const session = await extractAndVerifyCookie(req, pageId);
|
|
2468
2505
|
if (!session && page.authMode !== "public")
|
|
2469
2506
|
throw new Error("Page session required");
|
|
2470
2507
|
if (session?.uid && (await findUserById(session.uid))?.status !== "active")
|
|
@@ -6769,6 +6806,10 @@ async function handleAssets(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
6769
6806
|
jsonError(res, "Asset not found", 404);
|
|
6770
6807
|
return true;
|
|
6771
6808
|
}
|
|
6809
|
+
if (!await hasSessionWrite2(req, myAgentId, task.id)) {
|
|
6810
|
+
jsonError(res, await canReadSession2(req, myAgentId, task.id) ? "You can view this session but not move it." : SESSION_READ_DENIED2, 403);
|
|
6811
|
+
return true;
|
|
6812
|
+
}
|
|
6772
6813
|
const tasks = parsed.body.cascade ? await getRootTaskChain2(task.id) : [task];
|
|
6773
6814
|
for (const target of tasks) {
|
|
6774
6815
|
if (!await canMutateTaskNamespace(target, myAgentId, req)) {
|
|
@@ -6825,7 +6866,7 @@ async function handleAssets(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
6825
6866
|
return true;
|
|
6826
6867
|
}
|
|
6827
6868
|
const message = error instanceof Error ? error.message : String(error);
|
|
6828
|
-
jsonError(res, message,
|
|
6869
|
+
jsonError(res, message, error instanceof AssetKeyMovesBlockedError2 ? 409 : 400);
|
|
6829
6870
|
}
|
|
6830
6871
|
return true;
|
|
6831
6872
|
}
|
|
@@ -7076,6 +7117,7 @@ var OnboardingIntegrationMethodSchema = _enum2([
|
|
|
7076
7117
|
"slack",
|
|
7077
7118
|
"github",
|
|
7078
7119
|
"gitlab",
|
|
7120
|
+
"bitbucket",
|
|
7079
7121
|
"linear_oauth",
|
|
7080
7122
|
"jira_oauth"
|
|
7081
7123
|
]);
|
|
@@ -7143,6 +7185,7 @@ var OnboardingSignalsSchema = object2({
|
|
|
7143
7185
|
slack: boolean(),
|
|
7144
7186
|
github: boolean(),
|
|
7145
7187
|
gitlab: boolean(),
|
|
7188
|
+
bitbucket: boolean(),
|
|
7146
7189
|
linear: boolean(),
|
|
7147
7190
|
jira: boolean()
|
|
7148
7191
|
}),
|
|
@@ -7364,6 +7407,8 @@ function inferIntegrationMethod(signals) {
|
|
|
7364
7407
|
return "github";
|
|
7365
7408
|
if (signals.integrations.gitlab)
|
|
7366
7409
|
return "gitlab";
|
|
7410
|
+
if (signals.integrations.bitbucket)
|
|
7411
|
+
return "bitbucket";
|
|
7367
7412
|
if (signals.integrations.linear)
|
|
7368
7413
|
return "linear_oauth";
|
|
7369
7414
|
if (signals.integrations.jira)
|
|
@@ -9644,6 +9689,7 @@ declare module "swarm-extension" {
|
|
|
9644
9689
|
| "github"
|
|
9645
9690
|
| "gitlab"
|
|
9646
9691
|
| "azure-devops"
|
|
9692
|
+
| "bitbucket"
|
|
9647
9693
|
| "system"
|
|
9648
9694
|
| "linear"
|
|
9649
9695
|
| "jira"
|
|
@@ -9670,7 +9716,7 @@ declare module "swarm-extension" {
|
|
|
9670
9716
|
slackTriggerMessageTs?: string | undefined;
|
|
9671
9717
|
slackUserId?: string | undefined;
|
|
9672
9718
|
overrideSlackContext?: boolean | undefined;
|
|
9673
|
-
vcsProvider?: "github" | "gitlab" | "azure-devops" | undefined;
|
|
9719
|
+
vcsProvider?: "github" | "gitlab" | "azure-devops" | "bitbucket" | undefined;
|
|
9674
9720
|
vcsRepo?: string | undefined;
|
|
9675
9721
|
vcsEventType?: string | undefined;
|
|
9676
9722
|
vcsNumber?: number | undefined;
|
|
@@ -9696,6 +9742,7 @@ declare module "swarm-extension" {
|
|
|
9696
9742
|
sourceTaskId?: string | undefined;
|
|
9697
9743
|
outputSchema?: Record<string, unknown> | undefined;
|
|
9698
9744
|
inheritParentOutputSchema?: boolean | undefined;
|
|
9745
|
+
inheritParentRequester?: boolean | undefined;
|
|
9699
9746
|
inheritParentRoutingAffinity?: boolean | undefined;
|
|
9700
9747
|
inheritParentFollowUpConfig?: boolean | undefined;
|
|
9701
9748
|
followUpConfig?:
|
|
@@ -9760,6 +9807,7 @@ declare module "swarm-extension" {
|
|
|
9760
9807
|
| "github"
|
|
9761
9808
|
| "gitlab"
|
|
9762
9809
|
| "azure-devops"
|
|
9810
|
+
| "bitbucket"
|
|
9763
9811
|
| "system"
|
|
9764
9812
|
| "linear"
|
|
9765
9813
|
| "jira"
|
|
@@ -9800,7 +9848,7 @@ declare module "swarm-extension" {
|
|
|
9800
9848
|
slackUserId?: string | undefined;
|
|
9801
9849
|
slackProgressMessageTs?: string | undefined;
|
|
9802
9850
|
slackTreeRootMessageTs?: string | undefined;
|
|
9803
|
-
vcsProvider?: "github" | "gitlab" | "azure-devops" | undefined;
|
|
9851
|
+
vcsProvider?: "github" | "gitlab" | "azure-devops" | "bitbucket" | undefined;
|
|
9804
9852
|
vcsRepo?: string | undefined;
|
|
9805
9853
|
vcsEventType?: string | undefined;
|
|
9806
9854
|
vcsNumber?: number | undefined;
|
|
@@ -10114,8 +10162,15 @@ declare module "swarm-extension" {
|
|
|
10114
10162
|
userName?: string;
|
|
10115
10163
|
/** The buffered messages, joined. */
|
|
10116
10164
|
text: string;
|
|
10165
|
+
/** True for an @mention and for a reply to one of the bot's messages. */
|
|
10117
10166
|
botMentioned: boolean;
|
|
10118
10167
|
isDirectMessage: boolean;
|
|
10168
|
+
/** Platform chat type, such as Telegram \`private\`, \`group\`, \`supergroup\` or \`channel\`. */
|
|
10169
|
+
chatType?: string;
|
|
10170
|
+
/** Set for a Telegram supergroup with topics. */
|
|
10171
|
+
isForum?: boolean;
|
|
10172
|
+
/** A buffered message replies to one of the bot's own messages. */
|
|
10173
|
+
replyToBot: boolean;
|
|
10119
10174
|
/** Set when earlier swarm tasks exist in this thread. */
|
|
10120
10175
|
threadContext?: {
|
|
10121
10176
|
adapter: string;
|
|
@@ -10246,7 +10301,7 @@ declare module "swarm-extension" {
|
|
|
10246
10301
|
/** The text, or a placeholder naming the type for a non-text message. */
|
|
10247
10302
|
text: string;
|
|
10248
10303
|
}
|
|
10249
|
-
export type VcsProvider = "github" | "gitlab" | "azure-devops";
|
|
10304
|
+
export type VcsProvider = "github" | "gitlab" | "azure-devops" | "bitbucket";
|
|
10250
10305
|
/**
|
|
10251
10306
|
* The provider object the event is about, as the provider names it. GitLab
|
|
10252
10307
|
* calls a pull request a merge request, and a comment a \`note\`.
|
|
@@ -10269,7 +10324,7 @@ declare module "swarm-extension" {
|
|
|
10269
10324
|
provider: VcsProvider;
|
|
10270
10325
|
kind: VcsEventKind;
|
|
10271
10326
|
action: string;
|
|
10272
|
-
/** \`owner/name\` on GitHub and GitLab, the repository URL on Azure DevOps. */
|
|
10327
|
+
/** \`owner/name\` on GitHub and GitLab, \`workspace/repo_slug\` on Bitbucket, the repository URL on Azure DevOps. */
|
|
10273
10328
|
repo: string;
|
|
10274
10329
|
/** The PR, merge request, or issue number. Absent for a pipeline outside a merge request. */
|
|
10275
10330
|
number?: number;
|
|
@@ -11442,8 +11497,8 @@ export default extension;
|
|
|
11442
11497
|
manifest: {
|
|
11443
11498
|
$schema: "../manifest.schema.json",
|
|
11444
11499
|
name: "slack-reply-gate",
|
|
11445
|
-
description: "Asks
|
|
11446
|
-
version: "1.
|
|
11500
|
+
description: "Asks a SystemOne decision model (Jev or Clef) whether Lead should answer a buffered Slack thread follow-up, and drops the wake-up when it should not",
|
|
11501
|
+
version: "1.3.0",
|
|
11447
11502
|
runtime: "api",
|
|
11448
11503
|
assets: {
|
|
11449
11504
|
hooks: "hooks.ts"
|
|
@@ -11458,8 +11513,9 @@ import { z } from "zod";
|
|
|
11458
11513
|
*
|
|
11459
11514
|
* Every human message in a Slack thread the swarm takes part in is buffered and
|
|
11460
11515
|
* flushed into a Lead task ("[Thread follow-up — N message(s) buffered]"), even
|
|
11461
|
-
* "lol", ":joy:" or two people bantering. This hook asks
|
|
11462
|
-
* Lead should answer, and drops the wake-up when the
|
|
11516
|
+
* "lol", ":joy:" or two people bantering. This hook asks a SystemOne decision
|
|
11517
|
+
* model (Jev or Clef) whether Lead should answer, and drops the wake-up when the
|
|
11518
|
+
* answer is no.
|
|
11463
11519
|
*
|
|
11464
11520
|
* Hook: \`pre.task.create\`, origin "slack", description carrying the
|
|
11465
11521
|
* thread-buffer header. \`pre.slack.route\` cannot be used: the buffered thread
|
|
@@ -11467,17 +11523,20 @@ import { z } from "zod";
|
|
|
11467
11523
|
* re-target a message, never drop it.
|
|
11468
11524
|
*
|
|
11469
11525
|
* Rules:
|
|
11470
|
-
* - A message that @mentions Lead passes without a
|
|
11471
|
-
* - Otherwise one
|
|
11526
|
+
* - A message that @mentions Lead passes without a decision call.
|
|
11527
|
+
* - Otherwise one \`choice\` question classifies the message into a reason;
|
|
11472
11528
|
* reply=false only for reaction_only | banter_between_others |
|
|
11473
11529
|
* not_addressed_to_lead, and only when the summed probability of those
|
|
11474
11530
|
* three labels is >= minConfidence (a banter/reaction split is still a skip).
|
|
11475
11531
|
* - Fails OPEN on every error, timeout or missing key.
|
|
11476
11532
|
*
|
|
11477
|
-
*
|
|
11478
|
-
*
|
|
11479
|
-
*
|
|
11480
|
-
*
|
|
11533
|
+
* Provider (config.provider): the decision is made server-side by the
|
|
11534
|
+
* \`system-one-decide\` tool (\`ctx.swarm.systemOne_decide\`), the same path as a
|
|
11535
|
+
* \`system-one-decision\` workflow node, so every SystemOne host works and no key
|
|
11536
|
+
* enters this hook. \`auto\` picks the first host with a configured credential:
|
|
11537
|
+
* typesafe (Jev, TYPESAFE_API_KEY), openrouter (Jev, OPENROUTER_API_KEY), then
|
|
11538
|
+
* cloudflare (Clef, CLOUDFLARE_API_TOKEN or CLOUDFLARE_API_KEY, plus
|
|
11539
|
+
* CLOUDFLARE_ACCOUNT_ID). laya and openai are explicit choices only.
|
|
11481
11540
|
*
|
|
11482
11541
|
* modes (config.mode):
|
|
11483
11542
|
* off -> never evaluate
|
|
@@ -11500,17 +11559,19 @@ import { z } from "zod";
|
|
|
11500
11559
|
export const config = z.object({
|
|
11501
11560
|
mode: z.enum(["off", "shadow", "enforce"]).default("shadow"),
|
|
11502
11561
|
timeoutMs: z.number().int().positive().default(3000),
|
|
11503
|
-
provider: z
|
|
11504
|
-
|
|
11562
|
+
provider: z
|
|
11563
|
+
.enum(["auto", "typesafe", "openrouter", "cloudflare", "laya", "openai"])
|
|
11564
|
+
.default("auto"),
|
|
11565
|
+
/** Jev model on TypeSafe. */
|
|
11505
11566
|
model: z.string().default("jev-latest"),
|
|
11506
|
-
/** Jev model on
|
|
11567
|
+
/** Jev model on OpenRouter. */
|
|
11507
11568
|
openrouterModel: z.string().default("typesafe/jev-1.13"),
|
|
11569
|
+
/** Clef model on Cloudflare Workers AI: clef or clef-flash. */
|
|
11570
|
+
cloudflareModel: z.string().default("clef"),
|
|
11508
11571
|
minConfidence: z.number().min(0).max(1).default(0.55),
|
|
11509
11572
|
contextMessages: z.number().int().min(0).max(20).default(6),
|
|
11510
11573
|
leadAliases: z.array(z.string()).default(["lead"]),
|
|
11511
11574
|
botSlackUserIds: z.array(z.string()).default([]),
|
|
11512
|
-
/** Explicit TypeSafe key; otherwise TYPESAFE_API_KEY from the API process env. */
|
|
11513
|
-
apiKey: z.string().optional(),
|
|
11514
11575
|
/**
|
|
11515
11576
|
* Reaction for a dropped message in enforce mode. "" only removes the bot's
|
|
11516
11577
|
* acceptance reaction(s) and adds nothing.
|
|
@@ -11523,13 +11584,13 @@ type Cfg = z.infer<typeof config>;
|
|
|
11523
11584
|
type Swarm = ApiCtx["swarm"];
|
|
11524
11585
|
|
|
11525
11586
|
const NS = "ext:slack-reply-gate";
|
|
11526
|
-
const TYPESAFE_ENDPOINT = "https://api.typesafe.ai/v1/systemone";
|
|
11527
|
-
const OPENROUTER_ENDPOINT = "https://openrouter.ai/api/alpha/decisions";
|
|
11528
11587
|
const HEADER_LINE_RE = /(?:^|\\n)\\[Thread follow-up — \\d+ message\\(s\\) buffered\\]\\n/g;
|
|
11529
11588
|
const CTX_OPEN = "<thread_context>";
|
|
11530
11589
|
const CTX_CLOSE = "</thread_context>";
|
|
11531
11590
|
// The dispatcher cancels a handler after 5 s; leave room for the KV writes.
|
|
11532
11591
|
const MAX_TIMEOUT_MS = 4000;
|
|
11592
|
+
// The smallest time budget system-one-decide accepts.
|
|
11593
|
+
const MIN_DECIDE_TIMEOUT_MS = 1000;
|
|
11533
11594
|
|
|
11534
11595
|
export const REASONS = {
|
|
11535
11596
|
direct_ask:
|
|
@@ -11631,14 +11692,14 @@ const usable = (v: string): boolean => Boolean(v) && !v.startsWith("[REDACTED");
|
|
|
11631
11692
|
const envOf = (name: string): string => String(process.env[name] ?? "");
|
|
11632
11693
|
|
|
11633
11694
|
/**
|
|
11634
|
-
* Resolve a secret
|
|
11635
|
-
*
|
|
11695
|
+
* Resolve a secret from the API process env. The API process loads global
|
|
11696
|
+
* swarm_config rows into process.env at boot and on config reload.
|
|
11636
11697
|
* ctx.swarm.config_get cannot serve it: the SDK scrubs secrets to "[REDACTED:<name>]".
|
|
11637
11698
|
*/
|
|
11638
|
-
const secret = (name: string
|
|
11699
|
+
const secret = (name: string): string => {
|
|
11639
11700
|
const cached = secretCache.get(name);
|
|
11640
11701
|
if (cached && Date.now() - cached.at < KEY_TTL_MS) return cached.value;
|
|
11641
|
-
const value =
|
|
11702
|
+
const value = usable(envOf(name)) ? envOf(name) : "";
|
|
11642
11703
|
if (usable(value)) secretCache.set(name, { value, at: Date.now() });
|
|
11643
11704
|
return value;
|
|
11644
11705
|
};
|
|
@@ -11648,46 +11709,51 @@ export const resetKeyCache = (): void => {
|
|
|
11648
11709
|
secretCache.clear();
|
|
11649
11710
|
};
|
|
11650
11711
|
|
|
11651
|
-
export type
|
|
11652
|
-
|
|
11653
|
-
|
|
11654
|
-
|
|
11655
|
-
|
|
11656
|
-
|
|
11712
|
+
export type ProviderId = Exclude<Cfg["provider"], "auto">;
|
|
11713
|
+
|
|
11714
|
+
/** Where the decision is made. No model means the provider's own default. */
|
|
11715
|
+
export type DecisionTarget = { provider: ProviderId; model?: string };
|
|
11716
|
+
|
|
11717
|
+
/** What \`auto\` needs per provider: every group must have one usable value. */
|
|
11718
|
+
const AUTO_CREDENTIALS: ReadonlyArray<[ProviderId, string[][]]> = [
|
|
11719
|
+
["typesafe", [["TYPESAFE_API_KEY"]]],
|
|
11720
|
+
["openrouter", [["OPENROUTER_API_KEY"]]],
|
|
11721
|
+
["cloudflare", [["CLOUDFLARE_API_TOKEN", "CLOUDFLARE_API_KEY"], ["CLOUDFLARE_ACCOUNT_ID"]]],
|
|
11722
|
+
];
|
|
11723
|
+
|
|
11724
|
+
const modelFor = (provider: ProviderId, cfg: Cfg): string | undefined =>
|
|
11725
|
+
provider === "typesafe"
|
|
11726
|
+
? cfg.model
|
|
11727
|
+
: provider === "openrouter"
|
|
11728
|
+
? cfg.openrouterModel
|
|
11729
|
+
: provider === "cloudflare"
|
|
11730
|
+
? cfg.cloudflareModel
|
|
11731
|
+
: undefined;
|
|
11657
11732
|
|
|
11658
11733
|
/**
|
|
11659
|
-
*
|
|
11660
|
-
*
|
|
11734
|
+
* The decision providers to try, in order. An explicit one is used as is: the
|
|
11735
|
+
* server checks its credential. \`auto\` lists every provider in \`AUTO_CREDENTIALS\`
|
|
11736
|
+
* whose credential is set and throws when none is; the handler then fails open.
|
|
11661
11737
|
*/
|
|
11662
|
-
export const
|
|
11663
|
-
|
|
11664
|
-
|
|
11665
|
-
|
|
11666
|
-
|
|
11667
|
-
|
|
11668
|
-
};
|
|
11669
|
-
|
|
11670
|
-
const key = secret("OPENROUTER_API_KEY");
|
|
11671
|
-
return usable(key)
|
|
11672
|
-
? { provider: "openrouter", endpoint: OPENROUTER_ENDPOINT, key, model: cfg.openrouterModel }
|
|
11673
|
-
: null;
|
|
11674
|
-
};
|
|
11675
|
-
const transport =
|
|
11676
|
-
cfg.provider === "typesafe"
|
|
11677
|
-
? typesafe()
|
|
11678
|
-
: cfg.provider === "openrouter"
|
|
11679
|
-
? openrouter()
|
|
11680
|
-
: (typesafe() ?? openrouter());
|
|
11681
|
-
if (transport) return transport;
|
|
11738
|
+
export const resolveProviders = (cfg: Cfg): DecisionTarget[] => {
|
|
11739
|
+
if (cfg.provider !== "auto") {
|
|
11740
|
+
return [{ provider: cfg.provider, model: modelFor(cfg.provider, cfg) }];
|
|
11741
|
+
}
|
|
11742
|
+
const targets = AUTO_CREDENTIALS.filter(([, groups]) =>
|
|
11743
|
+
groups.every((keys) => keys.some((key) => usable(secret(key)))),
|
|
11744
|
+
).map(([provider]) => ({ provider, model: modelFor(provider, cfg) }));
|
|
11745
|
+
if (targets.length > 0) return targets;
|
|
11682
11746
|
throw new Error(
|
|
11683
|
-
|
|
11684
|
-
? "no TYPESAFE_API_KEY"
|
|
11685
|
-
: cfg.provider === "openrouter"
|
|
11686
|
-
? "no OPENROUTER_API_KEY"
|
|
11687
|
-
: "no TYPESAFE_API_KEY or OPENROUTER_API_KEY",
|
|
11747
|
+
"no TYPESAFE_API_KEY, OPENROUTER_API_KEY, or CLOUDFLARE_API_TOKEN with CLOUDFLARE_ACCOUNT_ID",
|
|
11688
11748
|
);
|
|
11689
11749
|
};
|
|
11690
11750
|
|
|
11751
|
+
/** The first provider \`resolveProviders\` would try. */
|
|
11752
|
+
export const resolveProvider = (cfg: Cfg): DecisionTarget => resolveProviders(cfg)[0]!;
|
|
11753
|
+
|
|
11754
|
+
/** The server refused the provider for a deployment setting and sent nothing. */
|
|
11755
|
+
export class ProviderUnavailableError extends Error {}
|
|
11756
|
+
|
|
11691
11757
|
const SLACK_API = "https://slack.com/api";
|
|
11692
11758
|
// The engine's acceptance reactions (src/slack/reaction-shortcode.ts), by config key.
|
|
11693
11759
|
const ACCEPTANCE_REACTIONS: ReadonlyArray<[string, string]> = [
|
|
@@ -11775,7 +11841,7 @@ export const settleMutes = async (): Promise<void> => {
|
|
|
11775
11841
|
await Promise.allSettled([...pendingMutes]);
|
|
11776
11842
|
};
|
|
11777
11843
|
|
|
11778
|
-
export type
|
|
11844
|
+
export type Verdict = {
|
|
11779
11845
|
reason: string;
|
|
11780
11846
|
confidence: number | null;
|
|
11781
11847
|
probabilities: Record<string, number> | null;
|
|
@@ -11783,56 +11849,73 @@ export type JevVerdict = {
|
|
|
11783
11849
|
};
|
|
11784
11850
|
|
|
11785
11851
|
/**
|
|
11786
|
-
* One
|
|
11787
|
-
*
|
|
11852
|
+
* One decision: classify the newest message into a reason. Throws on any failure.
|
|
11853
|
+
* The server makes the call (\`system-one-decide\`), so every provider returns the
|
|
11854
|
+
* same \`answers\` shape. The time budget is enforced here and on the server.
|
|
11788
11855
|
*/
|
|
11789
|
-
export const
|
|
11856
|
+
export const askModel = async (
|
|
11857
|
+
swarm: Swarm,
|
|
11790
11858
|
input: {
|
|
11791
11859
|
messages: string[];
|
|
11792
11860
|
context: string[];
|
|
11793
11861
|
sender: string;
|
|
11794
11862
|
leadNames: readonly string[];
|
|
11795
11863
|
},
|
|
11796
|
-
|
|
11864
|
+
target: DecisionTarget,
|
|
11797
11865
|
cfg: Cfg,
|
|
11798
11866
|
outer?: AbortSignal,
|
|
11799
|
-
): Promise<
|
|
11800
|
-
const
|
|
11801
|
-
|
|
11802
|
-
|
|
11803
|
-
|
|
11867
|
+
): Promise<Verdict> => {
|
|
11868
|
+
const budgetMs = Math.min(cfg.timeoutMs, MAX_TIMEOUT_MS);
|
|
11869
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
11870
|
+
let onOuterAbort: (() => void) | undefined;
|
|
11871
|
+
const stop = new Promise<never>((_resolve, reject) => {
|
|
11872
|
+
timer = setTimeout(() => reject(new Error(\`timed out after \${budgetMs}ms\`)), budgetMs);
|
|
11873
|
+
onOuterAbort = () => reject(new Error("aborted"));
|
|
11874
|
+
outer?.addEventListener("abort", onOuterAbort);
|
|
11875
|
+
});
|
|
11804
11876
|
const t0 = Date.now();
|
|
11805
11877
|
try {
|
|
11806
|
-
const
|
|
11807
|
-
|
|
11808
|
-
|
|
11809
|
-
|
|
11810
|
-
|
|
11878
|
+
const call = swarm.systemOne_decide({
|
|
11879
|
+
provider: target.provider,
|
|
11880
|
+
...(target.model ? { model: target.model } : {}),
|
|
11881
|
+
state: {
|
|
11882
|
+
role: "Decide whether Lead, the coordinating AI agent of a team's agent swarm, should answer a new Slack message in a thread it takes part in. Lead is woken by every message in the thread, including ones not meant for it. Answering noise wastes everyone's time; missing a real ask is worse.",
|
|
11883
|
+
lead_names: [...input.leadNames],
|
|
11884
|
+
recent_thread_messages: input.context,
|
|
11885
|
+
new_message: input.messages.join("\\n---\\n"),
|
|
11886
|
+
new_message_sender: input.sender,
|
|
11811
11887
|
},
|
|
11812
|
-
|
|
11813
|
-
|
|
11814
|
-
|
|
11815
|
-
|
|
11816
|
-
|
|
11817
|
-
|
|
11818
|
-
new_message_sender: input.sender,
|
|
11888
|
+
questions: {
|
|
11889
|
+
reason: {
|
|
11890
|
+
type: "choice",
|
|
11891
|
+
instructions:
|
|
11892
|
+
"Which label best describes new_message from Lead's point of view? Use recent_thread_messages to tell who it is talking to. Lines starting with [Agent] are swarm bots.",
|
|
11893
|
+
criteria: REASONS,
|
|
11819
11894
|
},
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
type: "choice",
|
|
11824
|
-
instructions:
|
|
11825
|
-
"Which label best describes new_message from Lead's point of view? Use recent_thread_messages to tell who it is talking to. Lines starting with [Agent] are swarm bots.",
|
|
11826
|
-
criteria: REASONS,
|
|
11827
|
-
},
|
|
11828
|
-
},
|
|
11829
|
-
}),
|
|
11830
|
-
signal: controller.signal,
|
|
11895
|
+
},
|
|
11896
|
+
timeoutMs: Math.max(budgetMs, MIN_DECIDE_TIMEOUT_MS),
|
|
11897
|
+
maxRetries: 0,
|
|
11831
11898
|
});
|
|
11832
|
-
|
|
11833
|
-
|
|
11899
|
+
// After a timeout nobody awaits the call; its late failure must not go unhandled.
|
|
11900
|
+
call.catch(() => {});
|
|
11901
|
+
const res = (await Promise.race([call, stop])) as {
|
|
11902
|
+
success?: boolean;
|
|
11903
|
+
status?: number;
|
|
11904
|
+
data?: unknown;
|
|
11905
|
+
} | null;
|
|
11906
|
+
const body = (res?.data ?? res) as {
|
|
11907
|
+
success?: boolean;
|
|
11908
|
+
message?: unknown;
|
|
11909
|
+
error?: unknown;
|
|
11910
|
+
providerUnavailable?: unknown;
|
|
11834
11911
|
answers?: { reason?: { choice?: unknown; confidence?: unknown; probabilities?: unknown } };
|
|
11835
11912
|
} | null;
|
|
11913
|
+
if (res?.success === false || body?.success === false) {
|
|
11914
|
+
const why = \`\${target.provider}: \${String(body?.message ?? body?.error ?? \`HTTP \${res?.status}\`).slice(0, 200)}\`;
|
|
11915
|
+
throw body?.providerUnavailable === true
|
|
11916
|
+
? new ProviderUnavailableError(why)
|
|
11917
|
+
: new Error(why);
|
|
11918
|
+
}
|
|
11836
11919
|
const answer = body?.answers?.reason;
|
|
11837
11920
|
const reason = typeof answer?.choice === "string" ? answer.choice : "";
|
|
11838
11921
|
if (!(reason in REASONS)) throw new Error(\`unexpected choice: \${String(reason).slice(0, 60)}\`);
|
|
@@ -11844,12 +11927,34 @@ export const askJev = async (
|
|
|
11844
11927
|
return { reason, confidence, probabilities, latencyMs: Date.now() - t0 };
|
|
11845
11928
|
} finally {
|
|
11846
11929
|
clearTimeout(timer);
|
|
11847
|
-
outer?.removeEventListener("abort", onOuterAbort);
|
|
11930
|
+
if (onOuterAbort) outer?.removeEventListener("abort", onOuterAbort);
|
|
11848
11931
|
}
|
|
11849
11932
|
};
|
|
11850
11933
|
|
|
11934
|
+
/**
|
|
11935
|
+
* \`askModel\` on each target in turn, moving on only when the server refused the
|
|
11936
|
+
* provider for a deployment setting (no request was sent). Any other failure, or
|
|
11937
|
+
* a refusal of the last target, throws.
|
|
11938
|
+
*/
|
|
11939
|
+
export const askFirstAvailable = async (
|
|
11940
|
+
swarm: Swarm,
|
|
11941
|
+
input: Parameters<typeof askModel>[1],
|
|
11942
|
+
targets: readonly DecisionTarget[],
|
|
11943
|
+
cfg: Cfg,
|
|
11944
|
+
outer?: AbortSignal,
|
|
11945
|
+
): Promise<{ verdict: Verdict; target: DecisionTarget }> => {
|
|
11946
|
+
for (const [i, target] of targets.entries()) {
|
|
11947
|
+
try {
|
|
11948
|
+
return { verdict: await askModel(swarm, input, target, cfg, outer), target };
|
|
11949
|
+
} catch (err) {
|
|
11950
|
+
if (!(err instanceof ProviderUnavailableError) || i === targets.length - 1) throw err;
|
|
11951
|
+
}
|
|
11952
|
+
}
|
|
11953
|
+
throw new Error("no decision provider");
|
|
11954
|
+
};
|
|
11955
|
+
|
|
11851
11956
|
/** Probability that the message needs no reply: the summed mass of the skip labels. */
|
|
11852
|
-
export const skipProbability = (verdict:
|
|
11957
|
+
export const skipProbability = (verdict: Verdict): number | null => {
|
|
11853
11958
|
if (verdict.probabilities) {
|
|
11854
11959
|
let sum = 0;
|
|
11855
11960
|
for (const [label, p] of Object.entries(verdict.probabilities)) {
|
|
@@ -11860,9 +11965,9 @@ export const skipProbability = (verdict: JevVerdict): number | null => {
|
|
|
11860
11965
|
return verdict.confidence;
|
|
11861
11966
|
};
|
|
11862
11967
|
|
|
11863
|
-
/** Map a
|
|
11968
|
+
/** Map a verdict to the typed decision. An unsure skip verdict replies. */
|
|
11864
11969
|
export const decide = (
|
|
11865
|
-
verdict:
|
|
11970
|
+
verdict: Verdict,
|
|
11866
11971
|
minConfidence: number,
|
|
11867
11972
|
): { reply: boolean; reason: Reason; skipP: number | null; lowConfidence: boolean } => {
|
|
11868
11973
|
const reason = verdict.reason as Reason;
|
|
@@ -11959,7 +12064,7 @@ const extension: SwarmExtension = (api) => {
|
|
|
11959
12064
|
return;
|
|
11960
12065
|
}
|
|
11961
12066
|
|
|
11962
|
-
const
|
|
12067
|
+
const targets = resolveProviders(cfg);
|
|
11963
12068
|
|
|
11964
12069
|
const sender =
|
|
11965
12070
|
opts.slackUserId && cfg.botSlackUserIds.includes(opts.slackUserId)
|
|
@@ -11968,14 +12073,15 @@ const extension: SwarmExtension = (api) => {
|
|
|
11968
12073
|
? "human (known teammate)"
|
|
11969
12074
|
: "unknown (not a known teammate; may be a bot or an external person)";
|
|
11970
12075
|
|
|
11971
|
-
const verdict = await
|
|
12076
|
+
const { verdict, target } = await askFirstAvailable(
|
|
12077
|
+
ctx.swarm,
|
|
11972
12078
|
{
|
|
11973
12079
|
messages,
|
|
11974
12080
|
context: context.slice(-cfg.contextMessages).map((m) => m.slice(0, 500)),
|
|
11975
12081
|
sender,
|
|
11976
12082
|
leadNames: cfg.leadAliases,
|
|
11977
12083
|
},
|
|
11978
|
-
|
|
12084
|
+
targets,
|
|
11979
12085
|
cfg,
|
|
11980
12086
|
ctx.signal,
|
|
11981
12087
|
);
|
|
@@ -11994,7 +12100,7 @@ const extension: SwarmExtension = (api) => {
|
|
|
11994
12100
|
skipP: decision.skipP,
|
|
11995
12101
|
lowConfidence: decision.lowConfidence,
|
|
11996
12102
|
latencyMs: verdict.latencyMs,
|
|
11997
|
-
provider:
|
|
12103
|
+
provider: target.provider,
|
|
11998
12104
|
sender,
|
|
11999
12105
|
preview,
|
|
12000
12106
|
},
|
|
@@ -12034,7 +12140,7 @@ const extension: SwarmExtension = (api) => {
|
|
|
12034
12140
|
export default extension;
|
|
12035
12141
|
`
|
|
12036
12142
|
},
|
|
12037
|
-
readme: '# slack-reply-gate\n\nLead is woken by every human message in a Slack thread the swarm takes part in, including "lol", an emoji, or two people chatting with each other. This extension asks
|
|
12143
|
+
readme: '# slack-reply-gate\n\nLead is woken by every human message in a Slack thread the swarm takes part in, including "lol", an emoji, or two people chatting with each other. This extension asks a SystemOne decision model, Jev ([TypeSafe](https://typesafe.ai)) or Clef (Cloudflare Workers AI), whether Lead should answer a new thread message, and drops the wake-up when it should not.\n\nInstall config:\n\n```json\n{ "mode": "shadow" }\n```\n\nStart in `shadow`, read the decisions in KV for a few days, then switch to `enforce`.\n\n## Modes\n\n| `mode` | Effect |\n|---|---|\n| `off` | Never evaluates. |\n| `shadow` (default) | Asks the model and records the verdict. Never blocks. |\n| `enforce` | Asks the model, records the verdict, and blocks the Lead task when the answer is no. The message gets :mute: instead: no Lead task, no reply, no outcome card. |\n\n## What it gates\n\nHook: `pre.task.create`, for tasks with origin `slack` whose description carries the thread buffer\'s header `[Thread follow-up — N message(s) buffered]`. Those are the non-mention thread replies that `src/slack/thread-buffer.ts` batches into a Lead task. A direct @mention of the bot creates a task without that header and is never gated.\n\nThe header counts only at the start of the description or right after the closing `</thread_context>`. Thread context quotes old messages verbatim, and an older Lead reply can quote the header itself, so a header inside the context is ignored. Only the text after the real header is treated as new.\n\n## Decision\n\n1. A follow-up with downloaded files (status `draft`) passes without a decision call.\n2. **@mention pass-through.** A new message that @mentions Lead passes without a decision call. A mention is the bot mention the engine rewrites to `<@U…> (that\'s you)`, a resolved `<@U…|lead>`, or a literal `@lead` (any name in `leadAliases`). Only the new messages are scanned, never the thread context, so an old mention earlier in the thread does not pass later banter.\n3. Otherwise one decision call (one `choice` question, see [Provider](#provider)) with the new message(s), the sender kind, and the last `contextMessages` thread messages. Labels: `direct_ask`, `followup_to_lead`, `instruction_to_lead`, `reaction_only`, `banter_between_others`, `not_addressed_to_lead`, `other`.\n4. The answer is no only when the top label is `reaction_only`, `banter_between_others`, or `not_addressed_to_lead`, and the summed probability of those three labels is at least `minConfidence`. A split between two skip labels is still a skip. An unsure verdict replies.\n\nSeveral buffered messages are judged together. One mention in any of them passes the batch.\n\n## Provider\n\nThe decision is made on the API server by the `system-one-decide` tool (`ctx.swarm.systemOne_decide`), the same path a `system-one-decision` workflow node takes. The server reads the provider\'s credential, sends the request, and validates the answer, so every SystemOne host works and no key enters the hook.\n\n| `provider` | Model | Credential |\n|---|---|---|\n| `typesafe` | Jev, `model` (default `jev-latest`) | `TYPESAFE_API_KEY` |\n| `openrouter` | Jev, `openrouterModel` (default `typesafe/jev-1.13`) | `OPENROUTER_API_KEY` |\n| `cloudflare` | Clef, `cloudflareModel` (default `clef`, or `clef-flash`) | `CLOUDFLARE_API_TOKEN` (or `CLOUDFLARE_API_KEY`) and `CLOUDFLARE_ACCOUNT_ID` |\n| `laya`, `openai` | The host\'s default model | See the workflows guide |\n| `auto` (default) | The first of typesafe, openrouter, cloudflare whose credential is set. A provider the server refuses for a deployment setting (openrouter when `OPENROUTER_BASE_URL` points at a gateway) is skipped for the next one | |\n\nWith no credential for `auto`, every message fails open and the KV row names the secrets. Each decision row records the `provider` that answered. `minConfidence` was tuned on Jev; check Clef\'s verdicts in `shadow` before you enforce on it.\n\n## Fail open\n\nEvery error lets the task through: a missing key, a provider error relayed by the server, a network error, a timeout, a malformed answer, or an unknown label. The handler never throws, so an outage cannot auto-disable the extension. Each failure is recorded as `fail-open` in KV and logged as a warning.\n\n## :mute: on drop (enforce)\n\nNo swarm tool or extension `ctx` method adds a Slack reaction, so the hook calls the Slack Web API with the bot token. It needs the `reactions:write` scope, which the Slack app already uses for its acceptance reactions.\n\n1. `reactions.remove` for each acceptance reaction the engine may have put on the triggering message: `eyes`, `heavy_plus_sign`, `zap`, `speech_balloon`, or their `SLACK_REACTION_ACCEPTED`, `SLACK_REACTION_BUFFERED`, `SLACK_REACTION_NOW`, and `SLACK_REACTION_STEERED` overrides. Slack only removes the bot\'s own reaction, so a human\'s :eyes: stays.\n2. `reactions.add` with `muteReaction` (default `mute`). `already_reacted` counts as success. Skipped when `muteReaction` is `""`.\n\nThe block returns first and the Slack calls run after it. A slow or failing Slack call never delays the drop or turns it back into a task. Set `muteReaction` to `""` for a true no-op: step 1 still clears the bot\'s acceptance reaction(s), and nothing is added.\n\n## Config\n\n| Key | Default | Notes |\n|---|---|---|\n| `mode` | `shadow` | `off`, `shadow`, or `enforce` |\n| `timeoutMs` | `3000` | Decision call timeout, capped at 4000 (the dispatcher cancels a handler at 5 s) |\n| `provider` | `auto` | `auto`, `typesafe`, `openrouter`, `cloudflare`, `laya`, or `openai`. See [Provider](#provider) |\n| `model` | `jev-latest` | Jev model on TypeSafe |\n| `openrouterModel` | `typesafe/jev-1.13` | Jev model on OpenRouter |\n| `cloudflareModel` | `clef` | Clef model on Cloudflare Workers AI |\n| `minConfidence` | `0.55` | Minimum summed skip probability to skip |\n| `contextMessages` | `6` | Thread messages sent to the model, 0 to 20 |\n| `leadAliases` | `["lead"]` | A literal `@alias` in a new message counts as a mention |\n| `botSlackUserIds` | `[]` | Slack user ids labelled `bot` as the sender for the model |\n| `muteReaction` | `mute` | Reaction for a dropped message; `""` only removes the acceptance reaction(s) |\n| `slackTimeoutMs` | `5000` | Timeout per Slack call |\n\n## Required secrets\n\nSet them as global `swarm_config` values. The server reads the provider credential from the global row when it makes the decision. `auto` and the Slack token read the API process environment, which loads global rows at boot and on config reload.\n\n- One provider credential from the [Provider](#provider) table is required for any evaluation. Without one, every message fails open. The `apiKey` config field of earlier versions is gone: the hook never holds a key, so store it as the global `TYPESAFE_API_KEY` secret.\n- `SLACK_BOT_TOKEN`: the Slack app\'s bot token. A swarm with Slack connected already has it. Without it, `enforce` still drops the message but cannot touch its reactions, and logs a `mute-error`.\n\nNo key is written to config, KV, logs, or a description.\n\n## KV namespace `ext:slack-reply-gate`\n\n- `d:<epochMs>:<channel>_<threadTs>`: one row per decision, with `action` (`pass`, `shadow-skip`, `skipped`, `fail-open`), `provider`, `reason`, `confidence`, `skipP`, `latencyMs`, `sender`, and a 300-character `preview`.\n- `m:<epochMs>:<channel>_<threadTs>`: one row per mute attempt, with `action` (`muted`, `unreacted` when `muteReaction` is `""`, `mute-error`), `removed`, `added`, and `errors`.\n- `last`: the latest decision. Mute rows never overwrite it.\n- Counters: `total`, `mention`, `files`, `reply`, `shadow-skip`, `skipped`, `error`, `muted`, `unreacted`, `mute-error`, and `reason:<label>`.\n\n## Known limits\n\n- When Lead already has an active task in the thread, the buffer steers that task instead of creating one, so `pre.task.create` does not fire and the gate does not run.\n- A blocked flush makes the thread buffer log a `TaskCreationBlockedError`. Nothing is lost: the next follow-up\'s `<thread_context>` carries the earlier messages.\n- A flush of several messages mutes only the triggering (last) message. The others keep their acceptance reaction.\n'
|
|
12038
12144
|
},
|
|
12039
12145
|
"task-digest": {
|
|
12040
12146
|
manifestFile: "manifest.yaml",
|
|
@@ -13220,6 +13326,17 @@ async function handleFleetBenchmark(req, res, pathSegments, queryParams) {
|
|
|
13220
13326
|
// src/http/fs.ts
|
|
13221
13327
|
init_zod();
|
|
13222
13328
|
init_db3();
|
|
13329
|
+
|
|
13330
|
+
// src/be/db/tasks/draft-text.ts
|
|
13331
|
+
init_runtime();
|
|
13332
|
+
async function appendDraftTaskText(taskId, block) {
|
|
13333
|
+
const result = await getDbClient2().run("UPDATE agent_tasks SET task = task || ?, lastUpdatedAt = ? WHERE id = ? AND status = 'draft'", [`
|
|
13334
|
+
|
|
13335
|
+
${block}`, new Date().toISOString(), taskId]);
|
|
13336
|
+
return result.changes > 0;
|
|
13337
|
+
}
|
|
13338
|
+
|
|
13339
|
+
// src/http/fs.ts
|
|
13223
13340
|
init_rbac();
|
|
13224
13341
|
init_types();
|
|
13225
13342
|
|
|
@@ -13343,7 +13460,7 @@ var uploadTaskFileRoute = route({
|
|
|
13343
13460
|
pattern: ["api", "fs", "tasks", null, "files"],
|
|
13344
13461
|
session: { taskParam: "taskId" },
|
|
13345
13462
|
summary: "Upload a binary task file attachment",
|
|
13346
|
-
description: "Accepts a raw binary request body. Pass the display/path name as the `name` query parameter.",
|
|
13463
|
+
description: "Accepts a raw binary request body. Pass the display/path name as the `name` query parameter. An `audio/*` file uploaded to a `draft` task gets a `[File: …]` line and its transcript (or why there is none) appended to the task text before the response; a failed transcription never fails the upload.",
|
|
13347
13464
|
tags: ["FS"],
|
|
13348
13465
|
params: taskParams,
|
|
13349
13466
|
query: uploadQuery,
|
|
@@ -13504,7 +13621,7 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
13504
13621
|
return notFound(res, "Task not found");
|
|
13505
13622
|
if (!await canMutateTask(task, myAgentId, req))
|
|
13506
13623
|
return forbidden(res);
|
|
13507
|
-
return sendUpload(req, res, parsed.params.taskId, parsed.query, myAgentId ?? null);
|
|
13624
|
+
return sendUpload(req, res, parsed.params.taskId, parsed.query, myAgentId ?? null, task.status === "draft");
|
|
13508
13625
|
}
|
|
13509
13626
|
if (listTaskFilesRoute.match(req.method, pathSegments)) {
|
|
13510
13627
|
const parsed = await listTaskFilesRoute.parse(req, res, pathSegments, queryParams);
|
|
@@ -13531,7 +13648,7 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
13531
13648
|
}
|
|
13532
13649
|
return false;
|
|
13533
13650
|
}
|
|
13534
|
-
async function sendUpload(req, res, taskId, query, agentId) {
|
|
13651
|
+
async function sendUpload(req, res, taskId, query, agentId, isDraft) {
|
|
13535
13652
|
const body = await readRawBody(req, MAX_TASK_ATTACHMENT_BYTES);
|
|
13536
13653
|
if (body === BODY_TOO_LARGE) {
|
|
13537
13654
|
jsonError(res, `Payload too large (max ${MAX_TASK_ATTACHMENT_BYTES} bytes)`, 413);
|
|
@@ -13572,9 +13689,27 @@ async function sendUpload(req, res, taskId, query, agentId) {
|
|
|
13572
13689
|
isPrimary: query.isPrimary === "true",
|
|
13573
13690
|
createdBy: auth?.kind === "user" ? auth.userId : undefined
|
|
13574
13691
|
});
|
|
13692
|
+
const mimeType = contentType.split(";")[0].trim().toLowerCase();
|
|
13693
|
+
if (isDraft && mimeType.startsWith("audio/")) {
|
|
13694
|
+
await appendAudioTranscript(taskId, query.name, mimeType, body);
|
|
13695
|
+
}
|
|
13575
13696
|
uploadTaskFileRoute.respond(res, 201, attachment);
|
|
13576
13697
|
return true;
|
|
13577
13698
|
}
|
|
13699
|
+
async function appendAudioTranscript(taskId, name, mimeType, body) {
|
|
13700
|
+
try {
|
|
13701
|
+
const notes = await transcribeAudioNotes([{ key: name, name, data: new Blob([body], { type: mimeType }), mimeType }], "[fs]");
|
|
13702
|
+
const note = notes.get(name);
|
|
13703
|
+
if (!note)
|
|
13704
|
+
return;
|
|
13705
|
+
if (!await appendDraftTaskText(taskId, `[File: ${name} (${mimeType})]
|
|
13706
|
+
${note}`)) {
|
|
13707
|
+
console.warn(`[fs] task ${taskId} left draft before the transcript of ${name} was added`);
|
|
13708
|
+
}
|
|
13709
|
+
} catch (error) {
|
|
13710
|
+
console.warn(scrubSecrets(`[fs] could not add the transcript of ${name} to task ${taskId}: ${error instanceof Error ? error.message : String(error)}`));
|
|
13711
|
+
}
|
|
13712
|
+
}
|
|
13578
13713
|
async function sendDownload(res, attachment) {
|
|
13579
13714
|
const provider = getFileStorageProvider2();
|
|
13580
13715
|
if (backingProviderId(attachment) !== provider.id) {
|
|
@@ -13634,23 +13769,6 @@ async function sendDelete(res, attachment) {
|
|
|
13634
13769
|
res.end();
|
|
13635
13770
|
return true;
|
|
13636
13771
|
}
|
|
13637
|
-
function scopeFromAttachment(attachment) {
|
|
13638
|
-
const storedKey = attachment.providerKey ?? attachment.path ?? "";
|
|
13639
|
-
return {
|
|
13640
|
-
taskId: attachment.taskId,
|
|
13641
|
-
name: attachment.name,
|
|
13642
|
-
key: storedKey.trim() ? storedKey : undefined,
|
|
13643
|
-
orgId: attachment.orgId,
|
|
13644
|
-
driveId: attachment.driveId
|
|
13645
|
-
};
|
|
13646
|
-
}
|
|
13647
|
-
function backingProviderId(attachment) {
|
|
13648
|
-
if (attachment.kind === "agent-fs")
|
|
13649
|
-
return attachment.providerId ?? "agent-fs";
|
|
13650
|
-
if (attachment.providerId === "local-fs")
|
|
13651
|
-
return "local-fs";
|
|
13652
|
-
return null;
|
|
13653
|
-
}
|
|
13654
13772
|
async function findAttachment(req, myAgentId, taskId, attachmentId, res) {
|
|
13655
13773
|
if (!await getTaskById2(taskId)) {
|
|
13656
13774
|
notFound(res, "Task not found");
|
|
@@ -16408,6 +16526,29 @@ init_db3();
|
|
|
16408
16526
|
init_env_flag();
|
|
16409
16527
|
init_request_auth_context();
|
|
16410
16528
|
init_task_tool_manifest();
|
|
16529
|
+
|
|
16530
|
+
// src/http/mcp-body.ts
|
|
16531
|
+
var MCP_BODY_TOO_LARGE = Symbol("mcp-body-too-large");
|
|
16532
|
+
async function readMcpBody(req, res) {
|
|
16533
|
+
try {
|
|
16534
|
+
return await parseBody(req, MCP_MAX_REQUEST_BODY_BYTES);
|
|
16535
|
+
} catch (error) {
|
|
16536
|
+
if (!(error instanceof RequestBodyTooLargeError))
|
|
16537
|
+
throw error;
|
|
16538
|
+
res.writeHead(413, { "Content-Type": "application/json", Connection: "close" });
|
|
16539
|
+
res.end(JSON.stringify({
|
|
16540
|
+
jsonrpc: "2.0",
|
|
16541
|
+
error: {
|
|
16542
|
+
code: -32000,
|
|
16543
|
+
message: `Payload Too Large: Request body must not exceed ${MCP_MAX_REQUEST_BODY_BYTES} bytes`
|
|
16544
|
+
},
|
|
16545
|
+
id: null
|
|
16546
|
+
}));
|
|
16547
|
+
return MCP_BODY_TOO_LARGE;
|
|
16548
|
+
}
|
|
16549
|
+
}
|
|
16550
|
+
|
|
16551
|
+
// src/http/mcp.ts
|
|
16411
16552
|
var DEFAULT_MCP_TRANSPORT_IDLE_TIMEOUT_MS = 2 * 60 * 60 * 1000;
|
|
16412
16553
|
function respondNoMcpSession(res, sessionId) {
|
|
16413
16554
|
const notFound = Boolean(sessionId);
|
|
@@ -16507,17 +16648,16 @@ async function handleMcp(req, res, transports, sessionActivity = {}, sessionAgen
|
|
|
16507
16648
|
if (agentMismatch)
|
|
16508
16649
|
return true;
|
|
16509
16650
|
if (req.method === "POST") {
|
|
16510
|
-
const
|
|
16511
|
-
|
|
16512
|
-
|
|
16513
|
-
|
|
16514
|
-
|
|
16515
|
-
if (!sessionId && body.method === "server/discover") {
|
|
16651
|
+
const body = await readMcpBody(req, res);
|
|
16652
|
+
if (body === MCP_BODY_TOO_LARGE)
|
|
16653
|
+
return true;
|
|
16654
|
+
const rpc = body;
|
|
16655
|
+
if (!sessionId && rpc?.method === "server/discover") {
|
|
16516
16656
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
16517
16657
|
res.end(JSON.stringify({
|
|
16518
16658
|
jsonrpc: "2.0",
|
|
16519
16659
|
error: { code: -32601, message: "Method not found" },
|
|
16520
|
-
id:
|
|
16660
|
+
id: rpc.id ?? null
|
|
16521
16661
|
}));
|
|
16522
16662
|
return true;
|
|
16523
16663
|
}
|
|
@@ -17854,7 +17994,8 @@ function createUserServer(user) {
|
|
|
17854
17994
|
}, {
|
|
17855
17995
|
capabilities: {
|
|
17856
17996
|
logging: {}
|
|
17857
|
-
}
|
|
17997
|
+
},
|
|
17998
|
+
maxToolInputElements: MCP_MAX_TOOL_INPUT_ELEMENTS
|
|
17858
17999
|
});
|
|
17859
18000
|
const registerTool = createToolRegistrar(server);
|
|
17860
18001
|
const sendTaskConfig = {
|
|
@@ -17972,11 +18113,9 @@ async function handleMcpUser(req, res, transports, sessionUsers, sessionActivity
|
|
|
17972
18113
|
return unauthorized2(res);
|
|
17973
18114
|
}
|
|
17974
18115
|
if (req.method === "POST") {
|
|
17975
|
-
const
|
|
17976
|
-
|
|
17977
|
-
|
|
17978
|
-
}
|
|
17979
|
-
const body = JSON.parse(Buffer.concat(chunks).toString());
|
|
18116
|
+
const body = await readMcpBody(req, res);
|
|
18117
|
+
if (body === MCP_BODY_TOO_LARGE)
|
|
18118
|
+
return true;
|
|
17980
18119
|
let transport;
|
|
17981
18120
|
if (sessionId && transports[sessionId]) {
|
|
17982
18121
|
transport = transports[sessionId];
|
|
@@ -18118,6 +18257,9 @@ async function hasRetrievalForTask(taskId, memoryId) {
|
|
|
18118
18257
|
return row != null;
|
|
18119
18258
|
}
|
|
18120
18259
|
|
|
18260
|
+
// src/http/memory.ts
|
|
18261
|
+
init_search_candidates();
|
|
18262
|
+
|
|
18121
18263
|
// src/be/memory/usefulness-stats.ts
|
|
18122
18264
|
init_db3();
|
|
18123
18265
|
async function getUsefulnessStats(options = {}) {
|
|
@@ -18958,7 +19100,7 @@ async function handleMemory(req, res, pathSegments, callerAgentId) {
|
|
|
18958
19100
|
const store = getMemoryStore2();
|
|
18959
19101
|
const queryEmbedding = await provider.embed(query);
|
|
18960
19102
|
const candidateLimit = Math.min(limit, 20) * CANDIDATE_SET_MULTIPLIER;
|
|
18961
|
-
const candidates = await store
|
|
19103
|
+
const candidates = await searchMemoryCandidates(store, queryEmbedding ?? new Float32Array(0), myAgentId, {
|
|
18962
19104
|
scope,
|
|
18963
19105
|
limit: candidateLimit,
|
|
18964
19106
|
source,
|
|
@@ -19039,7 +19181,7 @@ async function handleMemory(req, res, pathSegments, callerAgentId) {
|
|
|
19039
19181
|
const provider = getEmbeddingProvider2();
|
|
19040
19182
|
const queryEmbedding = await provider.embed(query.trim());
|
|
19041
19183
|
const candidateLimit = Math.min(4096, Math.max(offset + pageLimit, pageLimit) * CANDIDATE_SET_MULTIPLIER);
|
|
19042
|
-
let candidates = await store
|
|
19184
|
+
let candidates = await searchMemoryCandidates(store, queryEmbedding ?? new Float32Array(0), viewer.agentId, {
|
|
19043
19185
|
scope,
|
|
19044
19186
|
limit: candidateLimit,
|
|
19045
19187
|
isLead: viewer.seesAll,
|
|
@@ -21675,6 +21817,7 @@ async function buildSignals(state) {
|
|
|
21675
21817
|
slack: integrations.slack === "configured",
|
|
21676
21818
|
github: Boolean(process.env.GITHUB_TOKEN?.trim()) || integrations.github === "verified",
|
|
21677
21819
|
gitlab: Boolean(process.env.GITLAB_TOKEN?.trim()),
|
|
21820
|
+
bitbucket: Boolean(process.env.BITBUCKET_TOKEN?.trim()),
|
|
21678
21821
|
linear: integrations.linear === "verified",
|
|
21679
21822
|
jira: integrations.jira === "verified"
|
|
21680
21823
|
},
|
|
@@ -21974,6 +22117,35 @@ route({
|
|
|
21974
22117
|
auth: { apiKey: false }
|
|
21975
22118
|
});
|
|
21976
22119
|
var PROXY_PREFIX = "/@swarm/api/";
|
|
22120
|
+
var PAGE_ID_REQUEST_HEADER = "x-swarm-page-id";
|
|
22121
|
+
var PAGE_PATH_RE = /^\/p\/([^/.]+)/;
|
|
22122
|
+
function requestedPageId(req) {
|
|
22123
|
+
const raw = req.headers[PAGE_ID_REQUEST_HEADER];
|
|
22124
|
+
const fromHeader = Array.isArray(raw) ? raw[0] : raw;
|
|
22125
|
+
if (fromHeader !== undefined)
|
|
22126
|
+
return isCookieSafePageId(fromHeader) ? fromHeader : null;
|
|
22127
|
+
const referer = req.headers.referer;
|
|
22128
|
+
if (!referer)
|
|
22129
|
+
return;
|
|
22130
|
+
let refererUrl;
|
|
22131
|
+
try {
|
|
22132
|
+
refererUrl = new URL(referer);
|
|
22133
|
+
} catch {
|
|
22134
|
+
return;
|
|
22135
|
+
}
|
|
22136
|
+
if (refererUrl.host !== req.headers.host)
|
|
22137
|
+
return;
|
|
22138
|
+
const match = PAGE_PATH_RE.exec(refererUrl.pathname);
|
|
22139
|
+
if (!match)
|
|
22140
|
+
return;
|
|
22141
|
+
let fromReferer;
|
|
22142
|
+
try {
|
|
22143
|
+
fromReferer = decodeURIComponent(match[1]);
|
|
22144
|
+
} catch {
|
|
22145
|
+
return;
|
|
22146
|
+
}
|
|
22147
|
+
return isCookieSafePageId(fromReferer) ? fromReferer : undefined;
|
|
22148
|
+
}
|
|
21977
22149
|
function isSafeProxySuffix(suffix) {
|
|
21978
22150
|
if (suffix.length === 0)
|
|
21979
22151
|
return true;
|
|
@@ -22012,11 +22184,13 @@ async function handlePageProxy(req, res) {
|
|
|
22012
22184
|
jsonError(res, "not found", 404);
|
|
22013
22185
|
return true;
|
|
22014
22186
|
}
|
|
22015
|
-
const
|
|
22016
|
-
|
|
22187
|
+
const claimedPageId = requestedPageId(req);
|
|
22188
|
+
const session = claimedPageId === undefined ? await extractLegacyPageSession(req) : claimedPageId === null ? null : await extractPageSession(req, claimedPageId);
|
|
22189
|
+
if (!session) {
|
|
22017
22190
|
jsonError(res, "no page session", 401);
|
|
22018
22191
|
return true;
|
|
22019
22192
|
}
|
|
22193
|
+
const { payload } = session;
|
|
22020
22194
|
const page = await getPage2(payload.pageId);
|
|
22021
22195
|
if (!page) {
|
|
22022
22196
|
jsonError(res, "page session no longer valid", 401);
|
|
@@ -22034,7 +22208,7 @@ async function handlePageProxy(req, res) {
|
|
|
22034
22208
|
const headers = {
|
|
22035
22209
|
Authorization: `Bearer ${apiKey}`,
|
|
22036
22210
|
"X-Page-Id": page.id,
|
|
22037
|
-
"X-Page-Session":
|
|
22211
|
+
"X-Page-Session": session.token
|
|
22038
22212
|
};
|
|
22039
22213
|
const reqContentType = req.headers["content-type"];
|
|
22040
22214
|
if (reqContentType) {
|
|
@@ -22678,7 +22852,7 @@ var updateTaskVcsRoute = route({
|
|
|
22678
22852
|
tags: ["Tasks"],
|
|
22679
22853
|
params: object2({ id: string2() }),
|
|
22680
22854
|
body: object2({
|
|
22681
|
-
vcsProvider: _enum2(["github", "gitlab", "azure-devops"]),
|
|
22855
|
+
vcsProvider: _enum2(["github", "gitlab", "azure-devops", "bitbucket"]),
|
|
22682
22856
|
vcsRepo: string2(),
|
|
22683
22857
|
vcsNumber: number2().int().positive(),
|
|
22684
22858
|
vcsUrl: string2().url()
|
|
@@ -24378,12 +24552,12 @@ ${defuseFeedbackTags(note)}
|
|
|
24378
24552
|
}
|
|
24379
24553
|
const auth = getRequestAuth(req);
|
|
24380
24554
|
const identity = auth?.kind === "user" ? { uid: auth.userId, name: auth.user.name } : auth?.kind === "operator" ? { name: `guest-${randomUUID4().slice(0, 8)}`, op: true } : undefined;
|
|
24381
|
-
const
|
|
24555
|
+
const cookies = await pageSessionSetCookies(req, page.id, {
|
|
24382
24556
|
dev: isDevRequest(req),
|
|
24383
24557
|
...identity
|
|
24384
24558
|
});
|
|
24385
24559
|
applyLaunchCors(req, res);
|
|
24386
|
-
res.setHeader("Set-Cookie",
|
|
24560
|
+
res.setHeader("Set-Cookie", cookies);
|
|
24387
24561
|
res.writeHead(204);
|
|
24388
24562
|
res.end();
|
|
24389
24563
|
return true;
|
|
@@ -24405,10 +24579,15 @@ class SwarmSDK {
|
|
|
24405
24579
|
this.channel = async (name) => (await loadRealtime()).channel(name);
|
|
24406
24580
|
|
|
24407
24581
|
const base = '/@swarm/api';
|
|
24582
|
+
// Each page has its own session cookie. Naming the page tells the proxy
|
|
24583
|
+
// which one to read; the cookie's signed payload must still match it.
|
|
24584
|
+
const pathParts = typeof location === 'undefined' ? [] : location.pathname.split('/');
|
|
24585
|
+
const pageId = pathParts[1] === 'p' && pathParts[2] ? pathParts[2].split('.')[0] : '';
|
|
24408
24586
|
const call = async (method, path, body) => {
|
|
24409
|
-
const init = { method };
|
|
24587
|
+
const init = { method, headers: {} };
|
|
24588
|
+
if (pageId) init.headers['X-Swarm-Page-Id'] = pageId;
|
|
24410
24589
|
if (body !== undefined) {
|
|
24411
|
-
init.headers
|
|
24590
|
+
init.headers['Content-Type'] = 'application/json';
|
|
24412
24591
|
init.body = JSON.stringify(body);
|
|
24413
24592
|
}
|
|
24414
24593
|
const res = await fetch(base + path, init);
|
|
@@ -25593,7 +25772,7 @@ var FEEDBACK_OVERLAY_JS = `
|
|
|
25593
25772
|
return fetch('/@swarm/api/pages/' + encodeURIComponent(pageId) + '/feedback', {
|
|
25594
25773
|
method: 'POST',
|
|
25595
25774
|
credentials: 'same-origin',
|
|
25596
|
-
headers: { 'Content-Type': 'application/json' },
|
|
25775
|
+
headers: { 'Content-Type': 'application/json', 'X-Swarm-Page-Id': pageId },
|
|
25597
25776
|
body: JSON.stringify(payload),
|
|
25598
25777
|
}).then(function (res) {
|
|
25599
25778
|
return res.text().then(function (text) {
|
|
@@ -25979,23 +26158,8 @@ function buildCsp() {
|
|
|
25979
26158
|
function isAccessible(page, cookiePayload) {
|
|
25980
26159
|
if (page.authMode === "public")
|
|
25981
26160
|
return { ok: true };
|
|
25982
|
-
if (cookiePayload)
|
|
25983
|
-
|
|
25984
|
-
return { ok: true };
|
|
25985
|
-
if (page.authMode === "password") {
|
|
25986
|
-
return {
|
|
25987
|
-
ok: false,
|
|
25988
|
-
status: 401,
|
|
25989
|
-
reason: "password required",
|
|
25990
|
-
needsPassword: true
|
|
25991
|
-
};
|
|
25992
|
-
}
|
|
25993
|
-
return {
|
|
25994
|
-
ok: false,
|
|
25995
|
-
status: 403,
|
|
25996
|
-
reason: "page-session cookie scoped to a different page id"
|
|
25997
|
-
};
|
|
25998
|
-
}
|
|
26161
|
+
if (cookiePayload?.pageId === page.id)
|
|
26162
|
+
return { ok: true };
|
|
25999
26163
|
if (page.authMode === "authed") {
|
|
26000
26164
|
return {
|
|
26001
26165
|
ok: false,
|
|
@@ -26070,9 +26234,9 @@ async function handlePagesPublic(req, res, pathSegments, queryParams) {
|
|
|
26070
26234
|
res.end(JSON.stringify({ error: "Page not found" }));
|
|
26071
26235
|
return true;
|
|
26072
26236
|
}
|
|
26073
|
-
const cookiePayload = await extractAndVerifyCookie(req);
|
|
26237
|
+
const cookiePayload = await extractAndVerifyCookie(req, page.id);
|
|
26074
26238
|
const access = isAccessible(page, cookiePayload);
|
|
26075
|
-
let inlineSetCookie =
|
|
26239
|
+
let inlineSetCookie = [];
|
|
26076
26240
|
if (!access.ok) {
|
|
26077
26241
|
if (access.needsPassword) {
|
|
26078
26242
|
const candidate = extractPasswordCandidate(req, queryParams);
|
|
@@ -26084,7 +26248,7 @@ async function handlePagesPublic(req, res, pathSegments, queryParams) {
|
|
|
26084
26248
|
matched = false;
|
|
26085
26249
|
}
|
|
26086
26250
|
if (matched) {
|
|
26087
|
-
inlineSetCookie = await
|
|
26251
|
+
inlineSetCookie = await pageSessionSetCookies(req, page.id, {
|
|
26088
26252
|
dev: isLocalhostRequest(req)
|
|
26089
26253
|
});
|
|
26090
26254
|
} else {
|
|
@@ -26111,7 +26275,7 @@ async function handlePagesPublic(req, res, pathSegments, queryParams) {
|
|
|
26111
26275
|
}
|
|
26112
26276
|
if (isJsonRoute) {
|
|
26113
26277
|
res.setHeader("Cache-Control", "no-store");
|
|
26114
|
-
if (inlineSetCookie)
|
|
26278
|
+
if (inlineSetCookie.length > 0)
|
|
26115
26279
|
res.setHeader("Set-Cookie", inlineSetCookie);
|
|
26116
26280
|
publicPageJsonRoute.respond(res, 200, {
|
|
26117
26281
|
id: page.id,
|
|
@@ -26132,7 +26296,7 @@ async function handlePagesPublic(req, res, pathSegments, queryParams) {
|
|
|
26132
26296
|
"X-Content-Type-Options": "nosniff",
|
|
26133
26297
|
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; sandbox"
|
|
26134
26298
|
};
|
|
26135
|
-
if (inlineSetCookie)
|
|
26299
|
+
if (inlineSetCookie.length > 0)
|
|
26136
26300
|
headers["Set-Cookie"] = inlineSetCookie;
|
|
26137
26301
|
res.writeHead(200, headers);
|
|
26138
26302
|
res.end(page.body);
|
|
@@ -26148,15 +26312,17 @@ async function handlePagesPublic(req, res, pathSegments, queryParams) {
|
|
|
26148
26312
|
"Content-Security-Policy": buildCsp(),
|
|
26149
26313
|
"X-Content-Type-Options": "nosniff"
|
|
26150
26314
|
};
|
|
26151
|
-
if (inlineSetCookie)
|
|
26315
|
+
if (inlineSetCookie.length > 0)
|
|
26152
26316
|
headers["Set-Cookie"] = inlineSetCookie;
|
|
26153
26317
|
res.writeHead(200, headers);
|
|
26154
26318
|
res.end(printableJsonPageHtml(page));
|
|
26155
26319
|
await bumpViewCount(page.id);
|
|
26156
26320
|
return true;
|
|
26157
26321
|
}
|
|
26158
|
-
const headers = {
|
|
26159
|
-
|
|
26322
|
+
const headers = {
|
|
26323
|
+
Location: `${getAppBaseUrl2()}/pages/${page.id}`
|
|
26324
|
+
};
|
|
26325
|
+
if (inlineSetCookie.length > 0)
|
|
26160
26326
|
headers["Set-Cookie"] = inlineSetCookie;
|
|
26161
26327
|
res.writeHead(302, headers);
|
|
26162
26328
|
res.end();
|
|
@@ -26170,7 +26336,7 @@ async function handlePagesPublic(req, res, pathSegments, queryParams) {
|
|
|
26170
26336
|
"Content-Security-Policy": buildCsp(),
|
|
26171
26337
|
"X-Content-Type-Options": "nosniff"
|
|
26172
26338
|
};
|
|
26173
|
-
if (inlineSetCookie)
|
|
26339
|
+
if (inlineSetCookie.length > 0)
|
|
26174
26340
|
headers["Set-Cookie"] = inlineSetCookie;
|
|
26175
26341
|
res.writeHead(200, headers);
|
|
26176
26342
|
res.end(html);
|
|
@@ -31137,6 +31303,7 @@ var agentTaskBodySchema = object2({
|
|
|
31137
31303
|
routingNote: string2().max(200).optional(),
|
|
31138
31304
|
tags: array2(string2()).optional(),
|
|
31139
31305
|
priority: number2().int().min(0).max(100).optional(),
|
|
31306
|
+
dependsOn: array2(uuid()).optional(),
|
|
31140
31307
|
offerMode: boolean().optional(),
|
|
31141
31308
|
dir: string2().min(1).optional(),
|
|
31142
31309
|
vcsRepo: string2().min(1).optional(),
|
|
@@ -31617,6 +31784,7 @@ async function handleScriptRuns(req, res, pathSegments, queryParams, agentId) {
|
|
|
31617
31784
|
routingSource: parsed.body.routingReason ? "declared" : parsed.body.agentId ? "engine_default" : undefined,
|
|
31618
31785
|
tags: parsed.body.tags,
|
|
31619
31786
|
priority: parsed.body.priority,
|
|
31787
|
+
dependsOn: parsed.body.dependsOn,
|
|
31620
31788
|
offeredTo: parsed.body.offerMode ? parsed.body.agentId : undefined,
|
|
31621
31789
|
taskType: "script-run-step",
|
|
31622
31790
|
source: "mcp",
|
|
@@ -33101,7 +33269,10 @@ async function handleSessionData(req, res, pathSegments, queryParams, myAgentId)
|
|
|
33101
33269
|
|
|
33102
33270
|
// src/http/sessions.ts
|
|
33103
33271
|
init_zod();
|
|
33272
|
+
init_key();
|
|
33104
33273
|
init_db3();
|
|
33274
|
+
init_session_moves();
|
|
33275
|
+
init_session_tree();
|
|
33105
33276
|
init_rbac_roles();
|
|
33106
33277
|
init_steering();
|
|
33107
33278
|
init_users();
|
|
@@ -33143,7 +33314,54 @@ var SessionListItemSchema = object2({
|
|
|
33143
33314
|
root: union2([AgentTaskSchema, AgentTaskSummarySchema2]),
|
|
33144
33315
|
chainTaskCount: number2().int(),
|
|
33145
33316
|
lastActivityAt: string2(),
|
|
33146
|
-
latestStatus: AgentTaskStatusSchema
|
|
33317
|
+
latestStatus: AgentTaskStatusSchema,
|
|
33318
|
+
readState: _enum2(["unread", "read", "later", "done"]).optional(),
|
|
33319
|
+
snoozeUntil: string2().nullable().optional(),
|
|
33320
|
+
needsYou: boolean().optional(),
|
|
33321
|
+
groupKey: string2().optional(),
|
|
33322
|
+
shared: boolean().optional(),
|
|
33323
|
+
people: array2(object2({ id: string2(), name: string2() })).optional(),
|
|
33324
|
+
peopleCount: number2().int().optional(),
|
|
33325
|
+
preview: string2().nullable().optional(),
|
|
33326
|
+
shares: array2(object2({
|
|
33327
|
+
userId: string2().nullable(),
|
|
33328
|
+
name: string2().nullable(),
|
|
33329
|
+
access: _enum2(["read", "write"])
|
|
33330
|
+
})).optional()
|
|
33331
|
+
});
|
|
33332
|
+
var SessionSectionCountsSchema = object2({
|
|
33333
|
+
total: number2().int(),
|
|
33334
|
+
unread: number2().int(),
|
|
33335
|
+
running: number2().int(),
|
|
33336
|
+
needsYou: number2().int()
|
|
33337
|
+
});
|
|
33338
|
+
var SessionTreeGroupSchema = SessionSectionCountsSchema.extend({
|
|
33339
|
+
key: string2(),
|
|
33340
|
+
lastActivityAt: string2()
|
|
33341
|
+
});
|
|
33342
|
+
var SessionTreeResponseSchema = object2({
|
|
33343
|
+
groups: array2(SessionTreeGroupSchema),
|
|
33344
|
+
inbox: SessionSectionCountsSchema,
|
|
33345
|
+
later: SessionSectionCountsSchema,
|
|
33346
|
+
done: SessionSectionCountsSchema,
|
|
33347
|
+
noGroup: SessionSectionCountsSchema
|
|
33348
|
+
});
|
|
33349
|
+
var SessionApprovalSchema = object2({
|
|
33350
|
+
id: string2(),
|
|
33351
|
+
status: literal2("pending"),
|
|
33352
|
+
sourceTaskId: string2(),
|
|
33353
|
+
title: string2()
|
|
33354
|
+
});
|
|
33355
|
+
var SessionKeyFilterSchema = string2().transform((value, ctx) => {
|
|
33356
|
+
try {
|
|
33357
|
+
return normalizeAssetKey(value);
|
|
33358
|
+
} catch (error) {
|
|
33359
|
+
ctx.addIssue({
|
|
33360
|
+
code: "custom",
|
|
33361
|
+
message: error instanceof Error ? error.message : "Invalid asset key"
|
|
33362
|
+
});
|
|
33363
|
+
return NEVER;
|
|
33364
|
+
}
|
|
33147
33365
|
});
|
|
33148
33366
|
var TaskWithSteeringSchema = AgentTaskSchema.extend({
|
|
33149
33367
|
citations: array2(TaskCitationSchema),
|
|
@@ -33399,8 +33617,14 @@ var listSessions = route({
|
|
|
33399
33617
|
q: string2().optional(),
|
|
33400
33618
|
requestedByUserId: string2().min(1).optional(),
|
|
33401
33619
|
contextKeyPrefix: string2().min(1).optional(),
|
|
33620
|
+
keyPrefix: SessionKeyFilterSchema.optional(),
|
|
33621
|
+
groupKey: SessionKeyFilterSchema.optional(),
|
|
33622
|
+
noGroup: _enum2(["true", "false"]).transform((v) => v === "true").optional(),
|
|
33623
|
+
triage: _enum2(["inbox", "later", "done", "unread"]).optional(),
|
|
33624
|
+
status: _enum2(["running", "failed", "needs"]).optional(),
|
|
33625
|
+
withUserId: string2().min(1).optional(),
|
|
33402
33626
|
fields: _enum2(["full", "slim"]).optional(),
|
|
33403
|
-
scope: _enum2(["mine", "shared"]).optional()
|
|
33627
|
+
scope: _enum2(["mine", "shared", "tree"]).optional().describe("mine: owned sessions. shared: sessions shared with me. tree: Sessions that GET /api/session-tree counts: mine plus shared with me, no ownerless roots. A bypass actor sees every root with tree scope.")
|
|
33404
33628
|
}),
|
|
33405
33629
|
responses: {
|
|
33406
33630
|
200: {
|
|
@@ -33416,6 +33640,110 @@ var listSessions = route({
|
|
|
33416
33640
|
},
|
|
33417
33641
|
auth: { apiKey: true }
|
|
33418
33642
|
});
|
|
33643
|
+
var sessionTree = route({
|
|
33644
|
+
method: "get",
|
|
33645
|
+
path: "/api/session-tree",
|
|
33646
|
+
pattern: ["api", "session-tree"],
|
|
33647
|
+
summary: "Get session groups and triage counts",
|
|
33648
|
+
tags: ["Sessions"],
|
|
33649
|
+
responses: {
|
|
33650
|
+
200: { description: "Visible groups and section counts", schema: SessionTreeResponseSchema },
|
|
33651
|
+
401: { description: "Unauthorized" }
|
|
33652
|
+
},
|
|
33653
|
+
auth: { apiKey: true }
|
|
33654
|
+
});
|
|
33655
|
+
var SESSION_MOVE_LIMIT = 200;
|
|
33656
|
+
var SESSION_MOVE_OWNER_ONLY = "Only the session owner can move this group";
|
|
33657
|
+
var SESSION_MOVE_UNAVAILABLE = "Session not found or not accessible";
|
|
33658
|
+
var SessionMoveResultSchema = object2({
|
|
33659
|
+
moved: array2(string2()),
|
|
33660
|
+
skipped: array2(object2({ rootTaskId: string2(), reason: string2() }))
|
|
33661
|
+
});
|
|
33662
|
+
var sessionMoveRbac = {
|
|
33663
|
+
ungated: "per-session access, per-task task.fs.mutate, and asset-key write checks in the handler, as PATCH /api/assets/task/{id}/key"
|
|
33664
|
+
};
|
|
33665
|
+
var sessionMoveResponses = {
|
|
33666
|
+
400: { description: "Invalid namespace or request" },
|
|
33667
|
+
403: { description: "Personal destination not authorized" },
|
|
33668
|
+
409: { description: "Moves blocked until audit warnings are repaired" }
|
|
33669
|
+
};
|
|
33670
|
+
var moveSessions = route({
|
|
33671
|
+
method: "post",
|
|
33672
|
+
path: "/api/session-moves",
|
|
33673
|
+
pattern: ["api", "session-moves"],
|
|
33674
|
+
summary: "Move sessions into a group or reset their keys",
|
|
33675
|
+
tags: ["Sessions"],
|
|
33676
|
+
session: { none: "The handler checks each root and reports denied roots as skipped" },
|
|
33677
|
+
body: object2({
|
|
33678
|
+
rootTaskIds: array2(string2().min(1)).min(1).max(SESSION_MOVE_LIMIT),
|
|
33679
|
+
groupKey: AssetKeySchema.nullable()
|
|
33680
|
+
}),
|
|
33681
|
+
responses: {
|
|
33682
|
+
200: { description: "Moved and skipped sessions", schema: SessionMoveResultSchema },
|
|
33683
|
+
...sessionMoveResponses
|
|
33684
|
+
},
|
|
33685
|
+
auth: { apiKey: true },
|
|
33686
|
+
rbac: sessionMoveRbac
|
|
33687
|
+
});
|
|
33688
|
+
var moveSessionPrefix = route({
|
|
33689
|
+
method: "post",
|
|
33690
|
+
path: "/api/session-moves/prefix",
|
|
33691
|
+
pattern: ["api", "session-moves", "prefix"],
|
|
33692
|
+
summary: "Rename or delete a session group",
|
|
33693
|
+
tags: ["Sessions"],
|
|
33694
|
+
body: object2({ from: AssetKeySchema, to: AssetKeySchema.nullable() }),
|
|
33695
|
+
responses: {
|
|
33696
|
+
200: {
|
|
33697
|
+
description: "Moved and skipped sessions and remaining movable roots",
|
|
33698
|
+
schema: SessionMoveResultSchema.extend({ remaining: number2().int().min(0) })
|
|
33699
|
+
},
|
|
33700
|
+
...sessionMoveResponses
|
|
33701
|
+
},
|
|
33702
|
+
auth: { apiKey: true },
|
|
33703
|
+
rbac: sessionMoveRbac
|
|
33704
|
+
});
|
|
33705
|
+
async function withSessionMove(req, res, myAgentId, destination, run) {
|
|
33706
|
+
try {
|
|
33707
|
+
const changedBy = await resolveHttpAuditUserId(req, myAgentId);
|
|
33708
|
+
const actor = await resolveSessionActor2(req, myAgentId);
|
|
33709
|
+
const key = destination === null ? null : await authorizeAssetKeyWrite(destination, changedBy);
|
|
33710
|
+
await assertAssetKeyMovesAllowed2();
|
|
33711
|
+
await run({ actor, changedBy: changedBy ?? undefined, key });
|
|
33712
|
+
} catch (error) {
|
|
33713
|
+
if (error instanceof AssetKeyAuthorizationError) {
|
|
33714
|
+
jsonError(res, error.message, error.statusCode);
|
|
33715
|
+
} else if (error instanceof AssetKeyMovesBlockedError2) {
|
|
33716
|
+
jsonError(res, error.message, 409);
|
|
33717
|
+
} else {
|
|
33718
|
+
throw error;
|
|
33719
|
+
}
|
|
33720
|
+
}
|
|
33721
|
+
}
|
|
33722
|
+
async function checkSessionMove(rootTaskId, actor, req, myAgentId, options = {}) {
|
|
33723
|
+
const root = await getTaskById2(rootTaskId);
|
|
33724
|
+
if (!root)
|
|
33725
|
+
return { reason: SESSION_MOVE_UNAVAILABLE };
|
|
33726
|
+
if (options.ownerOnly) {
|
|
33727
|
+
if (actor.kind === "user" && (!actor.userId || root.requestedByUserId !== actor.userId)) {
|
|
33728
|
+
return { reason: SESSION_MOVE_OWNER_ONLY };
|
|
33729
|
+
}
|
|
33730
|
+
} else {
|
|
33731
|
+
const access = await sessionAccessFor2(actor, root.id);
|
|
33732
|
+
if (access === "none")
|
|
33733
|
+
return { reason: SESSION_MOVE_UNAVAILABLE };
|
|
33734
|
+
if (!hasSessionAccess(access, "write"))
|
|
33735
|
+
return { reason: "Session write access required" };
|
|
33736
|
+
}
|
|
33737
|
+
if (root.parentTaskId)
|
|
33738
|
+
return { reason: "Task is not a session root" };
|
|
33739
|
+
const tasks = await getRootTaskChain2(root.id);
|
|
33740
|
+
for (const task of tasks) {
|
|
33741
|
+
if (!await canMutateTaskNamespace(task, myAgentId, req)) {
|
|
33742
|
+
return { reason: "Not authorized to move this task namespace" };
|
|
33743
|
+
}
|
|
33744
|
+
}
|
|
33745
|
+
return { taskIds: tasks.map((task) => task.id) };
|
|
33746
|
+
}
|
|
33419
33747
|
var getSession = route({
|
|
33420
33748
|
method: "get",
|
|
33421
33749
|
path: "/api/sessions/{rootTaskId}",
|
|
@@ -33431,7 +33759,10 @@ var getSession = route({
|
|
|
33431
33759
|
root: TaskWithSteeringSchema,
|
|
33432
33760
|
chain: array2(TaskWithSteeringSchema),
|
|
33433
33761
|
access: SessionAccessSchema,
|
|
33434
|
-
owner: SessionOwnerSchema.nullable()
|
|
33762
|
+
owner: SessionOwnerSchema.nullable(),
|
|
33763
|
+
approvals: array2(SessionApprovalSchema).optional(),
|
|
33764
|
+
readState: _enum2(["unread", "read", "later", "done"]).optional(),
|
|
33765
|
+
lastActivityAt: string2().optional()
|
|
33435
33766
|
})
|
|
33436
33767
|
},
|
|
33437
33768
|
401: { description: "Unauthorized" },
|
|
@@ -33562,11 +33893,12 @@ async function handleSessions(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
33562
33893
|
return true;
|
|
33563
33894
|
const sources = parsed.query.source ? parsed.query.source.split(",").map((s) => s.trim()).filter(Boolean) : undefined;
|
|
33564
33895
|
const actor = await resolveSessionActor2(req, myAgentId);
|
|
33565
|
-
|
|
33896
|
+
const limit = Math.min(200, Math.max(0, parsed.query.limit ?? 25));
|
|
33897
|
+
if (actor.kind === "bypass" && parsed.query.scope && parsed.query.scope !== "tree") {
|
|
33566
33898
|
listSessions.respond(res, 200, {
|
|
33567
33899
|
sessions: [],
|
|
33568
33900
|
total: 0,
|
|
33569
|
-
limit
|
|
33901
|
+
limit,
|
|
33570
33902
|
offset: parsed.query.offset ?? 0
|
|
33571
33903
|
});
|
|
33572
33904
|
return true;
|
|
@@ -33576,11 +33908,24 @@ async function handleSessions(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
33576
33908
|
q: parsed.query.q,
|
|
33577
33909
|
requestedByUserId: parsed.query.requestedByUserId,
|
|
33578
33910
|
contextKeyPrefix: parsed.query.contextKeyPrefix,
|
|
33579
|
-
|
|
33911
|
+
keyPrefix: parsed.query.keyPrefix,
|
|
33912
|
+
groupKey: parsed.query.groupKey,
|
|
33913
|
+
noGroup: parsed.query.noGroup,
|
|
33914
|
+
triage: parsed.query.triage,
|
|
33915
|
+
status: parsed.query.status,
|
|
33916
|
+
withUserId: parsed.query.withUserId,
|
|
33917
|
+
now: new Date().toISOString(),
|
|
33918
|
+
tree: parsed.query.scope === "tree",
|
|
33919
|
+
...actor.kind === "user" ? {
|
|
33920
|
+
viewer: {
|
|
33921
|
+
userId: actor.userId,
|
|
33922
|
+
scope: parsed.query.scope === "tree" ? "all" : parsed.query.scope ?? "all"
|
|
33923
|
+
}
|
|
33924
|
+
} : {}
|
|
33580
33925
|
};
|
|
33581
33926
|
const baseOpts = {
|
|
33582
33927
|
...filterOpts,
|
|
33583
|
-
limit
|
|
33928
|
+
limit,
|
|
33584
33929
|
offset: parsed.query.offset
|
|
33585
33930
|
};
|
|
33586
33931
|
const sessions = parsed.query.fields === "full" ? await listRecentSessions2(baseOpts) : await listRecentSessions2({ ...baseOpts, slim: true });
|
|
@@ -33588,11 +33933,104 @@ async function handleSessions(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
33588
33933
|
listSessions.respond(res, 200, {
|
|
33589
33934
|
sessions,
|
|
33590
33935
|
total,
|
|
33591
|
-
limit
|
|
33936
|
+
limit,
|
|
33592
33937
|
offset: parsed.query.offset ?? 0
|
|
33593
33938
|
});
|
|
33594
33939
|
return true;
|
|
33595
33940
|
}
|
|
33941
|
+
if (sessionTree.match(req.method, pathSegments)) {
|
|
33942
|
+
const parsed = await sessionTree.parse(req, res, pathSegments, queryParams);
|
|
33943
|
+
if (!parsed)
|
|
33944
|
+
return true;
|
|
33945
|
+
sessionTree.respond(res, 200, await getSessionTree(await resolveSessionActor2(req, myAgentId)));
|
|
33946
|
+
return true;
|
|
33947
|
+
}
|
|
33948
|
+
if (moveSessions.match(req.method, pathSegments)) {
|
|
33949
|
+
const parsed = await moveSessions.parse(req, res, pathSegments, queryParams);
|
|
33950
|
+
if (!parsed)
|
|
33951
|
+
return true;
|
|
33952
|
+
await withSessionMove(req, res, myAgentId, parsed.body.groupKey, async ({ actor, changedBy, key }) => {
|
|
33953
|
+
const skipped = [];
|
|
33954
|
+
const moves = [];
|
|
33955
|
+
for (const rootId of new Set(parsed.body.rootTaskIds)) {
|
|
33956
|
+
const checked = await checkSessionMove(rootId, actor, req, myAgentId);
|
|
33957
|
+
if ("reason" in checked) {
|
|
33958
|
+
skipped.push({ rootTaskId: rootId, reason: checked.reason });
|
|
33959
|
+
} else {
|
|
33960
|
+
moves.push({
|
|
33961
|
+
rootId,
|
|
33962
|
+
taskIds: checked.taskIds,
|
|
33963
|
+
key: key ?? defaultAssetKey("task", rootId)
|
|
33964
|
+
});
|
|
33965
|
+
}
|
|
33966
|
+
}
|
|
33967
|
+
const moved = await moveSessionChains({ moves, changedBy });
|
|
33968
|
+
moveSessions.respond(res, 200, { moved, skipped });
|
|
33969
|
+
});
|
|
33970
|
+
return true;
|
|
33971
|
+
}
|
|
33972
|
+
if (moveSessionPrefix.match(req.method, pathSegments)) {
|
|
33973
|
+
const parsed = await moveSessionPrefix.parse(req, res, pathSegments, queryParams);
|
|
33974
|
+
if (!parsed)
|
|
33975
|
+
return true;
|
|
33976
|
+
const from = normalizeAssetKey(parsed.body.from);
|
|
33977
|
+
if (parseAssetKey(from).relativeSegments.length === 0) {
|
|
33978
|
+
jsonError(res, "Cannot move a namespace root", 400);
|
|
33979
|
+
return true;
|
|
33980
|
+
}
|
|
33981
|
+
await withSessionMove(req, res, myAgentId, parsed.body.to, async ({ actor, changedBy, key }) => {
|
|
33982
|
+
if (key?.startsWith(from)) {
|
|
33983
|
+
jsonError(res, "Destination must be outside the source prefix", 400);
|
|
33984
|
+
return;
|
|
33985
|
+
}
|
|
33986
|
+
const skipped = actor.kind === "user" ? (await listSharedPrefixRoots({ from, userId: actor.userId })).map((root) => ({
|
|
33987
|
+
rootTaskId: root.id,
|
|
33988
|
+
reason: SESSION_MOVE_OWNER_ONLY
|
|
33989
|
+
})) : [];
|
|
33990
|
+
const moves = [];
|
|
33991
|
+
let remaining = 0;
|
|
33992
|
+
for (let offset = 0;; offset += SESSION_MOVE_LIMIT) {
|
|
33993
|
+
const roots = await listPrefixRoots({
|
|
33994
|
+
from,
|
|
33995
|
+
userId: actor.kind === "user" ? actor.userId : undefined,
|
|
33996
|
+
limit: SESSION_MOVE_LIMIT,
|
|
33997
|
+
offset
|
|
33998
|
+
});
|
|
33999
|
+
for (const root of roots) {
|
|
34000
|
+
const nextKey = key === null ? defaultAssetKey("task", root.id) : key + root.key.slice(from.length);
|
|
34001
|
+
if (nextKey.startsWith(from)) {
|
|
34002
|
+
skipped.push({
|
|
34003
|
+
rootTaskId: root.id,
|
|
34004
|
+
reason: "The new key stays inside the source group"
|
|
34005
|
+
});
|
|
34006
|
+
continue;
|
|
34007
|
+
}
|
|
34008
|
+
if (nextKey.length > ASSET_KEY_MAX_LENGTH) {
|
|
34009
|
+
skipped.push({
|
|
34010
|
+
rootTaskId: root.id,
|
|
34011
|
+
reason: `Asset key cannot exceed ${ASSET_KEY_MAX_LENGTH} characters`
|
|
34012
|
+
});
|
|
34013
|
+
continue;
|
|
34014
|
+
}
|
|
34015
|
+
const checked = await checkSessionMove(root.id, actor, req, myAgentId, {
|
|
34016
|
+
ownerOnly: true
|
|
34017
|
+
});
|
|
34018
|
+
if ("reason" in checked) {
|
|
34019
|
+
skipped.push({ rootTaskId: root.id, reason: checked.reason });
|
|
34020
|
+
} else if (moves.length < SESSION_MOVE_LIMIT) {
|
|
34021
|
+
moves.push({ rootId: root.id, taskIds: checked.taskIds, key: nextKey });
|
|
34022
|
+
} else {
|
|
34023
|
+
remaining++;
|
|
34024
|
+
}
|
|
34025
|
+
}
|
|
34026
|
+
if (roots.length < SESSION_MOVE_LIMIT)
|
|
34027
|
+
break;
|
|
34028
|
+
}
|
|
34029
|
+
const moved = await moveSessionChains({ moves, changedBy });
|
|
34030
|
+
moveSessionPrefix.respond(res, 200, { moved, skipped, remaining });
|
|
34031
|
+
});
|
|
34032
|
+
return true;
|
|
34033
|
+
}
|
|
33596
34034
|
if (getSession.match(req.method, pathSegments)) {
|
|
33597
34035
|
const parsed = await getSession.parse(req, res, pathSegments, queryParams);
|
|
33598
34036
|
if (!parsed)
|
|
@@ -33606,7 +34044,8 @@ async function handleSessions(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
33606
34044
|
id: root.id,
|
|
33607
34045
|
requestedByUserId: root.requestedByUserId ?? null
|
|
33608
34046
|
};
|
|
33609
|
-
const
|
|
34047
|
+
const actor = await resolveSessionActor2(req, myAgentId);
|
|
34048
|
+
const access = await accessForRoot(actor, sessionRoot);
|
|
33610
34049
|
if (access === "none") {
|
|
33611
34050
|
jsonError(res, "You don't have access to this session. Ask the owner to share it.", 403);
|
|
33612
34051
|
return true;
|
|
@@ -33618,9 +34057,11 @@ async function handleSessions(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
33618
34057
|
} : null;
|
|
33619
34058
|
const chain = await getRootTaskChain2(parsed.params.rootTaskId);
|
|
33620
34059
|
const tasks = [root, ...chain];
|
|
33621
|
-
const [steering, citations] = await Promise.all([
|
|
34060
|
+
const [steering, citations, approvals, read] = await Promise.all([
|
|
33622
34061
|
getTaskSteeringFieldsForTasks(tasks),
|
|
33623
|
-
getTaskCitationsForTasks(tasks.map((task) => task.id))
|
|
34062
|
+
getTaskCitationsForTasks(tasks.map((task) => task.id)),
|
|
34063
|
+
getPendingApprovalsForTasks2(tasks.map((task) => task.id)),
|
|
34064
|
+
actor.kind === "user" && actor.userId ? getSessionReadState(sessionRoot.id, actor.userId) : Promise.resolve(null)
|
|
33624
34065
|
]);
|
|
33625
34066
|
const decorate = (task) => ({
|
|
33626
34067
|
...task,
|
|
@@ -33631,7 +34072,9 @@ async function handleSessions(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
33631
34072
|
root: decorate(root),
|
|
33632
34073
|
chain: chain.map(decorate),
|
|
33633
34074
|
access,
|
|
33634
|
-
owner
|
|
34075
|
+
owner,
|
|
34076
|
+
...approvals.length ? { approvals } : {},
|
|
34077
|
+
...read ?? {}
|
|
33635
34078
|
});
|
|
33636
34079
|
return true;
|
|
33637
34080
|
}
|
|
@@ -33720,8 +34163,7 @@ async function syncSkillsToFilesystem(agentId, harnessType = "all", homeOverride
|
|
|
33720
34163
|
})));
|
|
33721
34164
|
return writeSkillsToFilesystem(entries, harnessType, home);
|
|
33722
34165
|
}
|
|
33723
|
-
|
|
33724
|
-
const skills = await getAgentSkills2(agentId);
|
|
34166
|
+
function computeSkillsSignature(skills) {
|
|
33725
34167
|
const sorted = [...skills].sort((a, b) => a.id.localeCompare(b.id));
|
|
33726
34168
|
const canonical = JSON.stringify(sorted.map((s) => [
|
|
33727
34169
|
s.id,
|
|
@@ -33736,6 +34178,18 @@ async function computeAgentSkillsSignature(agentId) {
|
|
|
33736
34178
|
const hash = new Bun.CryptoHasher("sha256").update(canonical).digest("hex");
|
|
33737
34179
|
return { hash, count: sorted.length };
|
|
33738
34180
|
}
|
|
34181
|
+
async function computeAgentSkillsSignature(agentId) {
|
|
34182
|
+
return computeSkillsSignature(await getAgentSkills2(agentId));
|
|
34183
|
+
}
|
|
34184
|
+
async function buildSkillsBundle(readSkills, readFiles) {
|
|
34185
|
+
const skills = await readSkills();
|
|
34186
|
+
const signature = computeSkillsSignature(skills).hash;
|
|
34187
|
+
const entries = await Promise.all(skills.map(async (skill) => ({
|
|
34188
|
+
...skill,
|
|
34189
|
+
files: skill.isComplex ? await readFiles(skill.id) : []
|
|
34190
|
+
})));
|
|
34191
|
+
return { skills: entries, total: entries.length, signature };
|
|
34192
|
+
}
|
|
33739
34193
|
|
|
33740
34194
|
// src/be/skill-usage.ts
|
|
33741
34195
|
init_db3();
|
|
@@ -34021,6 +34475,13 @@ var agentSkillsListResponseSchema = object2({
|
|
|
34021
34475
|
total: number2(),
|
|
34022
34476
|
signature: string2()
|
|
34023
34477
|
});
|
|
34478
|
+
var agentSkillsBundleResponseSchema = object2({
|
|
34479
|
+
skills: array2(SkillWithInstallInfoSchema.extend({
|
|
34480
|
+
files: array2(SkillFileSchema)
|
|
34481
|
+
})),
|
|
34482
|
+
total: number2(),
|
|
34483
|
+
signature: string2()
|
|
34484
|
+
});
|
|
34024
34485
|
var agentSkillsSignatureResponseSchema = object2({
|
|
34025
34486
|
hash: string2(),
|
|
34026
34487
|
count: number2(),
|
|
@@ -34415,7 +34876,28 @@ var getAgentSkillsSignatureRoute = route({
|
|
|
34415
34876
|
200: { description: "Skills signature", schema: agentSkillsSignatureResponseSchema }
|
|
34416
34877
|
}
|
|
34417
34878
|
});
|
|
34879
|
+
var getAgentSkillsBundleRoute = route({
|
|
34880
|
+
method: "get",
|
|
34881
|
+
path: "/api/agents/{id}/skills/bundle",
|
|
34882
|
+
pattern: ["api", "agents", null, "skills", "bundle"],
|
|
34883
|
+
summary: "Get an agent's installed skills together with their bundled files",
|
|
34884
|
+
description: "Single-request boot payload: every installed-and-enabled skill with the full content of its bundled files, plus the same signature the cheap probe returns. Lets a worker sync its local skill tree without an N+1 file fan-out.",
|
|
34885
|
+
tags: ["Skills"],
|
|
34886
|
+
auth: { apiKey: true },
|
|
34887
|
+
params: object2({ id: string2() }),
|
|
34888
|
+
responses: {
|
|
34889
|
+
200: { description: "Agent skills bundle", schema: agentSkillsBundleResponseSchema }
|
|
34890
|
+
}
|
|
34891
|
+
});
|
|
34418
34892
|
async function handleSkills(req, res, pathSegments, queryParams, myAgentId) {
|
|
34893
|
+
if (getAgentSkillsBundleRoute.match(req.method, pathSegments)) {
|
|
34894
|
+
const parsed = await getAgentSkillsBundleRoute.parse(req, res, pathSegments, queryParams);
|
|
34895
|
+
if (!parsed)
|
|
34896
|
+
return true;
|
|
34897
|
+
const bundle = await buildSkillsBundle(() => getAgentSkills2(parsed.params.id), (skillId) => getSkillFiles2(skillId));
|
|
34898
|
+
getAgentSkillsBundleRoute.respond(res, 200, bundle);
|
|
34899
|
+
return true;
|
|
34900
|
+
}
|
|
34419
34901
|
if (getAgentSkillsSignatureRoute.match(req.method, pathSegments)) {
|
|
34420
34902
|
const parsed = await getAgentSkillsSignatureRoute.parse(req, res, pathSegments, queryParams);
|
|
34421
34903
|
if (!parsed)
|
|
@@ -36569,7 +37051,18 @@ var deleteIdentityRoute = route({
|
|
|
36569
37051
|
},
|
|
36570
37052
|
auth: { apiKey: true }
|
|
36571
37053
|
});
|
|
36572
|
-
var UNMAPPED_KINDS = [
|
|
37054
|
+
var UNMAPPED_KINDS = [
|
|
37055
|
+
"slack",
|
|
37056
|
+
"github",
|
|
37057
|
+
"gitlab",
|
|
37058
|
+
"azure-devops",
|
|
37059
|
+
"bitbucket",
|
|
37060
|
+
"linear",
|
|
37061
|
+
"kapso",
|
|
37062
|
+
"telegram",
|
|
37063
|
+
"discord",
|
|
37064
|
+
"teams"
|
|
37065
|
+
];
|
|
36573
37066
|
async function collectUnmappedForKind(kind, limit) {
|
|
36574
37067
|
const namespace = `integration:unmapped:${kind}`;
|
|
36575
37068
|
const rows = await listKv2(namespace, { limit: Math.min(limit * 2, 1000), offset: 0 });
|
|
@@ -37346,6 +37839,24 @@ var azureDevOpsWebhook = route({
|
|
|
37346
37839
|
503: { description: "Azure DevOps integration not configured" }
|
|
37347
37840
|
}
|
|
37348
37841
|
});
|
|
37842
|
+
var bitbucketWebhook = route({
|
|
37843
|
+
method: "post",
|
|
37844
|
+
path: "/api/bitbucket/webhook",
|
|
37845
|
+
pattern: ["api", "bitbucket", "webhook"],
|
|
37846
|
+
summary: "Handle Bitbucket Cloud webhook events",
|
|
37847
|
+
tags: ["Webhooks"],
|
|
37848
|
+
auth: { apiKey: false },
|
|
37849
|
+
rbac: {
|
|
37850
|
+
ungated: "provider webhook: authenticated by the per-webhook HMAC secret, not a swarm principal"
|
|
37851
|
+
},
|
|
37852
|
+
responses: {
|
|
37853
|
+
200: { description: "Event processed", schema: WebhookDispatchResultSchema },
|
|
37854
|
+
401: { description: "Invalid signature" },
|
|
37855
|
+
413: { description: "Body exceeds 1 MiB" },
|
|
37856
|
+
503: { description: "Bitbucket integration not configured" }
|
|
37857
|
+
}
|
|
37858
|
+
});
|
|
37859
|
+
var BITBUCKET_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
|
|
37349
37860
|
var agentmailWebhook = route({
|
|
37350
37861
|
method: "post",
|
|
37351
37862
|
path: "/api/agentmail/webhook",
|
|
@@ -37802,6 +38313,101 @@ async function handleWebhooks(req, res, pathSegments) {
|
|
|
37802
38313
|
}
|
|
37803
38314
|
return true;
|
|
37804
38315
|
}
|
|
38316
|
+
if (bitbucketWebhook.match(req.method, pathSegments)) {
|
|
38317
|
+
if (!isBitbucketEnabled()) {
|
|
38318
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
38319
|
+
res.end(JSON.stringify({ error: "Bitbucket integration not configured" }));
|
|
38320
|
+
return true;
|
|
38321
|
+
}
|
|
38322
|
+
if (enforceContentLengthCap(req, res, BITBUCKET_WEBHOOK_MAX_BODY_BYTES) === BODY_TOO_LARGE) {
|
|
38323
|
+
return true;
|
|
38324
|
+
}
|
|
38325
|
+
const chunks = [];
|
|
38326
|
+
let receivedBytes = 0;
|
|
38327
|
+
let tooLarge = false;
|
|
38328
|
+
for await (const chunk of req) {
|
|
38329
|
+
if (tooLarge)
|
|
38330
|
+
continue;
|
|
38331
|
+
receivedBytes += chunk.byteLength;
|
|
38332
|
+
if (receivedBytes > BITBUCKET_WEBHOOK_MAX_BODY_BYTES) {
|
|
38333
|
+
tooLarge = true;
|
|
38334
|
+
chunks.length = 0;
|
|
38335
|
+
jsonError(res, `Payload too large (max ${BITBUCKET_WEBHOOK_MAX_BODY_BYTES} bytes)`, 413);
|
|
38336
|
+
continue;
|
|
38337
|
+
}
|
|
38338
|
+
chunks.push(chunk);
|
|
38339
|
+
}
|
|
38340
|
+
if (tooLarge)
|
|
38341
|
+
return true;
|
|
38342
|
+
const rawBody = Buffer.concat(chunks).toString();
|
|
38343
|
+
const signature = req.headers["x-hub-signature"];
|
|
38344
|
+
if (!verifyBitbucketWebhook(rawBody, signature)) {
|
|
38345
|
+
console.log("[Bitbucket] Invalid webhook signature");
|
|
38346
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
38347
|
+
res.end(JSON.stringify({ error: "Invalid signature" }));
|
|
38348
|
+
return true;
|
|
38349
|
+
}
|
|
38350
|
+
let body;
|
|
38351
|
+
try {
|
|
38352
|
+
body = JSON.parse(rawBody);
|
|
38353
|
+
} catch {
|
|
38354
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
38355
|
+
res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
38356
|
+
return true;
|
|
38357
|
+
}
|
|
38358
|
+
const eventKey = req.headers["x-event-key"];
|
|
38359
|
+
const requestUuid = req.headers["x-request-uuid"];
|
|
38360
|
+
if (isDuplicateDelivery(requestUuid)) {
|
|
38361
|
+
console.log(`[Bitbucket] Skipping duplicate delivery ${requestUuid}`);
|
|
38362
|
+
bitbucketWebhook.respond(res, 200, { created: false });
|
|
38363
|
+
return true;
|
|
38364
|
+
}
|
|
38365
|
+
console.log(`[Bitbucket] Received ${eventKey} event`);
|
|
38366
|
+
try {
|
|
38367
|
+
const result = await runBlockableHandler("Bitbucket", async () => {
|
|
38368
|
+
switch (eventKey) {
|
|
38369
|
+
case "pullrequest:created":
|
|
38370
|
+
return await handlePullRequestCreated2(body);
|
|
38371
|
+
case "pullrequest:comment_created":
|
|
38372
|
+
return await handlePullRequestCommentCreated(body);
|
|
38373
|
+
case "pullrequest:fulfilled":
|
|
38374
|
+
return await handlePullRequestClosed(body, "merged");
|
|
38375
|
+
case "pullrequest:rejected":
|
|
38376
|
+
return await handlePullRequestClosed(body, "declined");
|
|
38377
|
+
default:
|
|
38378
|
+
console.log(`[Bitbucket] Ignoring unsupported event type: ${eventKey}`);
|
|
38379
|
+
return { created: false };
|
|
38380
|
+
}
|
|
38381
|
+
});
|
|
38382
|
+
const workflowEvent = {
|
|
38383
|
+
"pullrequest:created": "created",
|
|
38384
|
+
"pullrequest:comment_created": "commented",
|
|
38385
|
+
"pullrequest:fulfilled": "merged",
|
|
38386
|
+
"pullrequest:rejected": "declined"
|
|
38387
|
+
}[eventKey ?? ""];
|
|
38388
|
+
const prEvent = workflowEvent ? pullRequestEventOf(body) : null;
|
|
38389
|
+
if (workflowEvent && prEvent) {
|
|
38390
|
+
const pr = prEvent.pullrequest;
|
|
38391
|
+
const comment = workflowEvent === "commented" ? pullRequestCommentEventOf(body)?.comment : undefined;
|
|
38392
|
+
const user = comment?.user ?? prEvent.actor;
|
|
38393
|
+
workflowEventBus2.emit(`bitbucket.pull_request.${workflowEvent}`, {
|
|
38394
|
+
repo: prEvent.repository.full_name,
|
|
38395
|
+
number: pr.id,
|
|
38396
|
+
...workflowEvent === "commented" ? { body: comment?.content.raw } : { title: pr.title, body: pr.description ?? undefined },
|
|
38397
|
+
action: workflowEvent,
|
|
38398
|
+
user_login: user.account_id ?? user.nickname ?? user.display_name
|
|
38399
|
+
});
|
|
38400
|
+
}
|
|
38401
|
+
markDeliveryProcessed(requestUuid);
|
|
38402
|
+
bitbucketWebhook.respond(res, 200, result);
|
|
38403
|
+
} catch (err) {
|
|
38404
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
38405
|
+
console.error(`[Bitbucket] Error handling ${eventKey} event: ${scrubSecrets(errorMessage)}`);
|
|
38406
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
38407
|
+
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
38408
|
+
}
|
|
38409
|
+
return true;
|
|
38410
|
+
}
|
|
37805
38411
|
if (agentmailWebhook.match(req.method, pathSegments)) {
|
|
37806
38412
|
if (!isAgentMailEnabled()) {
|
|
37807
38413
|
res.writeHead(503, { "Content-Type": "application/json" });
|
|
@@ -39264,7 +39870,7 @@ var httpServer = createHttpServer(async (req, res) => {
|
|
|
39264
39870
|
});
|
|
39265
39871
|
});
|
|
39266
39872
|
var detachRealtime = attachRealtimeTransport(httpServer);
|
|
39267
|
-
var { attachControllerTransport } = await import("./controllers-connect-
|
|
39873
|
+
var { attachControllerTransport } = await import("./controllers-connect-tw1dan9r.js");
|
|
39268
39874
|
var detachControllers = attachControllerTransport(httpServer);
|
|
39269
39875
|
var stopWorkerNudges = startWorkerNudgeListener();
|
|
39270
39876
|
var stopRoomDeletion = realtimeBus.subscribe("room:namespace-deleted", (namespace) => {
|
|
@@ -39293,7 +39899,7 @@ async function shutdown() {
|
|
|
39293
39899
|
sessionsProcessed: getServerSessionsProcessed()
|
|
39294
39900
|
});
|
|
39295
39901
|
if (hasCapability("scheduling")) {
|
|
39296
|
-
const { stopScheduler } = await import("./index-
|
|
39902
|
+
const { stopScheduler } = await import("./index-07ef7nkm.js");
|
|
39297
39903
|
stopScheduler();
|
|
39298
39904
|
}
|
|
39299
39905
|
stopHeartbeat2();
|
|
@@ -39305,7 +39911,7 @@ async function shutdown() {
|
|
|
39305
39911
|
await stopSlackApp2();
|
|
39306
39912
|
await stopChatSdk();
|
|
39307
39913
|
if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
|
|
39308
|
-
const { stopOAuthKeepalive } = await import("./keepalive-
|
|
39914
|
+
const { stopOAuthKeepalive } = await import("./keepalive-5nh2rhmr.js");
|
|
39309
39915
|
await stopOAuthKeepalive();
|
|
39310
39916
|
}
|
|
39311
39917
|
stopOAuthPendingGc();
|
|
@@ -39385,21 +39991,21 @@ try {
|
|
|
39385
39991
|
console.warn("[startup] CAPABILITIES upgrade seed failed (non-fatal):", err);
|
|
39386
39992
|
}
|
|
39387
39993
|
try {
|
|
39388
|
-
const { ensurePricingSeeded } = await import("./boot-seeds-
|
|
39994
|
+
const { ensurePricingSeeded } = await import("./boot-seeds-3vn0m9sg.js");
|
|
39389
39995
|
ensurePricingSeeded();
|
|
39390
|
-
const { startPricingRefreshLoop } = await import("./pricing-refresh-
|
|
39996
|
+
const { startPricingRefreshLoop } = await import("./pricing-refresh-nevhat8h.js");
|
|
39391
39997
|
startPricingRefreshLoop();
|
|
39392
39998
|
} catch (err) {
|
|
39393
39999
|
console.error("[startup] Failed to seed pricing rows:", err);
|
|
39394
40000
|
}
|
|
39395
40001
|
try {
|
|
39396
|
-
const { ensureModelProfilesSeeded } = await import("./boot-seeds-
|
|
40002
|
+
const { ensureModelProfilesSeeded } = await import("./boot-seeds-3vn0m9sg.js");
|
|
39397
40003
|
await ensureModelProfilesSeeded();
|
|
39398
40004
|
} catch (err) {
|
|
39399
40005
|
console.error("[startup] Failed to seed model profiles:", err);
|
|
39400
40006
|
}
|
|
39401
40007
|
try {
|
|
39402
|
-
const { ensureRbacSeeded } = await import("./boot-seeds-
|
|
40008
|
+
const { ensureRbacSeeded } = await import("./boot-seeds-3vn0m9sg.js");
|
|
39403
40009
|
ensureRbacSeeded();
|
|
39404
40010
|
} catch (err) {
|
|
39405
40011
|
console.error("[startup] Failed to sync RBAC seed rows:", err);
|
|
@@ -39407,7 +40013,7 @@ try {
|
|
|
39407
40013
|
throw err;
|
|
39408
40014
|
}
|
|
39409
40015
|
try {
|
|
39410
|
-
const { runAllSeeders } = await import("./index-
|
|
40016
|
+
const { runAllSeeders } = await import("./index-zdycv3ja.js");
|
|
39411
40017
|
await runAllSeeders({ scriptEmbeddingMode: "skip" });
|
|
39412
40018
|
} catch (err) {
|
|
39413
40019
|
console.error("[startup] Failed to seed built-in entities:", err);
|
|
@@ -39463,45 +40069,46 @@ httpServer.listen(port, async () => {
|
|
|
39463
40069
|
initGitLab();
|
|
39464
40070
|
initAzureDevOps();
|
|
39465
40071
|
initAzureDevOpsOutbound();
|
|
40072
|
+
initBitbucket();
|
|
39466
40073
|
initAgentMail();
|
|
39467
40074
|
await initLinear();
|
|
39468
40075
|
await initJira();
|
|
39469
40076
|
await initWorkflows2();
|
|
39470
40077
|
await startScriptRunSupervisor(getMcpBaseUrl());
|
|
39471
40078
|
if (hasCapability("scheduling")) {
|
|
39472
|
-
const { startScheduler } = await import("./index-
|
|
39473
|
-
const { getExecutorRegistry } = await import("./index-
|
|
40079
|
+
const { startScheduler } = await import("./index-07ef7nkm.js");
|
|
40080
|
+
const { getExecutorRegistry } = await import("./index-q3t40ryv.js");
|
|
39474
40081
|
const intervalMs = Number(process.env.SCHEDULER_INTERVAL_MS) || 1e4;
|
|
39475
40082
|
startScheduler(getExecutorRegistry(), intervalMs, {
|
|
39476
40083
|
runId: globalState.__runId
|
|
39477
40084
|
});
|
|
39478
40085
|
}
|
|
39479
40086
|
if (!isEnvFlagEnabled("HEARTBEAT_DISABLE", false)) {
|
|
39480
|
-
const { startHeartbeat } = await import("./index-
|
|
40087
|
+
const { startHeartbeat } = await import("./index-7yts8n9s.js");
|
|
39481
40088
|
const heartbeatMs = Number(process.env.HEARTBEAT_INTERVAL_MS) || 90000;
|
|
39482
40089
|
startHeartbeat(heartbeatMs);
|
|
39483
40090
|
}
|
|
39484
40091
|
startPoolRouting();
|
|
39485
40092
|
startRuntimeSupervisor();
|
|
39486
40093
|
if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
|
|
39487
|
-
const { startOAuthKeepalive } = await import("./keepalive-
|
|
40094
|
+
const { startOAuthKeepalive } = await import("./keepalive-5nh2rhmr.js");
|
|
39488
40095
|
startOAuthKeepalive();
|
|
39489
40096
|
}
|
|
39490
40097
|
if (process.env.OAUTH_REFRESH_SWEEP_DISABLE !== "true") {
|
|
39491
|
-
const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-
|
|
40098
|
+
const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-y3n1knmd.js");
|
|
39492
40099
|
startOAuthRefreshSweep();
|
|
39493
40100
|
}
|
|
39494
40101
|
startOAuthPendingGc();
|
|
39495
40102
|
await startMemoryGc();
|
|
39496
40103
|
await startDbRetention();
|
|
39497
40104
|
startHumanFreeDrain();
|
|
39498
|
-
import("./boot-reembed-
|
|
40105
|
+
import("./boot-reembed-n4wq5dd2.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
|
|
39499
40106
|
console.error("[boot-reembed] startup backfill failed (non-fatal):", err);
|
|
39500
40107
|
});
|
|
39501
|
-
import("./boot-reembed-
|
|
40108
|
+
import("./boot-reembed-4xwq5tg0.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
|
|
39502
40109
|
console.error("[boot-reembed-scripts] startup backfill failed (non-fatal):", err);
|
|
39503
40110
|
});
|
|
39504
|
-
import("./boot-scrub-sweep-
|
|
40111
|
+
import("./boot-scrub-sweep-69h90xcq.js").then(({ runBootScrubSweep }) => runBootScrubSweep()).catch((err) => {
|
|
39505
40112
|
console.error("[boot-scrub-sweep] startup scrub failed (non-fatal):", err);
|
|
39506
40113
|
});
|
|
39507
40114
|
}).on("error", (err) => {
|