@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
|
@@ -13,11 +13,15 @@ export const argsSchema = z.object({
|
|
|
13
13
|
.min(0)
|
|
14
14
|
.max(1)
|
|
15
15
|
.optional()
|
|
16
|
-
.describe(
|
|
16
|
+
.describe(
|
|
17
|
+
"Quiet when the model's p(needs action) is below this (default: the provider's measured threshold, 0.5 for Jev, 0.2 for Clef)",
|
|
18
|
+
),
|
|
17
19
|
provider: z
|
|
18
|
-
.enum(["auto", "typesafe", "openrouter"])
|
|
20
|
+
.enum(["auto", "typesafe", "openrouter", "cloudflare"])
|
|
19
21
|
.optional()
|
|
20
|
-
.describe(
|
|
22
|
+
.describe(
|
|
23
|
+
"Decision host: typesafe or openrouter (Jev), cloudflare (Clef). auto tries them in that order (default auto)",
|
|
24
|
+
),
|
|
21
25
|
timeZone: z
|
|
22
26
|
.string()
|
|
23
27
|
.optional()
|
|
@@ -28,25 +32,56 @@ export const argsSchema = z.object({
|
|
|
28
32
|
.min(1_000)
|
|
29
33
|
.max(45_000)
|
|
30
34
|
.optional()
|
|
31
|
-
.describe(
|
|
35
|
+
.describe(
|
|
36
|
+
"Abandon the decision call after this many ms (default 25000); the gate deadline is 60 s",
|
|
37
|
+
),
|
|
32
38
|
});
|
|
33
39
|
|
|
34
40
|
const KV_NAMESPACE = "heartbeat-jev-gate";
|
|
35
41
|
const KV_KEY = "setup";
|
|
36
|
-
//
|
|
37
|
-
const
|
|
42
|
+
// Jev's cost, measured per decision in the 2026-10-15 heartbeat replay (229 states).
|
|
43
|
+
const JEV_COST_PER_DECISION = "$0.00004";
|
|
44
|
+
// Clef replayed on the same 229 states and labels (2026-10-20): 0.2 is the highest
|
|
45
|
+
// threshold that misses none of the 9 ticks where the Lead acted (57 quiet ticks;
|
|
46
|
+
// Jev at 0.5 has 100). Above 0.21 Clef starts missing real asks.
|
|
47
|
+
const CLEF_THRESHOLD = 0.2;
|
|
38
48
|
|
|
39
|
-
//
|
|
40
|
-
// key and sends the request, so no key ever enters this script's realm. The
|
|
49
|
+
// The model is called through `ctx.swarm.systemOne_decide`: the API server reads
|
|
50
|
+
// the key and sends the request, so no key ever enters this script's realm. The
|
|
41
51
|
// server also refuses OpenRouter when the effective OPENROUTER_BASE_URL (stored
|
|
42
|
-
// config or deployment env) points at a gateway
|
|
52
|
+
// config or deployment env) points at a gateway; `auto` then tries the next
|
|
53
|
+
// usable provider, since this script cannot read the deployment env.
|
|
54
|
+
//
|
|
55
|
+
// `credentials` lists what must be set: every group needs one of its keys.
|
|
56
|
+
// `threshold` is the default the provider's own replay measured (see `CLEF_THRESHOLD`).
|
|
43
57
|
const PROVIDERS = {
|
|
44
|
-
typesafe: {
|
|
45
|
-
|
|
58
|
+
typesafe: {
|
|
59
|
+
model: "jev-latest",
|
|
60
|
+
family: "jev",
|
|
61
|
+
threshold: 0.5,
|
|
62
|
+
credentials: [["TYPESAFE_API_KEY"]],
|
|
63
|
+
setup: "`TYPESAFE_API_KEY` (Jev on TypeSafe)",
|
|
64
|
+
},
|
|
65
|
+
openrouter: {
|
|
66
|
+
model: "~typesafe/jev-latest",
|
|
67
|
+
family: "jev",
|
|
68
|
+
threshold: 0.5,
|
|
69
|
+
credentials: [["OPENROUTER_API_KEY"]],
|
|
70
|
+
setup: "`OPENROUTER_API_KEY` (Jev on OpenRouter)",
|
|
71
|
+
},
|
|
72
|
+
cloudflare: {
|
|
73
|
+
model: "clef",
|
|
74
|
+
family: "clef",
|
|
75
|
+
threshold: CLEF_THRESHOLD,
|
|
76
|
+
credentials: [["CLOUDFLARE_API_TOKEN", "CLOUDFLARE_API_KEY"], ["CLOUDFLARE_ACCOUNT_ID"]],
|
|
77
|
+
setup:
|
|
78
|
+
"`CLOUDFLARE_API_TOKEN` (or `CLOUDFLARE_API_KEY`) as a secret plus the `CLOUDFLARE_ACCOUNT_ID` config value (Clef on Cloudflare Workers AI)",
|
|
79
|
+
},
|
|
46
80
|
} as const;
|
|
47
81
|
type ProviderId = keyof typeof PROVIDERS;
|
|
82
|
+
const AUTO_ORDER: ProviderId[] = ["typesafe", "openrouter", "cloudflare"];
|
|
48
83
|
|
|
49
|
-
// Worded exactly as in the
|
|
84
|
+
// Worded exactly as in the replays that set the thresholds; changing it voids those results.
|
|
50
85
|
const QUESTIONS = {
|
|
51
86
|
act: {
|
|
52
87
|
type: "noul",
|
|
@@ -229,21 +264,25 @@ async function buildState(ctx: any, leadId: string, nowMs: number, timeZone: str
|
|
|
229
264
|
};
|
|
230
265
|
}
|
|
231
266
|
|
|
232
|
-
function setupPrompt(
|
|
267
|
+
function setupPrompt(candidates: ProviderId[]) {
|
|
268
|
+
const options = candidates.map((id) => `- ${PROVIDERS[id].setup}`);
|
|
233
269
|
return {
|
|
234
|
-
title: "Heartbeat
|
|
270
|
+
title: "Heartbeat gate: add a decision model credential or decline",
|
|
235
271
|
detached: true,
|
|
236
272
|
questions: [
|
|
237
273
|
{
|
|
238
274
|
id: "enable",
|
|
239
275
|
type: "approval",
|
|
240
|
-
label: "Enable the
|
|
276
|
+
label: "Enable the heartbeat checklist gate?",
|
|
241
277
|
description: [
|
|
242
|
-
"Before each recurring heartbeat checklist, the `heartbeat-jev-gate` script sends a short summary of swarm state (about 1,500 characters: new failures, due tracked items, task and agent counts) to
|
|
278
|
+
"Before each recurring heartbeat checklist, the `heartbeat-jev-gate` script sends a short summary of swarm state (about 1,500 characters: new failures, due tracked items, task and agent counts) to a small SystemOne decision model (Jev by TypeSafe, or Clef on Cloudflare Workers AI) and asks whether the Lead needs to act. With `HEARTBEAT_CHECKLIST_GATE_MODE=enforce`, quiet ticks skip the Lead's checklist task.",
|
|
243
279
|
"",
|
|
244
|
-
`Cost:
|
|
280
|
+
`Cost: one decision per checklist tick, about ${JEV_COST_PER_DECISION} each with Jev.`,
|
|
245
281
|
"",
|
|
246
|
-
|
|
282
|
+
"No credential for the gate is configured. To enable, approve and add one of these as a global value on the Secrets page (Settings > Secrets):",
|
|
283
|
+
...options,
|
|
284
|
+
"",
|
|
285
|
+
"Do not paste a key here. The next tick picks it up.",
|
|
247
286
|
"",
|
|
248
287
|
"Reject to decline. The gate then stays inactive, with no further prompts, until a key is added.",
|
|
249
288
|
].join("\n"),
|
|
@@ -253,9 +292,10 @@ function setupPrompt(keys: string[]) {
|
|
|
253
292
|
}
|
|
254
293
|
|
|
255
294
|
/**
|
|
256
|
-
* Heartbeat checklist gate backed by
|
|
257
|
-
* tick needs the Lead and returns
|
|
258
|
-
* credential it asks a human once, then
|
|
295
|
+
* Heartbeat checklist gate backed by a SystemOne decision model (Jev or Clef):
|
|
296
|
+
* asks whether a recurring checklist tick needs the Lead and returns
|
|
297
|
+
* { quiet, reason, summary }. With no credential it asks a human once, then
|
|
298
|
+
* stays a no-op until a key is added.
|
|
259
299
|
*/
|
|
260
300
|
export default async function heartbeatJevGate(args: any, ctx: any) {
|
|
261
301
|
const parsed = argsSchema.safeParse(args ?? {});
|
|
@@ -276,25 +316,21 @@ export default async function heartbeatJevGate(args: any, ctx: any) {
|
|
|
276
316
|
};
|
|
277
317
|
|
|
278
318
|
const cfgThreshold = Number.parseFloat(cfg("HEARTBEAT_JEV_GATE_THRESHOLD"));
|
|
279
|
-
const threshold =
|
|
280
|
-
a.threshold ?? (cfgThreshold >= 0 && cfgThreshold <= 1 ? cfgThreshold : 0.5);
|
|
281
319
|
const providerPref = a.provider ?? (cfg("HEARTBEAT_JEV_GATE_PROVIDER").toLowerCase() || "auto");
|
|
282
320
|
const timeZone = a.timeZone ?? (cfg("HEARTBEAT_JEV_GATE_TIMEZONE") || "UTC");
|
|
283
321
|
|
|
284
|
-
const usable:
|
|
285
|
-
|
|
286
|
-
openrouter: cfg(PROVIDERS.openrouter.keyName) !== "",
|
|
287
|
-
};
|
|
322
|
+
const usable = (id: ProviderId) =>
|
|
323
|
+
PROVIDERS[id].credentials.every((group) => group.some((key) => cfg(key) !== ""));
|
|
288
324
|
const candidates: ProviderId[] =
|
|
289
|
-
providerPref
|
|
290
|
-
|
|
291
|
-
: providerPref === "openrouter"
|
|
292
|
-
? ["openrouter"]
|
|
293
|
-
: ["typesafe", "openrouter"];
|
|
294
|
-
const provider = candidates.find((id) => usable[id]);
|
|
325
|
+
Object.hasOwn(PROVIDERS, providerPref) ? [providerPref as ProviderId] : AUTO_ORDER;
|
|
326
|
+
const usableCandidates = candidates.filter(usable);
|
|
295
327
|
|
|
296
|
-
if (
|
|
297
|
-
const keys = candidates.map((id) =>
|
|
328
|
+
if (usableCandidates.length === 0) {
|
|
329
|
+
const keys = candidates.map((id) =>
|
|
330
|
+
PROVIDERS[id].credentials
|
|
331
|
+
.map((group) => (group.length > 1 ? `(${group.join(" or ")})` : group[0]))
|
|
332
|
+
.join(" + "),
|
|
333
|
+
);
|
|
298
334
|
const inactive = (state: string, extra: Record<string, unknown> = {}) => ({
|
|
299
335
|
quiet: false,
|
|
300
336
|
reason: `jev-gate inactive: ${state}`.slice(0, 300),
|
|
@@ -305,7 +341,7 @@ export default async function heartbeatJevGate(args: any, ctx: any) {
|
|
|
305
341
|
let setup: SetupState | null = typeof stored === "string" ? JSON.parse(stored) : (stored ?? null);
|
|
306
342
|
|
|
307
343
|
if (!setup) {
|
|
308
|
-
const prompt = setupPrompt(
|
|
344
|
+
const prompt = setupPrompt(candidates);
|
|
309
345
|
if (dryRun) {
|
|
310
346
|
const state = await buildState(ctx, leadId, Date.now(), timeZone);
|
|
311
347
|
return inactive("no credential, would ask a human", { prompt, state });
|
|
@@ -349,35 +385,51 @@ export default async function heartbeatJevGate(args: any, ctx: any) {
|
|
|
349
385
|
return inactive(why, { setup });
|
|
350
386
|
}
|
|
351
387
|
|
|
352
|
-
const target = PROVIDERS[provider];
|
|
353
388
|
const state = await buildState(ctx, leadId, Date.now(), timeZone);
|
|
354
389
|
|
|
355
|
-
// The server enforces the time budget. No retries, as in the replay.
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
390
|
+
// The server enforces the time budget. No retries, as in the replay. A provider
|
|
391
|
+
// this deployment refuses (OpenRouter behind a gateway) sends nothing, so the
|
|
392
|
+
// next usable one is tried; only an explicit choice or the last one throws.
|
|
393
|
+
let provider = usableCandidates[0] as ProviderId;
|
|
394
|
+
let body: any;
|
|
395
|
+
const skipped: string[] = [];
|
|
396
|
+
for (const [i, id] of usableCandidates.entries()) {
|
|
397
|
+
provider = id;
|
|
398
|
+
const res: any = await ctx.swarm.systemOne_decide({
|
|
399
|
+
provider: id,
|
|
400
|
+
model: PROVIDERS[id].model,
|
|
401
|
+
state,
|
|
402
|
+
questions: QUESTIONS,
|
|
403
|
+
timeoutMs: a.jevTimeoutMs ?? 25_000,
|
|
404
|
+
maxRetries: 0,
|
|
405
|
+
});
|
|
406
|
+
body = res?.data ?? res;
|
|
407
|
+
if (res?.success !== false && body?.success !== false) break;
|
|
408
|
+
const why = String(body?.message ?? body?.error ?? `HTTP ${res?.status}`).slice(0, 250);
|
|
409
|
+
if (body?.providerUnavailable === true && i < usableCandidates.length - 1) {
|
|
410
|
+
skipped.push(`${id}: ${why}`);
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
throw new Error(`${PROVIDERS[id].family} ${id}: ${why}`);
|
|
367
414
|
}
|
|
415
|
+
const target = PROVIDERS[provider];
|
|
416
|
+
// A configured threshold applies to every provider; unset, each uses its measured one.
|
|
417
|
+
const threshold =
|
|
418
|
+
a.threshold ?? (cfgThreshold >= 0 && cfgThreshold <= 1 ? cfgThreshold : target.threshold);
|
|
368
419
|
const p = body?.answers?.act?.noul;
|
|
369
420
|
if (typeof p !== "number" || !Number.isFinite(p)) {
|
|
370
|
-
throw new Error(
|
|
421
|
+
throw new Error(`${target.family} ${provider}: response has no numeric answers.act.noul`);
|
|
371
422
|
}
|
|
372
423
|
|
|
373
424
|
const pText = p.toFixed(3);
|
|
374
425
|
const quiet = p < threshold;
|
|
375
426
|
const verdict: Record<string, unknown> = {
|
|
376
427
|
quiet,
|
|
377
|
-
reason:
|
|
428
|
+
reason: `${target.family} p(needs action)=${pText} ${quiet ? "<" : ">="} ${threshold} via ${provider}`,
|
|
378
429
|
};
|
|
379
430
|
if (!quiet) {
|
|
380
|
-
const
|
|
431
|
+
const model = target.family === "jev" ? "Jev" : "Clef";
|
|
432
|
+
const lines = [`${model} p(needs action) = ${pText} (threshold ${threshold}, ${provider}).`];
|
|
381
433
|
const failures = state.new_failures_since_previous_heartbeat;
|
|
382
434
|
const tracked = state.tracked_items_mentioning_today_or_past_delete_date;
|
|
383
435
|
if (Array.isArray(failures)) {
|
|
@@ -387,9 +439,9 @@ export default async function heartbeatJevGate(args: any, ctx: any) {
|
|
|
387
439
|
lines.push("", "Tracked items due today or past their DELETE date:", ...tracked.map((t) => `- ${t.replace(/^[-*] /, "")}`));
|
|
388
440
|
}
|
|
389
441
|
if (!Array.isArray(failures) && !Array.isArray(tracked)) {
|
|
390
|
-
lines.push("",
|
|
442
|
+
lines.push("", `No new failures or due tracked items; ${model} flagged the overall state.`);
|
|
391
443
|
}
|
|
392
444
|
verdict.summary = lines.join("\n").slice(0, 4000);
|
|
393
445
|
}
|
|
394
|
-
return dryRun ? { ...verdict, dryRun: true, provider, p, threshold, state } : verdict;
|
|
446
|
+
return dryRun ? { ...verdict, dryRun: true, provider, skipped, p, threshold, state } : verdict;
|
|
395
447
|
}
|
|
@@ -116,9 +116,9 @@ export const SEED_SCRIPTS: SeedScript[] = [
|
|
|
116
116
|
{
|
|
117
117
|
name: "heartbeat-jev-gate",
|
|
118
118
|
description:
|
|
119
|
-
"Heartbeat checklist gate backed by Jev
|
|
119
|
+
"Heartbeat checklist gate backed by a SystemOne decision model (Jev on TypeSafe or OpenRouter, Clef on Cloudflare Workers AI): scores a compact swarm state and returns {quiet, reason, summary} for HEARTBEAT_CHECKLIST_GATE_SCRIPT.",
|
|
120
120
|
intent:
|
|
121
|
-
"Skip quiet Lead heartbeat checklist ticks with a cheap Jev decision; with no
|
|
121
|
+
"Skip quiet Lead heartbeat checklist ticks with a cheap Jev or Clef decision; with no credential it asks a human once, then stays a no-op until a key is added.",
|
|
122
122
|
source: asText(heartbeatJevGateSrc),
|
|
123
123
|
},
|
|
124
124
|
{
|
package/src/be/skill-sync.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { homedir } from "node:os";
|
|
13
|
+
import type { SkillFile, SkillWithInstallInfo } from "../types";
|
|
13
14
|
import {
|
|
14
15
|
type SkillFsEntry,
|
|
15
16
|
type SkillHarnessTarget,
|
|
@@ -71,8 +72,7 @@ export interface SkillsSignature {
|
|
|
71
72
|
* uninstall, toggle, or skill-update mutates at least one of them. Output is
|
|
72
73
|
* deterministic and contains no timestamps beyond per-row mutation fields.
|
|
73
74
|
*/
|
|
74
|
-
export
|
|
75
|
-
const skills = await getAgentSkills(agentId);
|
|
75
|
+
export function computeSkillsSignature(skills: readonly SkillWithInstallInfo[]): SkillsSignature {
|
|
76
76
|
const sorted = [...skills].sort((a, b) => a.id.localeCompare(b.id));
|
|
77
77
|
const canonical = JSON.stringify(
|
|
78
78
|
sorted.map((s) => [
|
|
@@ -89,3 +89,44 @@ export async function computeAgentSkillsSignature(agentId: string): Promise<Skil
|
|
|
89
89
|
const hash = new Bun.CryptoHasher("sha256").update(canonical).digest("hex");
|
|
90
90
|
return { hash, count: sorted.length };
|
|
91
91
|
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Fetch the agent's skills and sign them.
|
|
95
|
+
*
|
|
96
|
+
* Callers that already hold the rows (the bundle endpoint) must use
|
|
97
|
+
* `computeSkillsSignature` on those exact rows instead of calling this: a
|
|
98
|
+
* second read can observe a newer edit and pair a fresh hash with stale
|
|
99
|
+
* payload bytes, which makes later signature probes skip the refresh forever.
|
|
100
|
+
*/
|
|
101
|
+
export async function computeAgentSkillsSignature(agentId: string): Promise<SkillsSignature> {
|
|
102
|
+
return computeSkillsSignature(await getAgentSkills(agentId));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface SkillsBundle {
|
|
106
|
+
skills: (SkillWithInstallInfo & { files: SkillFile[] })[];
|
|
107
|
+
total: number;
|
|
108
|
+
signature: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Build the worker boot payload: every installed skill plus its bundled files,
|
|
113
|
+
* signed over the rows this same call returned.
|
|
114
|
+
*
|
|
115
|
+
* The readers are injected so the ordering the fix depends on is testable: the
|
|
116
|
+
* signature is taken before the first `readFiles`, so an edit that lands while
|
|
117
|
+
* the files are being read cannot pair stale bytes with a newer hash.
|
|
118
|
+
*/
|
|
119
|
+
export async function buildSkillsBundle(
|
|
120
|
+
readSkills: () => Promise<SkillWithInstallInfo[]>,
|
|
121
|
+
readFiles: (skillId: string) => Promise<SkillFile[]>,
|
|
122
|
+
): Promise<SkillsBundle> {
|
|
123
|
+
const skills = await readSkills();
|
|
124
|
+
const signature = computeSkillsSignature(skills).hash;
|
|
125
|
+
const entries = await Promise.all(
|
|
126
|
+
skills.map(async (skill) => ({
|
|
127
|
+
...skill,
|
|
128
|
+
files: skill.isComplex ? await readFiles(skill.id) : [],
|
|
129
|
+
})),
|
|
130
|
+
);
|
|
131
|
+
return { skills: entries, total: entries.length, signature };
|
|
132
|
+
}
|
|
@@ -68,7 +68,11 @@ const RESERVED_KEYS = new Set([
|
|
|
68
68
|
// the agent-fs bootstrap admin key would let a compromised worker administer the
|
|
69
69
|
// shared org. The API materializes these into its own process.env at boot via
|
|
70
70
|
// getInjectableGlobalConfigs, so no HTTP consumer legitimately needs them.
|
|
71
|
-
const API_ONLY_CONFIG_KEYS = new Set([
|
|
71
|
+
const API_ONLY_CONFIG_KEYS = new Set([
|
|
72
|
+
"API_AGENT_FS_API_KEY",
|
|
73
|
+
"SLACK_SIGNING_SECRET",
|
|
74
|
+
"TRANSCRIPTION_API_KEY",
|
|
75
|
+
]);
|
|
72
76
|
|
|
73
77
|
export function isApiOnlyConfigKey(key: string): boolean {
|
|
74
78
|
return API_ONLY_CONFIG_KEYS.has(key);
|
|
@@ -160,6 +164,20 @@ export const APPS_JSON_COLUMN_MAX_BYTES_BOUNDS = { min: 1_024, max: 16_777_216 }
|
|
|
160
164
|
*/
|
|
161
165
|
export const CODEX_OAUTH_KEEP_WARM_MAX_AGE_HOURS_BOUNDS = { min: 1, max: 216 } as const;
|
|
162
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Decision hosts `SYSTEM_ONE_DEFAULT_PROVIDER` may name: `SYSTEM_ONE_PROVIDER_IDS`
|
|
169
|
+
* in `src/workflows/executors/system-one-providers.ts`, listed here because that
|
|
170
|
+
* module's credential readers would pull the LLM stack into every `db.ts` import.
|
|
171
|
+
* A test keeps the two lists equal.
|
|
172
|
+
*/
|
|
173
|
+
export const SYSTEM_ONE_DEFAULT_PROVIDER_OPTIONS = [
|
|
174
|
+
"typesafe",
|
|
175
|
+
"openrouter",
|
|
176
|
+
"laya",
|
|
177
|
+
"openai",
|
|
178
|
+
"cloudflare",
|
|
179
|
+
] as const;
|
|
180
|
+
|
|
163
181
|
/** Build `{ KEY: validator }` entries accepting only boolean literals. */
|
|
164
182
|
function booleanValidators(keys: string[]): Record<string, ConfigValidator> {
|
|
165
183
|
const message = (key: string) =>
|
|
@@ -399,6 +417,7 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
|
|
|
399
417
|
},
|
|
400
418
|
...enumValidator("CLAUDE_TRANSPORT", ["cli", "sdk"]),
|
|
401
419
|
...enumValidator("SCRIPT_EXECUTOR", ["native", "quickjs"]),
|
|
420
|
+
...enumValidator("TRANSCRIPTION_PROVIDER", ["openai", "elevenlabs", "groq"]),
|
|
402
421
|
SCRIPT_QUICKJS_POOL_SIZE: validateQuickJSPoolSize,
|
|
403
422
|
...booleanValidators(["SCRIPT_EGRESS_ALLOW_PRIVATE"]),
|
|
404
423
|
SCRIPT_EGRESS_ALLOWLIST: (value) =>
|
|
@@ -407,6 +426,7 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
|
|
|
407
426
|
// model's weekly window (Fable/Opus/Sonnet). fallback: legacy random pick.
|
|
408
427
|
...enumValidator("MODEL_WINDOW_EXHAUSTED_POLICY", ["fail", "fallback"]),
|
|
409
428
|
...enumValidator("HEARTBEAT_CHECKLIST_GATE_MODE", ["shadow", "enforce"]),
|
|
429
|
+
...enumValidator("SYSTEM_ONE_DEFAULT_PROVIDER", [...SYSTEM_ONE_DEFAULT_PROVIDER_OPTIONS]),
|
|
410
430
|
// Codex credits-exhausted cooldown (ms). Permissive on range here (positive
|
|
411
431
|
// integer) — the worker clamps to [5m, 7d] via resolveCodexCreditsExhaustedCooldownMs.
|
|
412
432
|
CODEX_CREDITS_EXHAUSTED_COOLDOWN_MS: (value) => {
|
|
@@ -479,6 +499,7 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
|
|
|
479
499
|
"GITHUB_DISABLE",
|
|
480
500
|
"GITLAB_DISABLE",
|
|
481
501
|
"AZURE_DEVOPS_DISABLE",
|
|
502
|
+
"BITBUCKET_DISABLE",
|
|
482
503
|
"LINEAR_DISABLE",
|
|
483
504
|
"JIRA_DISABLE",
|
|
484
505
|
"AGENTMAIL_DISABLE",
|
|
@@ -582,6 +603,9 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
|
|
|
582
603
|
// default in thread-buffer.ts; above 10 minutes a
|
|
583
604
|
// reply waits longer than any operator expects.
|
|
584
605
|
...boundedIntegerValidators(["CHAT_SDK_BUFFER_MS"], 1, 600_000),
|
|
606
|
+
// Minutes a Chat SDK thread keeps its owner after the owner's task ends.
|
|
607
|
+
// 0 keeps it only while the task is open; a day is the longest useful window.
|
|
608
|
+
...boundedIntegerValidators(["CHAT_SDK_OWNER_WINDOW_MIN"], 0, 1440),
|
|
585
609
|
// Below ~100 tokens the preamble can't fit a useful summary; above 20000
|
|
586
610
|
// (~80k chars) it risks the SIGTERM-143 context-saturation failure mode
|
|
587
611
|
// the cap exists to prevent (see context-preamble.ts).
|
|
@@ -68,3 +68,33 @@ export async function recordTaskAttachmentUpload(
|
|
|
68
68
|
throw error;
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
// Resolve against the row's ACTUAL stored key + its own org/drive, rather than
|
|
73
|
+
// reconstructing `tasks/<taskId>/<name>`. Attachments written to agent-fs via the
|
|
74
|
+
// CLI live at arbitrary paths (`misc/…`, `smoke/…`); reconstruction mis-resolves
|
|
75
|
+
// them, which previously caused downloads to 404 and deletes to orphan the real
|
|
76
|
+
// file. New uploads have no stored key yet at scope-build time and keep the
|
|
77
|
+
// forward-looking layout.
|
|
78
|
+
export function scopeFromAttachment(attachment: TaskAttachment): FileScope {
|
|
79
|
+
const storedKey = attachment.providerKey ?? attachment.path ?? "";
|
|
80
|
+
return {
|
|
81
|
+
taskId: attachment.taskId,
|
|
82
|
+
name: attachment.name,
|
|
83
|
+
key: storedKey.trim() ? storedKey : undefined,
|
|
84
|
+
orgId: attachment.orgId,
|
|
85
|
+
driveId: attachment.driveId,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Which file-storage provider (if any) actually holds this attachment's bytes.
|
|
90
|
+
// Only agent-fs objects and local-fs uploads are provider-backed; `shared-fs`
|
|
91
|
+
// worker-volume pointers, `url`, and `page` attachments have no provider object,
|
|
92
|
+
// so they must never be fetched or deleted through a provider (doing so would
|
|
93
|
+
// mis-resolve and, on delete, orphan the real target). Note local-fs uploads are
|
|
94
|
+
// recorded with kind "shared-fs" but an explicit local-fs providerId, so key off
|
|
95
|
+
// both signals rather than kind alone.
|
|
96
|
+
export function backingProviderId(attachment: TaskAttachment): string | null {
|
|
97
|
+
if (attachment.kind === "agent-fs") return attachment.providerId ?? "agent-fs";
|
|
98
|
+
if (attachment.providerId === "local-fs") return "local-fs";
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bitbucket Cloud REST interactions — resolving the bot identity behind the
|
|
3
|
+
* token and posting pull request comments. Uses raw fetch, no SDK.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
7
|
+
import { getBitbucketToken } from "./auth";
|
|
8
|
+
|
|
9
|
+
export const BITBUCKET_API = "https://api.bitbucket.org/2.0";
|
|
10
|
+
|
|
11
|
+
function headers(): Record<string, string> {
|
|
12
|
+
const token = getBitbucketToken();
|
|
13
|
+
if (!token) throw new Error("[Bitbucket] No API token configured");
|
|
14
|
+
return {
|
|
15
|
+
Authorization: `Bearer ${token}`,
|
|
16
|
+
"Content-Type": "application/json",
|
|
17
|
+
Accept: "application/json",
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let cachedBotAccountId: string | null = null;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Atlassian `account_id` of the bot. A picker @mention is stored as
|
|
25
|
+
* `@{<account_id>}` in the comment's raw content, so this is what mention
|
|
26
|
+
* detection matches. Uses BITBUCKET_BOT_ACCOUNT_ID when set, otherwise the
|
|
27
|
+
* user that owns the token (`GET /2.0/user`), cached. Returns null when
|
|
28
|
+
* neither is available; callers then fall back to plain-text `@name` matching.
|
|
29
|
+
*/
|
|
30
|
+
export async function resolveBitbucketBotAccountId(): Promise<string | null> {
|
|
31
|
+
const fromEnv = process.env.BITBUCKET_BOT_ACCOUNT_ID?.trim();
|
|
32
|
+
if (fromEnv) return fromEnv;
|
|
33
|
+
if (cachedBotAccountId) return cachedBotAccountId;
|
|
34
|
+
if (!getBitbucketToken()) return null;
|
|
35
|
+
try {
|
|
36
|
+
// Redirects are refused so the token never follows a response to another host.
|
|
37
|
+
const resp = await fetch(`${BITBUCKET_API}/user`, { headers: headers(), redirect: "error" });
|
|
38
|
+
if (!resp.ok) {
|
|
39
|
+
console.error(`[Bitbucket] GET /user returned ${resp.status}`);
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const data = (await resp.json()) as { account_id?: string };
|
|
43
|
+
cachedBotAccountId = data.account_id?.trim() || null;
|
|
44
|
+
return cachedBotAccountId;
|
|
45
|
+
} catch (err) {
|
|
46
|
+
console.error(`[Bitbucket] Failed to resolve bot identity: ${scrubSecrets(String(err))}`);
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Reset the cached bot identity (tests, config reloads). */
|
|
52
|
+
export function resetBitbucketBotCache(): void {
|
|
53
|
+
cachedBotAccountId = null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Post a comment on a pull request. `fullName` is `<workspace>/<repo_slug>`. */
|
|
57
|
+
export async function postBitbucketPullRequestComment(
|
|
58
|
+
fullName: string,
|
|
59
|
+
pullRequestId: number,
|
|
60
|
+
raw: string,
|
|
61
|
+
): Promise<void> {
|
|
62
|
+
const [workspace, repoSlug] = fullName.split("/");
|
|
63
|
+
if (!workspace || !repoSlug) throw new Error(`[Bitbucket] Invalid repository: ${fullName}`);
|
|
64
|
+
const url =
|
|
65
|
+
`${BITBUCKET_API}/repositories/${encodeURIComponent(workspace)}/` +
|
|
66
|
+
`${encodeURIComponent(repoSlug)}/pullrequests/${pullRequestId}/comments`;
|
|
67
|
+
const resp = await fetch(url, {
|
|
68
|
+
method: "POST",
|
|
69
|
+
headers: headers(),
|
|
70
|
+
redirect: "error",
|
|
71
|
+
body: JSON.stringify({ content: { raw } }),
|
|
72
|
+
});
|
|
73
|
+
if (!resp.ok) {
|
|
74
|
+
const text = await resp.text().catch(() => "");
|
|
75
|
+
throw new Error(
|
|
76
|
+
scrubSecrets(
|
|
77
|
+
`[Bitbucket] Comment on ${fullName}#${pullRequestId} failed: ${resp.status} ${text}`,
|
|
78
|
+
),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { isEnvFlagEnabled } from "../utils/env-flag";
|
|
2
|
+
import { verifyHmacSignature } from "../workflows/triggers";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Bitbucket Cloud Authentication & Webhook Verification
|
|
6
|
+
*
|
|
7
|
+
* Webhooks are signed with the webhook's secret: `X-Hub-Signature` carries
|
|
8
|
+
* `sha256=<hex HMAC-SHA256 of the raw body>`, checked against
|
|
9
|
+
* BITBUCKET_WEBHOOK_SECRET. API calls send BITBUCKET_TOKEN (an Atlassian API
|
|
10
|
+
* token or a workspace access token) as a Bearer token.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
let initialized = false;
|
|
14
|
+
let webhookSecret: string | null = null;
|
|
15
|
+
let apiToken: string | null = null;
|
|
16
|
+
|
|
17
|
+
export function isBitbucketEnabled(): boolean {
|
|
18
|
+
return !!process.env.BITBUCKET_WEBHOOK_SECRET && !isEnvFlagEnabled("BITBUCKET_DISABLE", false);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function initBitbucket(): void {
|
|
22
|
+
if (initialized) return;
|
|
23
|
+
if (!isBitbucketEnabled()) {
|
|
24
|
+
console.log("[Bitbucket] Integration disabled (BITBUCKET_WEBHOOK_SECRET not set)");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
webhookSecret = process.env.BITBUCKET_WEBHOOK_SECRET!;
|
|
29
|
+
apiToken = process.env.BITBUCKET_TOKEN ?? null;
|
|
30
|
+
initialized = true;
|
|
31
|
+
|
|
32
|
+
console.log(`[Bitbucket] Integration initialized (API token: ${apiToken ? "set" : "not set"})`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Verify a Bitbucket webhook delivery from its raw body and `X-Hub-Signature`
|
|
37
|
+
* header. Only the `sha256=<hex>` form Bitbucket sends is accepted.
|
|
38
|
+
*/
|
|
39
|
+
export function verifyBitbucketWebhook(rawBody: string, signatureHeader: string | undefined) {
|
|
40
|
+
if (!webhookSecret) return false;
|
|
41
|
+
if (!signatureHeader?.startsWith("sha256=")) return false;
|
|
42
|
+
return verifyHmacSignature(webhookSecret, rawBody, signatureHeader);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Get the Bitbucket token for API calls. */
|
|
46
|
+
export function getBitbucketToken(): string | null {
|
|
47
|
+
return apiToken;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Bot name for plain-text `@name` mentions. Read on every call: a stored
|
|
52
|
+
* swarm_config value reaches process.env after this module loads, and again
|
|
53
|
+
* on each config reload.
|
|
54
|
+
*/
|
|
55
|
+
export function getBitbucketBotName(): string {
|
|
56
|
+
return process.env.BITBUCKET_BOT_NAME || "agent-swarm-bot";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Reset state for testing and config reloads. */
|
|
60
|
+
export function resetBitbucket(): void {
|
|
61
|
+
initialized = false;
|
|
62
|
+
webhookSecret = null;
|
|
63
|
+
apiToken = null;
|
|
64
|
+
}
|