@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
package/src/http/sessions.ts
CHANGED
|
@@ -1,17 +1,36 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import {
|
|
4
|
+
ASSET_KEY_MAX_LENGTH,
|
|
5
|
+
defaultAssetKey,
|
|
6
|
+
normalizeAssetKey,
|
|
7
|
+
parseAssetKey,
|
|
8
|
+
} from "../assets/key";
|
|
9
|
+
import { AssetKeyAuthorizationError, authorizeAssetKeyWrite } from "../be/asset-key-auth";
|
|
10
|
+
import { resolveHttpAuditUserId } from "../be/audit-user";
|
|
11
|
+
import {
|
|
12
|
+
AssetKeyMovesBlockedError,
|
|
13
|
+
assertAssetKeyMovesAllowed,
|
|
4
14
|
countSessions,
|
|
5
15
|
createAgent,
|
|
6
16
|
getAgentById,
|
|
7
17
|
getDbClient,
|
|
18
|
+
getPendingApprovalsForTasks,
|
|
8
19
|
getRootTaskChain,
|
|
9
20
|
getTaskById,
|
|
10
21
|
listRecentSessions,
|
|
11
22
|
} from "../be/db";
|
|
23
|
+
import {
|
|
24
|
+
listPrefixRoots,
|
|
25
|
+
listSharedPrefixRoots,
|
|
26
|
+
moveSessionChains,
|
|
27
|
+
type SessionKeyMove,
|
|
28
|
+
} from "../be/db/session-moves";
|
|
29
|
+
import { getSessionReadState, getSessionTree } from "../be/db/session-tree";
|
|
12
30
|
import { getUserGrant } from "../be/rbac-roles";
|
|
13
31
|
import {
|
|
14
32
|
getSessionRoot,
|
|
33
|
+
hasSessionAccess,
|
|
15
34
|
listActiveSessionShares,
|
|
16
35
|
type SessionAccess,
|
|
17
36
|
sessionAccessForRoot,
|
|
@@ -38,6 +57,7 @@ import {
|
|
|
38
57
|
type AgentTask,
|
|
39
58
|
AgentTaskSchema,
|
|
40
59
|
AgentTaskStatusSchema,
|
|
60
|
+
AssetKeySchema,
|
|
41
61
|
SessionAccessSchema,
|
|
42
62
|
SessionOwnerSchema,
|
|
43
63
|
SessionSharesSchema,
|
|
@@ -46,9 +66,10 @@ import {
|
|
|
46
66
|
} from "../types";
|
|
47
67
|
import { getRequestAuth } from "../utils/request-auth-context";
|
|
48
68
|
import { isRuntimePoolsEnabled } from "../utils/runtime-pools-flag";
|
|
69
|
+
import { canMutateTaskNamespace } from "./assets";
|
|
49
70
|
import { agentFirstPrincipal } from "./request-principal";
|
|
50
71
|
import { route } from "./route-def";
|
|
51
|
-
import { resolveSessionActor, type SessionActor } from "./session-access";
|
|
72
|
+
import { resolveSessionActor, type SessionActor, sessionAccessFor } from "./session-access";
|
|
52
73
|
import { jsonError } from "./utils";
|
|
53
74
|
|
|
54
75
|
// ─── Response Schemas ────────────────────────────────────────────────────────
|
|
@@ -100,6 +121,61 @@ const SessionListItemSchema = z.object({
|
|
|
100
121
|
chainTaskCount: z.number().int(),
|
|
101
122
|
lastActivityAt: z.string(),
|
|
102
123
|
latestStatus: AgentTaskStatusSchema,
|
|
124
|
+
readState: z.enum(["unread", "read", "later", "done"]).optional(),
|
|
125
|
+
snoozeUntil: z.string().nullable().optional(),
|
|
126
|
+
needsYou: z.boolean().optional(),
|
|
127
|
+
groupKey: z.string().optional(),
|
|
128
|
+
shared: z.boolean().optional(),
|
|
129
|
+
people: z.array(z.object({ id: z.string(), name: z.string() })).optional(),
|
|
130
|
+
peopleCount: z.number().int().optional(),
|
|
131
|
+
preview: z.string().nullable().optional(),
|
|
132
|
+
/** Active shares with their access level. `userId` null means everyone on the swarm. */
|
|
133
|
+
shares: z
|
|
134
|
+
.array(
|
|
135
|
+
z.object({
|
|
136
|
+
userId: z.string().nullable(),
|
|
137
|
+
name: z.string().nullable(),
|
|
138
|
+
access: z.enum(["read", "write"]),
|
|
139
|
+
}),
|
|
140
|
+
)
|
|
141
|
+
.optional(),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const SessionSectionCountsSchema = z.object({
|
|
145
|
+
total: z.number().int(),
|
|
146
|
+
unread: z.number().int(),
|
|
147
|
+
running: z.number().int(),
|
|
148
|
+
needsYou: z.number().int(),
|
|
149
|
+
});
|
|
150
|
+
const SessionTreeGroupSchema = SessionSectionCountsSchema.extend({
|
|
151
|
+
key: z.string(),
|
|
152
|
+
lastActivityAt: z.string(),
|
|
153
|
+
});
|
|
154
|
+
const SessionTreeResponseSchema = z.object({
|
|
155
|
+
groups: z.array(SessionTreeGroupSchema),
|
|
156
|
+
inbox: SessionSectionCountsSchema,
|
|
157
|
+
later: SessionSectionCountsSchema,
|
|
158
|
+
done: SessionSectionCountsSchema,
|
|
159
|
+
noGroup: SessionSectionCountsSchema,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const SessionApprovalSchema = z.object({
|
|
163
|
+
id: z.string(),
|
|
164
|
+
status: z.literal("pending"),
|
|
165
|
+
sourceTaskId: z.string(),
|
|
166
|
+
title: z.string(),
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const SessionKeyFilterSchema = z.string().transform((value, ctx) => {
|
|
170
|
+
try {
|
|
171
|
+
return normalizeAssetKey(value);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
ctx.addIssue({
|
|
174
|
+
code: "custom",
|
|
175
|
+
message: error instanceof Error ? error.message : "Invalid asset key",
|
|
176
|
+
});
|
|
177
|
+
return z.NEVER;
|
|
178
|
+
}
|
|
103
179
|
});
|
|
104
180
|
|
|
105
181
|
/**
|
|
@@ -431,6 +507,21 @@ const listSessions = route({
|
|
|
431
507
|
* key plus a trailing `:` (e.g. `task:ui:workflow:abc:`).
|
|
432
508
|
*/
|
|
433
509
|
contextKeyPrefix: z.string().min(1).optional(),
|
|
510
|
+
/** Literal asset key prefix, including nested groups. */
|
|
511
|
+
keyPrefix: SessionKeyFilterSchema.optional(),
|
|
512
|
+
/** Exact asset group, including direct resource leaves. */
|
|
513
|
+
groupKey: SessionKeyFilterSchema.optional(),
|
|
514
|
+
/** Shared and viewer-personal namespace roots and direct resource leaves. */
|
|
515
|
+
noGroup: z
|
|
516
|
+
.enum(["true", "false"])
|
|
517
|
+
.transform((v) => v === "true")
|
|
518
|
+
.optional(),
|
|
519
|
+
/** Chain-wide read state or Inbox section. */
|
|
520
|
+
triage: z.enum(["inbox", "later", "done", "unread"]).optional(),
|
|
521
|
+
/** Latest chain status or pending human attention. */
|
|
522
|
+
status: z.enum(["running", "failed", "needs"]).optional(),
|
|
523
|
+
/** Requester anywhere in the chain or active share recipient. */
|
|
524
|
+
withUserId: z.string().min(1).optional(),
|
|
434
525
|
/** `full` restores the legacy shape (full root `AgentTask`); default is slim. */
|
|
435
526
|
fields: z.enum(["full", "slim"]).optional(),
|
|
436
527
|
/**
|
|
@@ -438,9 +529,16 @@ const listSessions = route({
|
|
|
438
529
|
* with an active share to the caller or to everyone. Omit for every
|
|
439
530
|
* session the caller can open. Callers that act for no user (agents, the
|
|
440
531
|
* operator key with no picked dashboard user) see every session, and
|
|
441
|
-
* an empty list for
|
|
532
|
+
* an empty list for mine or shared.
|
|
533
|
+
* tree: Sessions that GET /api/session-tree counts: mine plus shared with me,
|
|
534
|
+
* no ownerless roots. A bypass actor sees every root.
|
|
442
535
|
*/
|
|
443
|
-
scope: z
|
|
536
|
+
scope: z
|
|
537
|
+
.enum(["mine", "shared", "tree"])
|
|
538
|
+
.optional()
|
|
539
|
+
.describe(
|
|
540
|
+
"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.",
|
|
541
|
+
),
|
|
444
542
|
}),
|
|
445
543
|
responses: {
|
|
446
544
|
200: {
|
|
@@ -457,6 +555,126 @@ const listSessions = route({
|
|
|
457
555
|
auth: { apiKey: true },
|
|
458
556
|
});
|
|
459
557
|
|
|
558
|
+
const sessionTree = route({
|
|
559
|
+
method: "get",
|
|
560
|
+
path: "/api/session-tree",
|
|
561
|
+
pattern: ["api", "session-tree"],
|
|
562
|
+
summary: "Get session groups and triage counts",
|
|
563
|
+
tags: ["Sessions"],
|
|
564
|
+
responses: {
|
|
565
|
+
200: { description: "Visible groups and section counts", schema: SessionTreeResponseSchema },
|
|
566
|
+
401: { description: "Unauthorized" },
|
|
567
|
+
},
|
|
568
|
+
auth: { apiKey: true },
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
const SESSION_MOVE_LIMIT = 200;
|
|
572
|
+
const SESSION_MOVE_OWNER_ONLY = "Only the session owner can move this group";
|
|
573
|
+
const SESSION_MOVE_UNAVAILABLE = "Session not found or not accessible";
|
|
574
|
+
|
|
575
|
+
const SessionMoveResultSchema = z.object({
|
|
576
|
+
moved: z.array(z.string()),
|
|
577
|
+
skipped: z.array(z.object({ rootTaskId: z.string(), reason: z.string() })),
|
|
578
|
+
});
|
|
579
|
+
const sessionMoveRbac = {
|
|
580
|
+
ungated:
|
|
581
|
+
"per-session access, per-task task.fs.mutate, and asset-key write checks in the handler, as PATCH /api/assets/task/{id}/key",
|
|
582
|
+
};
|
|
583
|
+
const sessionMoveResponses = {
|
|
584
|
+
400: { description: "Invalid namespace or request" },
|
|
585
|
+
403: { description: "Personal destination not authorized" },
|
|
586
|
+
409: { description: "Moves blocked until audit warnings are repaired" },
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
const moveSessions = route({
|
|
590
|
+
method: "post",
|
|
591
|
+
path: "/api/session-moves",
|
|
592
|
+
pattern: ["api", "session-moves"],
|
|
593
|
+
summary: "Move sessions into a group or reset their keys",
|
|
594
|
+
tags: ["Sessions"],
|
|
595
|
+
session: { none: "The handler checks each root and reports denied roots as skipped" },
|
|
596
|
+
body: z.object({
|
|
597
|
+
rootTaskIds: z.array(z.string().min(1)).min(1).max(SESSION_MOVE_LIMIT),
|
|
598
|
+
groupKey: AssetKeySchema.nullable(),
|
|
599
|
+
}),
|
|
600
|
+
responses: {
|
|
601
|
+
200: { description: "Moved and skipped sessions", schema: SessionMoveResultSchema },
|
|
602
|
+
...sessionMoveResponses,
|
|
603
|
+
},
|
|
604
|
+
auth: { apiKey: true },
|
|
605
|
+
rbac: sessionMoveRbac,
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
const moveSessionPrefix = route({
|
|
609
|
+
method: "post",
|
|
610
|
+
path: "/api/session-moves/prefix",
|
|
611
|
+
pattern: ["api", "session-moves", "prefix"],
|
|
612
|
+
summary: "Rename or delete a session group",
|
|
613
|
+
tags: ["Sessions"],
|
|
614
|
+
body: z.object({ from: AssetKeySchema, to: AssetKeySchema.nullable() }),
|
|
615
|
+
responses: {
|
|
616
|
+
200: {
|
|
617
|
+
description: "Moved and skipped sessions and remaining movable roots",
|
|
618
|
+
schema: SessionMoveResultSchema.extend({ remaining: z.number().int().min(0) }),
|
|
619
|
+
},
|
|
620
|
+
...sessionMoveResponses,
|
|
621
|
+
},
|
|
622
|
+
auth: { apiKey: true },
|
|
623
|
+
rbac: sessionMoveRbac,
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
async function withSessionMove(
|
|
627
|
+
req: IncomingMessage,
|
|
628
|
+
res: ServerResponse,
|
|
629
|
+
myAgentId: string | undefined,
|
|
630
|
+
destination: string | null,
|
|
631
|
+
run: (context: { actor: SessionActor; changedBy?: string; key: string | null }) => Promise<void>,
|
|
632
|
+
): Promise<void> {
|
|
633
|
+
try {
|
|
634
|
+
const changedBy = await resolveHttpAuditUserId(req, myAgentId);
|
|
635
|
+
const actor = await resolveSessionActor(req, myAgentId);
|
|
636
|
+
const key = destination === null ? null : await authorizeAssetKeyWrite(destination, changedBy);
|
|
637
|
+
await assertAssetKeyMovesAllowed();
|
|
638
|
+
await run({ actor, changedBy: changedBy ?? undefined, key });
|
|
639
|
+
} catch (error) {
|
|
640
|
+
if (error instanceof AssetKeyAuthorizationError) {
|
|
641
|
+
jsonError(res, error.message, error.statusCode);
|
|
642
|
+
} else if (error instanceof AssetKeyMovesBlockedError) {
|
|
643
|
+
jsonError(res, error.message, 409);
|
|
644
|
+
} else {
|
|
645
|
+
throw error;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
async function checkSessionMove(
|
|
651
|
+
rootTaskId: string,
|
|
652
|
+
actor: SessionActor,
|
|
653
|
+
req: IncomingMessage,
|
|
654
|
+
myAgentId: string | undefined,
|
|
655
|
+
options: { ownerOnly?: boolean } = {},
|
|
656
|
+
): Promise<{ reason: string } | { taskIds: string[] }> {
|
|
657
|
+
const root = await getTaskById(rootTaskId);
|
|
658
|
+
if (!root) return { reason: SESSION_MOVE_UNAVAILABLE };
|
|
659
|
+
if (options.ownerOnly) {
|
|
660
|
+
if (actor.kind === "user" && (!actor.userId || root.requestedByUserId !== actor.userId)) {
|
|
661
|
+
return { reason: SESSION_MOVE_OWNER_ONLY };
|
|
662
|
+
}
|
|
663
|
+
} else {
|
|
664
|
+
const access = await sessionAccessFor(actor, root.id);
|
|
665
|
+
if (access === "none") return { reason: SESSION_MOVE_UNAVAILABLE };
|
|
666
|
+
if (!hasSessionAccess(access, "write")) return { reason: "Session write access required" };
|
|
667
|
+
}
|
|
668
|
+
if (root.parentTaskId) return { reason: "Task is not a session root" };
|
|
669
|
+
const tasks = await getRootTaskChain(root.id);
|
|
670
|
+
for (const task of tasks) {
|
|
671
|
+
if (!(await canMutateTaskNamespace(task, myAgentId, req))) {
|
|
672
|
+
return { reason: "Not authorized to move this task namespace" };
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return { taskIds: tasks.map((task) => task.id) };
|
|
676
|
+
}
|
|
677
|
+
|
|
460
678
|
const getSession = route({
|
|
461
679
|
method: "get",
|
|
462
680
|
path: "/api/sessions/{rootTaskId}",
|
|
@@ -474,6 +692,11 @@ const getSession = route({
|
|
|
474
692
|
chain: z.array(TaskWithSteeringSchema),
|
|
475
693
|
access: SessionAccessSchema,
|
|
476
694
|
owner: SessionOwnerSchema.nullable(),
|
|
695
|
+
approvals: z.array(SessionApprovalSchema).optional(),
|
|
696
|
+
/** The viewer's read state, with the list's rule. Omitted for callers that act for no user. */
|
|
697
|
+
readState: z.enum(["unread", "read", "later", "done"]).optional(),
|
|
698
|
+
/** Newest chain activity: the read cursor that "mark read" writes. With `readState`. */
|
|
699
|
+
lastActivityAt: z.string().optional(),
|
|
477
700
|
}),
|
|
478
701
|
},
|
|
479
702
|
401: { description: "Unauthorized" },
|
|
@@ -641,12 +864,13 @@ export async function handleSessions(
|
|
|
641
864
|
.filter(Boolean)
|
|
642
865
|
: undefined;
|
|
643
866
|
const actor = await resolveSessionActor(req, myAgentId);
|
|
644
|
-
|
|
867
|
+
const limit = Math.min(200, Math.max(0, parsed.query.limit ?? 25));
|
|
868
|
+
if (actor.kind === "bypass" && parsed.query.scope && parsed.query.scope !== "tree") {
|
|
645
869
|
// No user to own or receive a share.
|
|
646
870
|
listSessions.respond(res, 200, {
|
|
647
871
|
sessions: [],
|
|
648
872
|
total: 0,
|
|
649
|
-
limit
|
|
873
|
+
limit,
|
|
650
874
|
offset: parsed.query.offset ?? 0,
|
|
651
875
|
});
|
|
652
876
|
return true;
|
|
@@ -656,13 +880,26 @@ export async function handleSessions(
|
|
|
656
880
|
q: parsed.query.q,
|
|
657
881
|
requestedByUserId: parsed.query.requestedByUserId,
|
|
658
882
|
contextKeyPrefix: parsed.query.contextKeyPrefix,
|
|
883
|
+
keyPrefix: parsed.query.keyPrefix,
|
|
884
|
+
groupKey: parsed.query.groupKey,
|
|
885
|
+
noGroup: parsed.query.noGroup,
|
|
886
|
+
triage: parsed.query.triage,
|
|
887
|
+
status: parsed.query.status,
|
|
888
|
+
withUserId: parsed.query.withUserId,
|
|
889
|
+
now: new Date().toISOString(),
|
|
890
|
+
tree: parsed.query.scope === "tree",
|
|
659
891
|
...(actor.kind === "user"
|
|
660
|
-
? {
|
|
892
|
+
? {
|
|
893
|
+
viewer: {
|
|
894
|
+
userId: actor.userId,
|
|
895
|
+
scope: parsed.query.scope === "tree" ? "all" : (parsed.query.scope ?? "all"),
|
|
896
|
+
} as const,
|
|
897
|
+
}
|
|
661
898
|
: {}),
|
|
662
899
|
};
|
|
663
900
|
const baseOpts = {
|
|
664
901
|
...filterOpts,
|
|
665
|
-
limit
|
|
902
|
+
limit,
|
|
666
903
|
offset: parsed.query.offset,
|
|
667
904
|
};
|
|
668
905
|
// List responses default to slim (root is a task summary); `?fields=full` restores it.
|
|
@@ -676,12 +913,121 @@ export async function handleSessions(
|
|
|
676
913
|
listSessions.respond(res, 200, {
|
|
677
914
|
sessions,
|
|
678
915
|
total,
|
|
679
|
-
limit
|
|
916
|
+
limit,
|
|
680
917
|
offset: parsed.query.offset ?? 0,
|
|
681
918
|
});
|
|
682
919
|
return true;
|
|
683
920
|
}
|
|
684
921
|
|
|
922
|
+
if (sessionTree.match(req.method, pathSegments)) {
|
|
923
|
+
const parsed = await sessionTree.parse(req, res, pathSegments, queryParams);
|
|
924
|
+
if (!parsed) return true;
|
|
925
|
+
sessionTree.respond(res, 200, await getSessionTree(await resolveSessionActor(req, myAgentId)));
|
|
926
|
+
return true;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
if (moveSessions.match(req.method, pathSegments)) {
|
|
930
|
+
const parsed = await moveSessions.parse(req, res, pathSegments, queryParams);
|
|
931
|
+
if (!parsed) return true;
|
|
932
|
+
await withSessionMove(
|
|
933
|
+
req,
|
|
934
|
+
res,
|
|
935
|
+
myAgentId,
|
|
936
|
+
parsed.body.groupKey,
|
|
937
|
+
async ({ actor, changedBy, key }) => {
|
|
938
|
+
const skipped: { rootTaskId: string; reason: string }[] = [];
|
|
939
|
+
const moves: SessionKeyMove[] = [];
|
|
940
|
+
for (const rootId of new Set(parsed.body.rootTaskIds)) {
|
|
941
|
+
const checked = await checkSessionMove(rootId, actor, req, myAgentId);
|
|
942
|
+
if ("reason" in checked) {
|
|
943
|
+
skipped.push({ rootTaskId: rootId, reason: checked.reason });
|
|
944
|
+
} else {
|
|
945
|
+
moves.push({
|
|
946
|
+
rootId,
|
|
947
|
+
taskIds: checked.taskIds,
|
|
948
|
+
key: key ?? defaultAssetKey("task", rootId),
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
const moved = await moveSessionChains({ moves, changedBy });
|
|
953
|
+
moveSessions.respond(res, 200, { moved, skipped });
|
|
954
|
+
},
|
|
955
|
+
);
|
|
956
|
+
return true;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
if (moveSessionPrefix.match(req.method, pathSegments)) {
|
|
960
|
+
const parsed = await moveSessionPrefix.parse(req, res, pathSegments, queryParams);
|
|
961
|
+
if (!parsed) return true;
|
|
962
|
+
const from = normalizeAssetKey(parsed.body.from);
|
|
963
|
+
if (parseAssetKey(from).relativeSegments.length === 0) {
|
|
964
|
+
jsonError(res, "Cannot move a namespace root", 400);
|
|
965
|
+
return true;
|
|
966
|
+
}
|
|
967
|
+
await withSessionMove(
|
|
968
|
+
req,
|
|
969
|
+
res,
|
|
970
|
+
myAgentId,
|
|
971
|
+
parsed.body.to,
|
|
972
|
+
async ({ actor, changedBy, key }) => {
|
|
973
|
+
if (key?.startsWith(from)) {
|
|
974
|
+
jsonError(res, "Destination must be outside the source prefix", 400);
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
const skipped =
|
|
978
|
+
actor.kind === "user"
|
|
979
|
+
? (await listSharedPrefixRoots({ from, userId: actor.userId })).map((root) => ({
|
|
980
|
+
rootTaskId: root.id,
|
|
981
|
+
reason: SESSION_MOVE_OWNER_ONLY,
|
|
982
|
+
}))
|
|
983
|
+
: [];
|
|
984
|
+
const moves: SessionKeyMove[] = [];
|
|
985
|
+
let remaining = 0;
|
|
986
|
+
// Scan before moving so offsets stay stable. Only owned roots need chain authorization.
|
|
987
|
+
for (let offset = 0; ; offset += SESSION_MOVE_LIMIT) {
|
|
988
|
+
const roots = await listPrefixRoots({
|
|
989
|
+
from,
|
|
990
|
+
userId: actor.kind === "user" ? actor.userId : undefined,
|
|
991
|
+
limit: SESSION_MOVE_LIMIT,
|
|
992
|
+
offset,
|
|
993
|
+
});
|
|
994
|
+
for (const root of roots) {
|
|
995
|
+
const nextKey =
|
|
996
|
+
key === null ? defaultAssetKey("task", root.id) : key + root.key.slice(from.length);
|
|
997
|
+
if (nextKey.startsWith(from)) {
|
|
998
|
+
skipped.push({
|
|
999
|
+
rootTaskId: root.id,
|
|
1000
|
+
reason: "The new key stays inside the source group",
|
|
1001
|
+
});
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
if (nextKey.length > ASSET_KEY_MAX_LENGTH) {
|
|
1005
|
+
skipped.push({
|
|
1006
|
+
rootTaskId: root.id,
|
|
1007
|
+
reason: `Asset key cannot exceed ${ASSET_KEY_MAX_LENGTH} characters`,
|
|
1008
|
+
});
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
const checked = await checkSessionMove(root.id, actor, req, myAgentId, {
|
|
1012
|
+
ownerOnly: true,
|
|
1013
|
+
});
|
|
1014
|
+
if ("reason" in checked) {
|
|
1015
|
+
skipped.push({ rootTaskId: root.id, reason: checked.reason });
|
|
1016
|
+
} else if (moves.length < SESSION_MOVE_LIMIT) {
|
|
1017
|
+
moves.push({ rootId: root.id, taskIds: checked.taskIds, key: nextKey });
|
|
1018
|
+
} else {
|
|
1019
|
+
remaining++;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
if (roots.length < SESSION_MOVE_LIMIT) break;
|
|
1023
|
+
}
|
|
1024
|
+
const moved = await moveSessionChains({ moves, changedBy });
|
|
1025
|
+
moveSessionPrefix.respond(res, 200, { moved, skipped, remaining });
|
|
1026
|
+
},
|
|
1027
|
+
);
|
|
1028
|
+
return true;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
685
1031
|
if (getSession.match(req.method, pathSegments)) {
|
|
686
1032
|
const parsed = await getSession.parse(req, res, pathSegments, queryParams);
|
|
687
1033
|
if (!parsed) return true;
|
|
@@ -695,7 +1041,8 @@ export async function handleSessions(
|
|
|
695
1041
|
id: root.id,
|
|
696
1042
|
requestedByUserId: root.requestedByUserId ?? null,
|
|
697
1043
|
};
|
|
698
|
-
const
|
|
1044
|
+
const actor = await resolveSessionActor(req, myAgentId);
|
|
1045
|
+
const access = await accessForRoot(actor, sessionRoot);
|
|
699
1046
|
if (access === "none") {
|
|
700
1047
|
jsonError(res, "You don't have access to this session. Ask the owner to share it.", 403);
|
|
701
1048
|
return true;
|
|
@@ -710,12 +1057,17 @@ export async function handleSessions(
|
|
|
710
1057
|
}
|
|
711
1058
|
: null;
|
|
712
1059
|
const chain = await getRootTaskChain(parsed.params.rootTaskId);
|
|
713
|
-
//
|
|
714
|
-
// citations lookup for the whole chain (chunked past 500 ids).
|
|
1060
|
+
// Load steering, citations and pending approvals for the already loaded chain.
|
|
715
1061
|
const tasks = [root, ...chain];
|
|
716
|
-
const [steering, citations] = await Promise.all([
|
|
1062
|
+
const [steering, citations, approvals, read] = await Promise.all([
|
|
717
1063
|
getTaskSteeringFieldsForTasks(tasks),
|
|
718
1064
|
getTaskCitationsForTasks(tasks.map((task) => task.id)),
|
|
1065
|
+
getPendingApprovalsForTasks(tasks.map((task) => task.id)),
|
|
1066
|
+
// The viewer's read state, so a client marks a session read on open
|
|
1067
|
+
// without the list row (the same rule as `GET /api/sessions`).
|
|
1068
|
+
actor.kind === "user" && actor.userId
|
|
1069
|
+
? getSessionReadState(sessionRoot.id, actor.userId)
|
|
1070
|
+
: Promise.resolve(null),
|
|
719
1071
|
]);
|
|
720
1072
|
const decorate = (task: AgentTask) => ({
|
|
721
1073
|
...task,
|
|
@@ -727,6 +1079,8 @@ export async function handleSessions(
|
|
|
727
1079
|
chain: chain.map(decorate),
|
|
728
1080
|
access,
|
|
729
1081
|
owner,
|
|
1082
|
+
...(approvals.length ? { approvals } : {}),
|
|
1083
|
+
...(read ?? {}),
|
|
730
1084
|
});
|
|
731
1085
|
return true;
|
|
732
1086
|
}
|
package/src/http/skills.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
getAgentSkills,
|
|
8
8
|
getSkillById,
|
|
9
9
|
getSkillFile,
|
|
10
|
+
getSkillFiles,
|
|
10
11
|
installSkill,
|
|
11
12
|
listSkillFileManifest,
|
|
12
13
|
listSkills,
|
|
@@ -17,7 +18,11 @@ import {
|
|
|
17
18
|
upsertSkillFiles,
|
|
18
19
|
} from "../be/db";
|
|
19
20
|
import { parseSkillContent } from "../be/skill-parser";
|
|
20
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
buildSkillsBundle,
|
|
23
|
+
computeAgentSkillsSignature,
|
|
24
|
+
syncSkillsToFilesystem,
|
|
25
|
+
} from "../be/skill-sync";
|
|
21
26
|
import { getSkillUsageDetail, getSkillUsageSummary, SKILL_USAGE_WINDOWS } from "../be/skill-usage";
|
|
22
27
|
import {
|
|
23
28
|
AgentSkillSchema,
|
|
@@ -102,6 +107,21 @@ const agentSkillsListResponseSchema = z.object({
|
|
|
102
107
|
signature: z.string(),
|
|
103
108
|
});
|
|
104
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Boot payload for workers: every installed-and-enabled skill together with
|
|
112
|
+
* the full content of its bundled files. Replaces the list call plus the
|
|
113
|
+
* per-skill, per-file fan-out a worker used to make while booting.
|
|
114
|
+
*/
|
|
115
|
+
const agentSkillsBundleResponseSchema = z.object({
|
|
116
|
+
skills: z.array(
|
|
117
|
+
SkillWithInstallInfoSchema.extend({
|
|
118
|
+
files: z.array(SkillFileSchema),
|
|
119
|
+
}),
|
|
120
|
+
),
|
|
121
|
+
total: z.number(),
|
|
122
|
+
signature: z.string(),
|
|
123
|
+
});
|
|
124
|
+
|
|
105
125
|
const agentSkillsSignatureResponseSchema = z.object({
|
|
106
126
|
hash: z.string(),
|
|
107
127
|
count: z.number(),
|
|
@@ -543,6 +563,21 @@ const getAgentSkillsSignatureRoute = route({
|
|
|
543
563
|
},
|
|
544
564
|
});
|
|
545
565
|
|
|
566
|
+
const getAgentSkillsBundleRoute = route({
|
|
567
|
+
method: "get",
|
|
568
|
+
path: "/api/agents/{id}/skills/bundle",
|
|
569
|
+
pattern: ["api", "agents", null, "skills", "bundle"],
|
|
570
|
+
summary: "Get an agent's installed skills together with their bundled files",
|
|
571
|
+
description:
|
|
572
|
+
"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.",
|
|
573
|
+
tags: ["Skills"],
|
|
574
|
+
auth: { apiKey: true },
|
|
575
|
+
params: z.object({ id: z.string() }),
|
|
576
|
+
responses: {
|
|
577
|
+
200: { description: "Agent skills bundle", schema: agentSkillsBundleResponseSchema },
|
|
578
|
+
},
|
|
579
|
+
});
|
|
580
|
+
|
|
546
581
|
// ─── Handler ─────────────────────────────────────────────────────────────────
|
|
547
582
|
|
|
548
583
|
export async function handleSkills(
|
|
@@ -552,6 +587,18 @@ export async function handleSkills(
|
|
|
552
587
|
queryParams: URLSearchParams,
|
|
553
588
|
myAgentId: string | undefined,
|
|
554
589
|
): Promise<boolean> {
|
|
590
|
+
// GET /api/agents/:id/skills/bundle (must come before the shorter patterns)
|
|
591
|
+
if (getAgentSkillsBundleRoute.match(req.method, pathSegments)) {
|
|
592
|
+
const parsed = await getAgentSkillsBundleRoute.parse(req, res, pathSegments, queryParams);
|
|
593
|
+
if (!parsed) return true;
|
|
594
|
+
const bundle = await buildSkillsBundle(
|
|
595
|
+
() => getAgentSkills(parsed.params.id),
|
|
596
|
+
(skillId) => getSkillFiles(skillId),
|
|
597
|
+
);
|
|
598
|
+
getAgentSkillsBundleRoute.respond(res, 200, bundle);
|
|
599
|
+
return true;
|
|
600
|
+
}
|
|
601
|
+
|
|
555
602
|
// GET /api/agents/:id/skills/signature (must come before the shorter pattern)
|
|
556
603
|
if (getAgentSkillsSignatureRoute.match(req.method, pathSegments)) {
|
|
557
604
|
const parsed = await getAgentSkillsSignatureRoute.parse(req, res, pathSegments, queryParams);
|
package/src/http/tasks.ts
CHANGED
|
@@ -744,7 +744,7 @@ const updateTaskVcsRoute = route({
|
|
|
744
744
|
tags: ["Tasks"],
|
|
745
745
|
params: z.object({ id: z.string() }),
|
|
746
746
|
body: z.object({
|
|
747
|
-
vcsProvider: z.enum(["github", "gitlab", "azure-devops"]),
|
|
747
|
+
vcsProvider: z.enum(["github", "gitlab", "azure-devops", "bitbucket"]),
|
|
748
748
|
vcsRepo: z.string(),
|
|
749
749
|
vcsNumber: z.number().int().positive(),
|
|
750
750
|
vcsUrl: z.string().url(),
|
package/src/http/users.ts
CHANGED
|
@@ -550,7 +550,19 @@ const deleteIdentityRoute = route({
|
|
|
550
550
|
|
|
551
551
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
552
552
|
|
|
553
|
-
const UNMAPPED_KINDS = [
|
|
553
|
+
const UNMAPPED_KINDS = [
|
|
554
|
+
"slack",
|
|
555
|
+
"github",
|
|
556
|
+
"gitlab",
|
|
557
|
+
"azure-devops",
|
|
558
|
+
"bitbucket",
|
|
559
|
+
"linear",
|
|
560
|
+
"kapso",
|
|
561
|
+
// Chat SDK adapters record unknown senders under the adapter name.
|
|
562
|
+
"telegram",
|
|
563
|
+
"discord",
|
|
564
|
+
"teams",
|
|
565
|
+
] as const;
|
|
554
566
|
|
|
555
567
|
/**
|
|
556
568
|
* Group the two-key-per-identity kv entries (`<externalId>:meta` json +
|