@desplega.ai/agent-swarm 2.7.4 → 2.7.6
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 +1 -1
- package/dist/acp-adapter-bp91d37f.js +15 -0
- package/dist/{actions-gfrr9vb7.js → actions-yj0eeaqh.js} +19 -18
- package/dist/{amp-adapter-0s58gm46.js → amp-adapter-4pg9h34q.js} +3 -3
- package/dist/{app-stvq692g.js → app-909e97cr.js} +3 -3
- package/dist/{assistant-5s37fr9n.js → assistant-qdyb5fyy.js} +32 -23
- package/dist/{boot-reembed-yzy1841n.js → boot-reembed-fk0s9z18.js} +1 -1
- package/dist/{boot-reembed-cv82gz7y.js → boot-reembed-gs1rzrnh.js} +1 -1
- package/dist/{boot-scrub-sweep-8k4q95ap.js → boot-scrub-sweep-4qfd4kfx.js} +4 -4
- package/dist/{boot-seeds-ftt9htah.js → boot-seeds-yr00k13d.js} +4 -4
- package/dist/{boxd-515v1t8z.js → boxd-vdmfssd9.js} +5 -5
- package/dist/{claude-adapter-t00wrzeh.js → claude-adapter-qta1f1xn.js} +7 -7
- package/dist/{claude-managed-adapter-g6vmjccb.js → claude-managed-adapter-dvs2xw5n.js} +2 -2
- package/dist/{claude-managed-setup-afpa82f7.js → claude-managed-setup-sc6j4dm0.js} +3 -3
- package/dist/{claude-sdk-session-vj0c2azx.js → claude-sdk-session-zgrz6yvc.js} +9175 -9076
- package/dist/{cli-gjr2y2xk.js → cli-0agcv1v5.js} +4 -1
- package/dist/{cli-edmm9d9y.js → cli-0at8w34w.js} +7 -7
- package/dist/{cli-a81qevyg.js → cli-0qatpy8q.js} +20 -36
- package/dist/{cli-bcz074zx.js → cli-0yv5z2pg.js} +1 -1
- package/dist/{cli-a4btqqft.js → cli-1b7btd38.js} +1 -1
- package/dist/{cli-a2jw6ee5.js → cli-1p89xny0.js} +1 -1
- package/dist/{cli-v726afdm.js → cli-2pnswgh8.js} +1 -1
- package/dist/{cli-jkt52mgt.js → cli-386tyxxg.js} +2 -2
- package/dist/{cli-az0bpf8p.js → cli-3ypk9vk1.js} +3 -3
- package/dist/{cli-wk9md5px.js → cli-436532ck.js} +4 -4
- package/dist/{cli-7n5sbw0z.js → cli-4necwtsv.js} +5 -5
- package/dist/cli-54t00gt8.js +151 -0
- package/dist/{cli-655xm9ms.js → cli-583frbyr.js} +1 -1
- package/dist/{cli-4r12t6w6.js → cli-5c4hjzne.js} +101 -4
- package/dist/{cli-hf6zt5m6.js → cli-69szn7w7.js} +17 -1
- package/dist/{cli-rtk9g7av.js → cli-73jzwksr.js} +6 -2
- package/dist/{cli-jg0nfda6.js → cli-75gkpjvw.js} +52 -21
- package/dist/{cli-wcmnan2g.js → cli-89knwags.js} +5 -5
- package/dist/{cli-pwyevs29.js → cli-8bc1w0tw.js} +22 -21
- package/dist/{cli-t282jgab.js → cli-8snvgs5r.js} +41 -16
- package/dist/{cli-vqgnyh76.js → cli-98rw5dt5.js} +15 -3
- package/dist/{cli-9cctx1tn.js → cli-a22h291q.js} +1 -1
- package/dist/{cli-ycm6180s.js → cli-a7v854rf.js} +5 -3
- package/dist/{cli-98pke4vk.js → cli-ae7mrdf9.js} +2 -2
- package/dist/{cli-je8yeyc5.js → cli-bjb3z51z.js} +5 -5
- package/dist/{cli-mc97xy2s.js → cli-c60vetmm.js} +16 -4
- package/dist/{cli-j4kw3rsx.js → cli-cm5yqn35.js} +18 -0
- package/dist/{cli-wndv9p7g.js → cli-decfb6v8.js} +6 -6
- package/dist/{cli-h55y2bjc.js → cli-dhar9qay.js} +2 -2
- package/dist/{cli-8yy525ad.js → cli-drr5v6av.js} +2 -2
- package/dist/{cli-5gr1g711.js → cli-dxae2yg7.js} +2 -2
- package/dist/{cli-pe09p6dv.js → cli-e3r2neck.js} +8 -7
- package/dist/{cli-13jvmew3.js → cli-ekwks2vs.js} +7 -7
- package/dist/{cli-jg4p5t36.js → cli-es6va8jk.js} +121 -51
- package/dist/{cli-j4kt1mpb.js → cli-faxe6thg.js} +3 -3
- package/dist/{cli-tzrpv5sc.js → cli-ftyxyspd.js} +1 -1
- package/dist/cli-fw2w0kva.js +47 -0
- package/dist/{cli-2dfbv8qr.js → cli-fy6x31ps.js} +1 -0
- package/dist/{cli-aq58fk5m.js → cli-g83s9ewh.js} +7 -1
- package/dist/cli-gdhpcbhf.js +55 -0
- package/dist/{cli-82jg7fp9.js → cli-he5nxaa8.js} +1 -1
- package/dist/{cli-8veytvbh.js → cli-he9wchme.js} +1 -1
- package/dist/{cli-k76tgq03.js → cli-hgaazk69.js} +2 -2
- package/dist/{cli-d2pzqej1.js → cli-hr2e2fft.js} +741 -7
- package/dist/{cli-dae4sbgy.js → cli-jm2vfpfd.js} +2 -2
- package/dist/{cli-1s1dqn1j.js → cli-k2gzwsj2.js} +297 -140
- package/dist/{cli-b1afgtj3.js → cli-kqdg1bsb.js} +3 -3
- package/dist/cli-m2z8jagn.js +12078 -0
- package/dist/{cli-mvckssyq.js → cli-m48g6p95.js} +5 -5
- package/dist/{cli-bwty35gx.js → cli-mthwvjre.js} +1 -1
- package/dist/{cli-41z8xwj0.js → cli-nhrvpaj7.js} +34 -8
- package/dist/{cli-ahd90bwt.js → cli-nwj0ee2w.js} +6 -6
- package/dist/{cli-kvtc7aez.js → cli-p5gp8pcx.js} +44 -14
- package/dist/{cli-avgcz4ra.js → cli-pd7vagab.js} +436 -171
- package/dist/{cli-zn15c46z.js → cli-q0k1w5zd.js} +1 -1
- package/dist/{cli-bq2pcphn.js → cli-rmmmrjkd.js} +1 -1
- package/dist/{cli-2qpvmmkw.js → cli-s0qnd4wv.js} +1 -1
- package/dist/{cli-kg8hchyg.js → cli-sa02jsqt.js} +1 -1
- package/dist/{cli-1yekbyqm.js → cli-shpbx5ee.js} +283 -82
- package/dist/{cli-9y354g3j.js → cli-srye3vdt.js} +1 -1
- package/dist/{cli-byqfbnjw.js → cli-svj5090z.js} +9 -9
- package/dist/{cli-gcbdjt47.js → cli-vfym7wmz.js} +6 -6
- package/dist/{cli-v1yw3wha.js → cli-vvpx3gsx.js} +4 -4
- package/dist/{cli-a1fhj4n9.js → cli-w6dt9ged.js} +2 -2
- package/dist/{cli-1gm8jcyr.js → cli-waqbxg08.js} +5232 -68
- package/dist/{cli-5hjced2v.js → cli-wp9sp1qb.js} +6 -6
- package/dist/{cli-eny54typ.js → cli-wx3rhnm0.js} +1 -1
- package/dist/{cli-s0exq74r.js → cli-xbbndvjy.js} +3 -3
- package/dist/{cli-f255yaqn.js → cli-xds5f77d.js} +3 -3
- package/dist/{cli-0sbkss7h.js → cli-xnqgpshy.js} +2 -2
- package/dist/{cli-0fgr1w9b.js → cli-xnrwazav.js} +20 -3
- package/dist/{cli-gf5hbmdr.js → cli-yjdgea32.js} +3 -3
- package/dist/{cli-cq1pf336.js → cli-yn4kxs06.js} +124 -11
- package/dist/{cli-224epwwf.js → cli-yx4kfngt.js} +3 -3
- package/dist/{cli-eqx6k993.js → cli-yy3rhsdw.js} +47 -13
- package/dist/{cli-36g23z21.js → cli-zbbwez0r.js} +4 -4
- package/dist/{cli-ktmvb20s.js → cli-zm72anhg.js} +12 -12
- package/dist/cli.js +26 -25
- package/dist/{codex-adapter-g0gctw29.js → codex-adapter-ayd1fb6h.js} +5 -5
- package/dist/{codex-hook-vrzz2xhr.js → codex-hook-9efm2x1m.js} +1 -1
- package/dist/{codex-login-0gg46ygn.js → codex-login-y0pmff1c.js} +1 -1
- package/dist/{codex-session-runner-dr2cxe1c.js → codex-session-runner-jesa32xc.js} +6 -6
- package/dist/{commands-1p07cvff.js → commands-hanewgf2.js} +9 -3
- package/dist/{controller-z0s7sv6r.js → controller-dt1n3c1q.js} +9 -4
- package/dist/{controllers-connect-x9kvvhyk.js → controllers-connect-xx8x64n5.js} +22 -21
- package/dist/{cursor-adapter-m5b98pvk.js → cursor-adapter-9s4dwwdw.js} +2 -2
- package/dist/{db-capk2raj.js → db-3vttcbv4.js} +3 -3
- package/dist/{db-rsh04kh3.js → db-9dv0g1ab.js} +12 -4
- package/dist/{devin-adapter-yxm87fe9.js → devin-adapter-r9phbnc9.js} +1 -1
- package/dist/{devtools-82fzqra8.js → devtools-qxfgbsqz.js} +1 -1
- package/dist/{dispatcher-304q6fwa.js → dispatcher-tx9hd1sv.js} +18 -17
- package/dist/{docker-7rxt0rry.js → docker-c1a3gqmf.js} +91 -24
- package/dist/{dsh-adapter-btewem0y.js → dsh-adapter-007y4ejs.js} +2 -2
- package/dist/{e2b-renc0k8q.js → e2b-a19d28rz.js} +4 -4
- package/dist/{e2b-7fc9b9bd.js → e2b-we6hs6q5.js} +6 -6
- package/dist/{easter-egg-3d-fm1p3n4d.js → easter-egg-3d-rgbrqb8s.js} +1 -1
- package/dist/{embedding-space-s68v49ct.js → embedding-space-ny1e2940.js} +6 -6
- package/dist/{embeddings-4ms3604w.js → embeddings-2t386t13.js} +1 -1
- package/dist/{event-bus-dfjx2qdh.js → event-bus-g0ka3pbw.js} +1 -1
- package/dist/{execute-mcdgccjs.js → execute-pksh0ceh.js} +1 -1
- package/dist/{external-channel-9dxwfztn.js → external-channel-phgmhmd7.js} +2 -2
- package/dist/fx-adapter-fy8rr921.js +18 -0
- package/dist/{google-generative-ai-yvmdxyxa.js → google-generative-ai-8y3m3k4b.js} +1 -1
- package/dist/{google-vertex-jrtdh9gs.js → google-vertex-w3v98n7b.js} +1 -1
- package/dist/{grok-adapter-05ecv4h0.js → grok-adapter-q0abvpaj.js} +8 -8
- package/dist/handlers-by3sfxyx.js +32 -0
- package/dist/{heartbeat-3n94heh3.js → heartbeat-kadyek8p.js} +21 -20
- package/dist/{hook-aadrn69x.js → hook-5zs3msf6.js} +6 -6
- package/dist/{hook-zxwje86c.js → hook-9k551xnj.js} +4 -4
- package/dist/{http-xzxk9mst.js → http-mhz9vw1f.js} +272 -103
- package/dist/index-4f2pvxae.js +37 -0
- package/dist/index-98gz7tmp.js +30 -0
- package/dist/{index-5rk6rfzr.js → index-fvzke5kx.js} +1 -1
- package/dist/index-k08z2cak.js +33 -0
- package/dist/{index-yqhz88yy.js → index-pxn30mse.js} +8 -8
- package/dist/{index-d34f6r87.js → index-q361cg2f.js} +1 -1
- package/dist/{index-9j4zwcht.js → index-rvr00sg7.js} +168 -675
- package/dist/{index-qeb82z37.js → index-vdygbq10.js} +1 -1
- package/dist/{keepalive-w4aakbyk.js → keepalive-w56f32cg.js} +4 -4
- package/dist/lead-enq35nqp.js +70 -0
- package/dist/{maintenance-hbptpk6p.js → maintenance-7mdgxcea.js} +1 -1
- package/dist/{oauth-refresh-sweep-217semrv.js → oauth-refresh-sweep-29txy9vk.js} +3 -3
- package/dist/{onboard-vz2s5df3.js → onboard-kpsrkarj.js} +5 -5
- package/dist/{opencode-adapter-gtfd7d7v.js → opencode-adapter-j25vm3rr.js} +5 -5
- package/dist/{otel-impl-aefave37.js → otel-impl-6kkct7a8.js} +2 -2
- package/dist/{pi-mono-adapter-f8h5yseh.js → pi-mono-adapter-exhkzhn2.js} +117 -23
- package/dist/{pricing-refresh-m1yhh9ya.js → pricing-refresh-n2x7jh6k.js} +3 -3
- package/dist/{rbac-roles-rrw8twdz.js → rbac-roles-81ypx6es.js} +1 -1
- package/dist/{rbac-roles-svj72cdf.js → rbac-roles-b5htkjby.js} +2 -2
- package/dist/{render-v2-amwbnfxh.js → render-v2-qn6jmpk4.js} +3 -3
- package/dist/{resume-9cd6wf6z.js → resume-wd30wx23.js} +7 -7
- package/dist/{runtime-fp4vrx7m.js → runtime-4hsg7h60.js} +1 -1
- package/dist/{session-access-21t6yjyg.js → session-access-5qvfevhk.js} +4 -4
- package/dist/{session-source-backfill-pxbzcsc6.js → session-source-backfill-n1czavbr.js} +3 -3
- package/dist/{setup-4592tqz4.js → setup-dnb56qy5.js} +3 -3
- package/dist/{virtual-modules-gzfnpmnz.js → virtual-modules-76xxv9fs.js} +2 -2
- package/dist/{work-objects-t9tcf174.js → work-objects-4n21tfkm.js} +21 -20
- package/dist/{worker-follow-up-q32jh4yk.js → worker-follow-up-77hvzxww.js} +18 -17
- package/dist/worker-gg1576j3.js +70 -0
- package/dist/{x-b6wxntdt.js → x-vmmye1kb.js} +2 -2
- package/openapi.json +189 -32
- package/package.json +8 -7
- package/src/be/db/script-runs.ts +23 -0
- package/src/be/db/session-summaries.ts +8 -2
- package/src/be/db/session-tree.ts +174 -61
- package/src/be/db/session-visibility.ts +5 -4
- package/src/be/db/tasks/read.ts +122 -3
- package/src/be/db-client.ts +8 -5
- package/src/be/db.ts +23 -8
- package/src/be/memory/providers/sqlite-store.ts +49 -12
- package/src/be/migrations/259_session_summaries_open_tasks.sql +111 -0
- package/src/be/migrations/260_runtime_pool_cache.sql +20 -0
- package/src/be/migrations/262_session_visibility_indexes.sql +18 -0
- package/src/be/migrations/263_task_list_filter_index.sql +11 -0
- package/src/be/migrations/264_slack_thread_cover_index.sql +9 -0
- package/src/be/migrations/266_script_runs_source_task.sql +11 -0
- package/src/be/model-validation.ts +5 -1
- package/src/be/pricing-normalize.ts +7 -2
- package/src/be/runtime-pools/cache-cleanup.ts +109 -0
- package/src/be/runtime-pools/db.ts +65 -6
- package/src/be/runtime-pools/remote-controller.ts +5 -0
- package/src/be/runtime-pools/supervisor.ts +33 -2
- package/src/be/scripts/typecheck.ts +23 -0
- package/src/be/seed/schedules-seeder.ts +135 -9
- package/src/be/seed/workflows-seeder.ts +65 -1
- package/src/be/seed-pricing.ts +23 -0
- package/src/be/seed-scripts/catalog/task-failure-audit.ts +5 -1
- package/src/be/steering.ts +50 -3
- package/src/be/swarm-config-guard.ts +1 -0
- package/src/commands/controller.ts +4 -0
- package/src/commands/entrypoint-api-readiness.ts +74 -0
- package/src/commands/provider-credentials.ts +29 -2
- package/src/commands/runner.ts +86 -5
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +27 -1
- package/src/extensions/dispatcher.ts +4 -0
- package/src/http/agents.ts +1 -0
- package/src/http/index.ts +1 -1
- package/src/http/list-total-cache.ts +14 -7
- package/src/http/mcp-bridge.ts +4 -0
- package/src/http/memory.ts +38 -9
- package/src/http/script-runs.ts +174 -5
- package/src/http/status.ts +10 -2
- package/src/http/tasks.ts +4 -2
- package/src/integrations/chat-sdk/inbound.ts +15 -5
- package/src/integrations/chat-sdk/steering.ts +3 -1
- package/src/kv-reserved-namespaces.ts +2 -0
- package/src/prompts/base-prompt.ts +4 -1
- package/src/prompts/session-templates.ts +26 -0
- package/src/providers/acp-adapter.ts +190 -26
- package/src/providers/acp-targets.ts +306 -6
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +9 -44
- package/src/providers/codex-oauth/flow.ts +24 -3
- package/src/providers/codex-oauth/storage.ts +56 -17
- package/src/providers/codex-oauth/subscription.ts +67 -0
- package/src/providers/fx-adapter.ts +211 -0
- package/src/providers/index.ts +5 -1
- package/src/providers/mcp-loopback-proxy.ts +94 -0
- package/src/providers/pi-mono-adapter.ts +98 -13
- package/src/providers/pricing-sources.md +8 -0
- package/src/providers/reasoning-effort.ts +17 -1
- package/src/providers/types.ts +2 -1
- package/src/runtime-providers/controller-protocol.ts +14 -1
- package/src/runtime-providers/controller.ts +5 -0
- package/src/runtime-providers/docker.ts +108 -23
- package/src/runtime-providers/types.ts +16 -0
- package/src/scheduler/scheduler.ts +36 -3
- package/src/script-workflows/workflow-ctx.ts +8 -0
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/types/stdlib.d.ts +27 -0
- package/src/scripts-runtime/types/swarm-extension.d.ts +33 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -0
- package/src/server-user.ts +1 -0
- package/src/server.ts +7 -0
- package/src/slack/assistant.ts +17 -1
- package/src/slack/commands.ts +5 -1
- package/src/slack/handlers.ts +53 -5
- package/src/slack/left-threads.ts +88 -0
- package/src/slack/steering.ts +3 -0
- package/src/slack/thread-buffer.ts +40 -16
- package/src/tasks/follow-up-redelegation-guard.ts +155 -0
- package/src/tests/acp-adapter.test.ts +79 -0
- package/src/tests/base-prompt.test.ts +12 -0
- package/src/tests/chat-sdk-inbound.test.ts +30 -0
- package/src/tests/chatgpt-subscription-model-groups.test.ts +62 -0
- package/src/tests/chatgpt-subscription-pi.test.ts +170 -0
- package/src/tests/chatgpt-subscription-session-bench.test.ts +198 -0
- package/src/tests/claude-artifacts-read-script.test.ts +209 -0
- package/src/tests/codex-oauth-credential-info.test.ts +40 -0
- package/src/tests/codex-oauth-refresh-egress.test.ts +194 -0
- package/src/tests/context-window.test.ts +7 -0
- package/src/tests/db-client.test.ts +21 -0
- package/src/tests/db-task-read-contract.test.ts +8 -2
- package/src/tests/db-task-write-contract.test.ts +5 -1
- package/src/tests/entrypoint-api-readiness.test.ts +416 -210
- package/src/tests/explicit-model-validation.test.ts +113 -8
- package/src/tests/extensions-run-context.test.ts +11 -0
- package/src/tests/extensions-steering.test.ts +353 -0
- package/src/tests/extensions-validate.test.ts +5 -0
- package/src/tests/fixtures/extensions/steering-policy/hooks.ts +39 -0
- package/src/tests/fixtures/extensions/steering-policy/manifest.json +7 -0
- package/src/tests/fixtures/fake-fx.ts +167 -0
- package/src/tests/fixtures/fx/acp-responses-recorded.json +157 -0
- package/src/tests/fixtures/fx/acp-updates-recorded.jsonl +15 -0
- package/src/tests/follow-up-redelivery-guard.test.ts +432 -202
- package/src/tests/fx-adapter.test.ts +771 -0
- package/src/tests/grok-adapter.test.ts +36 -0
- package/src/tests/list-total-cache.test.ts +27 -2
- package/src/tests/mcp-loopback-proxy.test.ts +193 -0
- package/src/tests/memory-fts-populate.test.ts +11 -9
- package/src/tests/memory-retrieval-purge.test.ts +80 -0
- package/src/tests/migration-260-runtime-pool-cache.test.ts +118 -0
- package/src/tests/model-tier-resolution.test.ts +3 -3
- package/src/tests/pricing-refresh.test.ts +5 -5
- package/src/tests/prompt-template-session.test.ts +5 -3
- package/src/tests/provider-registration.test.ts +2 -1
- package/src/tests/query-audit-plans.test.ts +30 -2
- package/src/tests/runtime-cache-cleanup.test.ts +367 -0
- package/src/tests/runtime-e2b-pools.test.ts +41 -0
- package/src/tests/runtime-pools-db.test.ts +98 -0
- package/src/tests/runtime-pools-http.test.ts +29 -0
- package/src/tests/runtime-providers-docker.test.ts +253 -0
- package/src/tests/runtime-remote-controller.test.ts +66 -2
- package/src/tests/runtime-supervisor.test.ts +66 -2
- package/src/tests/script-connections.test.ts +13 -0
- package/src/tests/script-runs-http.test.ts +684 -2
- package/src/tests/seed-model-profiles.test.ts +2 -2
- package/src/tests/seed-schedules.test.ts +138 -0
- package/src/tests/seed-scripts.test.ts +26 -0
- package/src/tests/session-cost-recompute-unit.test.ts +39 -0
- package/src/tests/session-costs-golden.test.ts +26 -0
- package/src/tests/session-summaries-plans.test.ts +120 -0
- package/src/tests/session-summaries.test.ts +72 -12
- package/src/tests/sessions-tree.test.ts +11 -3
- package/src/tests/slack-leave-thread.test.ts +571 -0
- package/src/tests/slack-render-v2-query-plans.test.ts +24 -2
- package/src/tests/status.test.ts +11 -0
- package/src/tests/tasks-readable-row.test.ts +155 -0
- package/src/tests/tool-annotations.test.ts +2 -1
- package/src/tests/workflow-engine-v2.test.ts +42 -0
- package/src/tests/workflow-script-principal.test.ts +207 -7
- package/src/tools/send-task.ts +35 -47
- package/src/tools/slack-leave-thread.ts +104 -0
- package/src/tools/steer-task.ts +3 -2
- package/src/tools/task-dedup.ts +45 -21
- package/src/tools/tool-config.ts +2 -1
- package/src/types.ts +57 -0
- package/src/utils/codex-auth-failure.ts +30 -0
- package/src/utils/context-window.ts +4 -1
- package/src/utils/credentials.ts +7 -0
- package/src/utils/fx-models.ts +43 -0
- package/src/workflows/cooldown.ts +3 -2
- package/src/workflows/engine.ts +24 -9
- package/templates/schedules/dreaming-daily/config.json +1 -1
- package/templates/schedules/dreaming-daily/content.md +6 -14
- package/templates/skills/composio-google-docs/content.md +10 -0
- package/templates/skills/script-workflows/content.md +19 -1
- package/templates/skills/swarm-docs/content.md +2 -0
- package/templates/skills/swarm-scripts/SKILL.md +2 -0
- package/templates/skills/swarm-scripts/content.md +2 -0
- package/templates/workflows/dreaming/content.md +5 -1
- package/dist/acp-adapter-96dc64m2.js +0 -15
- package/dist/cli-dznrgaep.js +0 -30
- package/dist/cli-w3hcrxht.js +0 -6985
- package/dist/handlers-jg7v74ty.js +0 -31
- package/dist/index-8c1zbz59.js +0 -29
- package/dist/index-8k8tmfxd.js +0 -32
- package/dist/index-hq617xdj.js +0 -36
- package/dist/lead-567kw7x3.js +0 -68
- package/dist/worker-fjdqra6q.js +0 -68
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
createRuntimeProvider,
|
|
5
5
|
type RuntimeTargetConfig,
|
|
6
6
|
} from "../../runtime-providers";
|
|
7
|
+
import { poolCacheKey } from "../../runtime-providers/types";
|
|
7
8
|
import type { Agent, DeliveryMode, RuntimeController, RuntimePool } from "../../types";
|
|
8
9
|
import {
|
|
9
10
|
RuntimeControllerCreateSchema,
|
|
@@ -17,6 +18,7 @@ import { decryptSecret, encryptSecret, getEncryptionKey } from "../crypto";
|
|
|
17
18
|
import { createAgent, getAgentById, getAllAgents, getDbClient, upsertSwarmConfig } from "../db";
|
|
18
19
|
import { type AgentRow, rowToAgent } from "../db/agents";
|
|
19
20
|
import { registerStoredSecret } from "../secret-registry";
|
|
21
|
+
import { getPoolCache, queuePoolCacheRelease } from "./cache-cleanup";
|
|
20
22
|
import { RuntimePoolError } from "./errors";
|
|
21
23
|
import { revokeControllerTokens } from "./tokens";
|
|
22
24
|
|
|
@@ -29,10 +31,11 @@ type ControllerRow = Omit<
|
|
|
29
31
|
config: string;
|
|
30
32
|
secretConfig: string | null;
|
|
31
33
|
};
|
|
32
|
-
type PoolRow = Omit<RuntimePool, "placements" | "env" | "resources" | "enabled"> & {
|
|
34
|
+
type PoolRow = Omit<RuntimePool, "placements" | "env" | "resources" | "cache" | "enabled"> & {
|
|
33
35
|
placements: string;
|
|
34
36
|
env: string;
|
|
35
37
|
resources: string | null;
|
|
38
|
+
cache: string | null;
|
|
36
39
|
enabled: number;
|
|
37
40
|
};
|
|
38
41
|
const controllerColumns = `id, name, kind, placement, config, secret_config AS secretConfig,
|
|
@@ -40,7 +43,7 @@ const controllerColumns = `id, name, kind, placement, config, secret_config AS s
|
|
|
40
43
|
created_by AS createdBy, updated_by AS updatedBy, created_at AS createdAt, updated_at AS updatedAt`;
|
|
41
44
|
const poolColumns = `id, name, kind, placements, image, lifecycle, idle_minutes AS idleMinutes,
|
|
42
45
|
slots_per_runtime AS slotsPerRuntime, max_runtimes AS maxRuntimes, min_runtimes AS minRuntimes,
|
|
43
|
-
delivery, env, resources, ttl_sec AS ttlSec, resolved_image AS resolvedImage, enabled,
|
|
46
|
+
delivery, env, resources, cache, ttl_sec AS ttlSec, resolved_image AS resolvedImage, enabled,
|
|
44
47
|
consecutive_failures AS consecutiveFailures, circuit_open_until AS circuitOpenUntil,
|
|
45
48
|
last_error AS lastError, last_error_at AS lastErrorAt, created_by AS createdBy,
|
|
46
49
|
updated_by AS updatedBy, created_at AS createdAt, updated_at AS updatedAt`;
|
|
@@ -65,11 +68,13 @@ function rowToController(row: ControllerRow): RuntimeController {
|
|
|
65
68
|
};
|
|
66
69
|
}
|
|
67
70
|
function rowToPool(row: PoolRow): RuntimePool {
|
|
71
|
+
const cache = row.cache ? JSON.parse(row.cache) : null;
|
|
68
72
|
return {
|
|
69
73
|
...row,
|
|
70
74
|
placements: JSON.parse(row.placements),
|
|
71
75
|
env: JSON.parse(row.env),
|
|
72
76
|
resources: row.resources ? JSON.parse(row.resources) : null,
|
|
77
|
+
cache: cache ? { mountPath: cache.mountPath } : null,
|
|
73
78
|
enabled: row.enabled === 1,
|
|
74
79
|
};
|
|
75
80
|
}
|
|
@@ -121,6 +126,17 @@ export async function getRuntimeControllerTarget(
|
|
|
121
126
|
E2B_MAX_CONTINUOUS_SEC: env.E2B_MAX_CONTINUOUS_SEC,
|
|
122
127
|
RUNTIME_WORKER_API_URL: env.RUNTIME_WORKER_API_URL,
|
|
123
128
|
PUBLIC_MCP_BASE_URL: env.PUBLIC_MCP_BASE_URL,
|
|
129
|
+
...(controller.kind === "boxd" && controller.placement === "builtin"
|
|
130
|
+
? {
|
|
131
|
+
BOXD_ORG: env.BOXD_ORG,
|
|
132
|
+
BOXD_BASE_URL: env.BOXD_BASE_URL,
|
|
133
|
+
BOXD_SNAPSHOT: env.BOXD_SNAPSHOT,
|
|
134
|
+
BOXD_DELETE_AFTER_SEC: env.BOXD_DELETE_AFTER_SEC,
|
|
135
|
+
BOXD_START_TIMEOUT_SEC: env.BOXD_START_TIMEOUT_SEC,
|
|
136
|
+
BOXD_STATUS_TIMEOUT_SEC: env.BOXD_STATUS_TIMEOUT_SEC,
|
|
137
|
+
BOXD_TEARDOWN_TIMEOUT_SEC: env.BOXD_TEARDOWN_TIMEOUT_SEC,
|
|
138
|
+
}
|
|
139
|
+
: {}),
|
|
124
140
|
...controller.config,
|
|
125
141
|
...secrets,
|
|
126
142
|
};
|
|
@@ -234,11 +250,32 @@ export async function updateRuntimeController(
|
|
|
234
250
|
)
|
|
235
251
|
throw new RuntimePoolError("Controller name already exists", 409);
|
|
236
252
|
const pools = await listRuntimePools();
|
|
253
|
+
const oldConfig: Record<string, string> = JSON.parse(row.config);
|
|
254
|
+
const configChanged =
|
|
255
|
+
Object.keys(data.config).length !== Object.keys(oldConfig).length ||
|
|
256
|
+
Object.entries(data.config).some(([key, value]) => oldConfig[key] !== value);
|
|
257
|
+
const secrets = patch.secretConfig !== undefined ? controllerSecrets(row) : {};
|
|
258
|
+
const secretsChanged = Object.entries(patch.secretConfig ?? {}).some(([key, value]) =>
|
|
259
|
+
value === null ? key in secrets : secrets[key] !== value,
|
|
260
|
+
);
|
|
261
|
+
if (
|
|
262
|
+
(data.kind !== row.kind ||
|
|
263
|
+
data.placement !== row.placement ||
|
|
264
|
+
configChanged ||
|
|
265
|
+
secretsChanged) &&
|
|
266
|
+
(pools.some((pool) => pool.cache && pool.placements.includes(id)) ||
|
|
267
|
+
(await tx.get("SELECT id FROM runtime_cache_releases WHERE controller_id = ? LIMIT 1", [
|
|
268
|
+
id,
|
|
269
|
+
])))
|
|
270
|
+
)
|
|
271
|
+
throw new RuntimePoolError(
|
|
272
|
+
"Disable pool caches and wait for cleanup before changing controller configuration or placement",
|
|
273
|
+
409,
|
|
274
|
+
);
|
|
237
275
|
if (pools.some((pool) => pool.placements.includes(id) && pool.kind !== data.kind))
|
|
238
276
|
throw new RuntimePoolError("Pool kind must equal controller kind");
|
|
239
277
|
let encrypted = row.secretConfig;
|
|
240
278
|
if (patch.secretConfig !== undefined) {
|
|
241
|
-
const secrets = controllerSecrets(row);
|
|
242
279
|
for (const [key, value] of Object.entries(patch.secretConfig)) {
|
|
243
280
|
if (value === null) delete secrets[key];
|
|
244
281
|
else secrets[key] = value;
|
|
@@ -272,6 +309,8 @@ export async function deleteRuntimeController(id: string): Promise<void> {
|
|
|
272
309
|
if (!(await getRuntimeController(id))) throw new RuntimePoolError("Controller not found", 404);
|
|
273
310
|
if ((await listRuntimePools()).some((pool) => pool.placements.includes(id)))
|
|
274
311
|
throw new RuntimePoolError("Controller has runtime pools", 409);
|
|
312
|
+
if (await tx.get("SELECT id FROM runtime_cache_releases WHERE controller_id = ? LIMIT 1", [id]))
|
|
313
|
+
throw new RuntimePoolError("Controller has pending cache cleanup", 409);
|
|
275
314
|
await revokeControllerTokens(id);
|
|
276
315
|
await tx.run("DELETE FROM runtime_controllers WHERE id = ?", [id]);
|
|
277
316
|
});
|
|
@@ -291,6 +330,9 @@ export async function getRuntimePool(id: string): Promise<RuntimePool | null> {
|
|
|
291
330
|
}
|
|
292
331
|
async function validatePool(data: z.output<typeof RuntimePoolCreateSchema>): Promise<void> {
|
|
293
332
|
supportedKind(data.kind);
|
|
333
|
+
if (data.cache && !(await createRuntimeProvider(data.kind)).traits.persistentCache) {
|
|
334
|
+
throw new RuntimePoolError(`${data.kind} does not support persistent pool caches`);
|
|
335
|
+
}
|
|
294
336
|
const controller = await getRuntimeController(data.placements[0]!);
|
|
295
337
|
if (!controller) throw new RuntimePoolError("Controller not found");
|
|
296
338
|
if (data.kind !== controller.kind)
|
|
@@ -316,7 +358,7 @@ export async function createRuntimePool(
|
|
|
316
358
|
if (await tx.get("SELECT id FROM runtime_pools WHERE name = ? OR id = ?", [data.name, id]))
|
|
317
359
|
throw new RuntimePoolError("Pool already exists", 409);
|
|
318
360
|
await tx.run(
|
|
319
|
-
`INSERT INTO runtime_pools (id, name, kind, placements, image, lifecycle, idle_minutes, slots_per_runtime, max_runtimes, min_runtimes, delivery, env, resources, ttl_sec, enabled, created_by, updated_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
361
|
+
`INSERT INTO runtime_pools (id, name, kind, placements, image, lifecycle, idle_minutes, slots_per_runtime, max_runtimes, min_runtimes, delivery, env, resources, cache, ttl_sec, enabled, created_by, updated_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
320
362
|
[
|
|
321
363
|
id,
|
|
322
364
|
data.name,
|
|
@@ -331,6 +373,9 @@ export async function createRuntimePool(
|
|
|
331
373
|
data.delivery,
|
|
332
374
|
JSON.stringify(data.env),
|
|
333
375
|
data.resources ? JSON.stringify(data.resources) : null,
|
|
376
|
+
data.cache
|
|
377
|
+
? JSON.stringify({ ...data.cache, key: poolCacheKey(id, crypto.randomUUID()) })
|
|
378
|
+
: null,
|
|
334
379
|
data.ttlSec,
|
|
335
380
|
Number(data.enabled),
|
|
336
381
|
actor,
|
|
@@ -364,8 +409,19 @@ export async function updateRuntimePool(
|
|
|
364
409
|
await validatePool(data);
|
|
365
410
|
if (await tx.get("SELECT id FROM runtime_pools WHERE name = ? AND id != ?", [data.name, id]))
|
|
366
411
|
throw new RuntimePoolError("Pool name already exists", 409);
|
|
412
|
+
const cache = data.cache
|
|
413
|
+
? {
|
|
414
|
+
...data.cache,
|
|
415
|
+
key:
|
|
416
|
+
existing.cache && data.placements[0] === existing.placements[0]
|
|
417
|
+
? (await getPoolCache(id))!.key
|
|
418
|
+
: poolCacheKey(id, crypto.randomUUID()),
|
|
419
|
+
}
|
|
420
|
+
: null;
|
|
421
|
+
if (existing.cache && (!data.cache || data.placements[0] !== existing.placements[0]))
|
|
422
|
+
await queuePoolCacheRelease(existing, actor);
|
|
367
423
|
await tx.run(
|
|
368
|
-
`UPDATE runtime_pools SET name = ?, kind = ?, placements = ?, image = ?, resolved_image = ?, lifecycle = ?, idle_minutes = ?, slots_per_runtime = ?, max_runtimes = ?, min_runtimes = ?, delivery = ?, env = ?, resources = ?, ttl_sec = ?, enabled = ?, updated_by = COALESCE(?, updated_by), updated_at = ? WHERE id = ?`,
|
|
424
|
+
`UPDATE runtime_pools SET name = ?, kind = ?, placements = ?, image = ?, resolved_image = ?, lifecycle = ?, idle_minutes = ?, slots_per_runtime = ?, max_runtimes = ?, min_runtimes = ?, delivery = ?, env = ?, resources = ?, cache = ?, ttl_sec = ?, enabled = ?, updated_by = COALESCE(?, updated_by), updated_at = ? WHERE id = ?`,
|
|
369
425
|
[
|
|
370
426
|
data.name,
|
|
371
427
|
data.kind,
|
|
@@ -384,6 +440,7 @@ export async function updateRuntimePool(
|
|
|
384
440
|
data.delivery,
|
|
385
441
|
JSON.stringify(data.env),
|
|
386
442
|
data.resources ? JSON.stringify(data.resources) : null,
|
|
443
|
+
cache ? JSON.stringify(cache) : null,
|
|
387
444
|
data.ttlSec,
|
|
388
445
|
Number(data.enabled),
|
|
389
446
|
actor,
|
|
@@ -400,7 +457,8 @@ export async function deleteRuntimePool(
|
|
|
400
457
|
actor: string | null = null,
|
|
401
458
|
): Promise<void> {
|
|
402
459
|
await getDbClient().transaction(async (tx) => {
|
|
403
|
-
|
|
460
|
+
const pool = await getRuntimePool(id);
|
|
461
|
+
if (!pool) throw new RuntimePoolError("Pool not found", 404);
|
|
404
462
|
if (
|
|
405
463
|
await tx.get(
|
|
406
464
|
"SELECT id FROM runtime_instances WHERE pool_id = ? AND status != 'offline' LIMIT 1",
|
|
@@ -419,6 +477,7 @@ export async function deleteRuntimePool(
|
|
|
419
477
|
[new Date().toISOString(), actor, id],
|
|
420
478
|
);
|
|
421
479
|
}
|
|
480
|
+
await queuePoolCacheRelease(pool, actor);
|
|
422
481
|
await tx.run("DELETE FROM runtime_pools WHERE id = ?", [id]);
|
|
423
482
|
});
|
|
424
483
|
}
|
|
@@ -53,6 +53,7 @@ export class RemoteController implements ControllerCommands {
|
|
|
53
53
|
readonly renew?: ControllerCommands["renew"];
|
|
54
54
|
readonly pause?: ControllerCommands["pause"];
|
|
55
55
|
readonly resume?: ControllerCommands["resume"];
|
|
56
|
+
readonly releaseCache?: ControllerCommands["releaseCache"];
|
|
56
57
|
|
|
57
58
|
constructor(
|
|
58
59
|
readonly id: string,
|
|
@@ -61,6 +62,10 @@ export class RemoteController implements ControllerCommands {
|
|
|
61
62
|
readonly workerApiUrl?: string,
|
|
62
63
|
private readonly timeouts: { commandTimeoutMs?: number; provisionTimeoutMs?: number } = {},
|
|
63
64
|
) {
|
|
65
|
+
if (traits.persistentCache)
|
|
66
|
+
this.releaseCache = async (cache) => {
|
|
67
|
+
await this.call({ op: "release-cache", payload: cache });
|
|
68
|
+
};
|
|
64
69
|
if (traits.maxTtlSec)
|
|
65
70
|
this.renew = async (handle, ttlSec) =>
|
|
66
71
|
z
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
runtimeStaleThresholdMinutes,
|
|
30
30
|
} from "../multi-runtime";
|
|
31
31
|
import { mintRuntimeToken } from "../users";
|
|
32
|
+
import { getPoolCache, reconcileCacheReleases } from "./cache-cleanup";
|
|
32
33
|
import {
|
|
33
34
|
clearRuntimeOwnerIdCache,
|
|
34
35
|
ensureRuntimeOwnerId,
|
|
@@ -245,6 +246,13 @@ export async function provisionRuntime(
|
|
|
245
246
|
if (pool.lifecycle === "per-task") env.SWARM_EXIT_AFTER_TASKS = "1";
|
|
246
247
|
if (templateId) env.TEMPLATE_ID = templateId;
|
|
247
248
|
const ttlSec = pool.kind === "e2b" ? poolLeaseTtl(pool, controller) : undefined;
|
|
249
|
+
const currentPoolCache = await getPoolCache(pool.id);
|
|
250
|
+
const currentCache =
|
|
251
|
+
currentPoolCache?.controllerId === pool.placements[0] ? currentPoolCache : null;
|
|
252
|
+
const cache =
|
|
253
|
+
currentCache && controller.traits.persistentCache
|
|
254
|
+
? { key: currentCache.key, mountPath: currentCache.mountPath }
|
|
255
|
+
: undefined;
|
|
248
256
|
signal?.throwIfAborted();
|
|
249
257
|
const handle = await controller.provision({
|
|
250
258
|
runtimeId,
|
|
@@ -255,6 +263,7 @@ export async function provisionRuntime(
|
|
|
255
263
|
...(ttlSec === undefined ? {} : { ttlSec }),
|
|
256
264
|
env,
|
|
257
265
|
resources: pool.resources ?? undefined,
|
|
266
|
+
...(cache ? { cache } : {}),
|
|
258
267
|
labels: {
|
|
259
268
|
[RUNTIME_LABELS.runtimeId]: runtimeId,
|
|
260
269
|
[RUNTIME_LABELS.poolId]: pool.id,
|
|
@@ -267,6 +276,25 @@ export async function provisionRuntime(
|
|
|
267
276
|
await markRuntimeTerminated(runtimeId, "Supervisor stopped during provision");
|
|
268
277
|
return false;
|
|
269
278
|
}
|
|
279
|
+
const cacheWarning = currentCache
|
|
280
|
+
? !controller.traits.persistentCache
|
|
281
|
+
? "Controller does not support persistent caches; runtime started without cache"
|
|
282
|
+
: handle.meta?.cacheWarning
|
|
283
|
+
: undefined;
|
|
284
|
+
// A cache is optional: surface degradation without counting a provision failure.
|
|
285
|
+
const cacheErrorPrefix = "Persistent cache: ";
|
|
286
|
+
const cacheError = cacheWarning ? cacheErrorPrefix + scrubSecrets(cacheWarning) : null;
|
|
287
|
+
await getDbClient().run(
|
|
288
|
+
`UPDATE runtime_pools SET last_error = ?, last_error_at = ?
|
|
289
|
+
WHERE id = ? AND (last_error IS NULL OR substr(last_error, 1, ?) = ?)`,
|
|
290
|
+
[
|
|
291
|
+
cacheError,
|
|
292
|
+
cacheError ? new Date().toISOString() : null,
|
|
293
|
+
pool.id,
|
|
294
|
+
cacheErrorPrefix.length,
|
|
295
|
+
cacheErrorPrefix,
|
|
296
|
+
],
|
|
297
|
+
);
|
|
270
298
|
await getDbClient().run(
|
|
271
299
|
"UPDATE runtime_instances SET external_handle = ?, lease_expires_at = ?, created_at = COALESCE(?, created_at), updated_at = ? WHERE id = ?",
|
|
272
300
|
[
|
|
@@ -886,11 +914,14 @@ export async function reconcileOnce(
|
|
|
886
914
|
opts: { now?: Date; getControllerCommands?: typeof getControllerCommands } = {},
|
|
887
915
|
): Promise<void> {
|
|
888
916
|
if (!isRuntimePoolsEnabled() || !isMultiRuntimeEnabled() || inFlight) return;
|
|
889
|
-
|
|
917
|
+
const tick = new AbortController();
|
|
918
|
+
tickAbort = tick;
|
|
890
919
|
inFlight = reconcilePools(
|
|
891
920
|
opts.now ?? new Date(),
|
|
892
921
|
opts.getControllerCommands ?? getControllerCommands,
|
|
893
|
-
|
|
922
|
+
tick.signal,
|
|
923
|
+
).then(() =>
|
|
924
|
+
reconcileCacheReleases(opts.getControllerCommands ?? getControllerCommands, tick.signal),
|
|
894
925
|
);
|
|
895
926
|
try {
|
|
896
927
|
await inFlight;
|
|
@@ -65,6 +65,8 @@ export type AgentTaskStepConfig = {
|
|
|
65
65
|
parentTaskId?: string;
|
|
66
66
|
requestedByUserId?: string;
|
|
67
67
|
outputSchema?: Record<string, unknown>;
|
|
68
|
+
/** Dispatch even when the follow-up re-delegation guard blocks this step (a run launched from a follow-up task in a Slack thread, checked at its first new step). */
|
|
69
|
+
allowRedelegation?: boolean;
|
|
68
70
|
/** Wait for the dispatched task to reach a terminal status before resolving. Default: true. */
|
|
69
71
|
waitForCompletion?: boolean;
|
|
70
72
|
/** Max ms to wait for a terminal status before throwing. Default: 2h. Only used when waitForCompletion is true. */
|
|
@@ -315,6 +317,7 @@ export interface SwarmSdk {
|
|
|
315
317
|
slack_downloadFile(args: { url: string }): Promise<unknown>;
|
|
316
318
|
slack_delete(args: { channelId: string; messageTs: string }): Promise<unknown>;
|
|
317
319
|
slack_update(args: { channelId: string; messageTs: string; message: string }): Promise<unknown>;
|
|
320
|
+
slack_leaveThread(args: { taskId?: string; channelId?: string; threadTs?: string }): Promise<unknown>;
|
|
318
321
|
|
|
319
322
|
// --- write: chat sdk ---
|
|
320
323
|
chat_reply(args: { text: string }): Promise<unknown>;
|
|
@@ -496,6 +499,26 @@ export interface ScriptWorkflowSteps {
|
|
|
496
499
|
label: string,
|
|
497
500
|
config: { prompt: string; model?: string; schema?: Record<string, unknown> },
|
|
498
501
|
): Promise<unknown>;
|
|
502
|
+
/**
|
|
503
|
+
* Dispatch a task and journal its result under \`label\`.
|
|
504
|
+
*
|
|
505
|
+
* Resolves to the task's OUTPUT on success: the stored output string (JSON
|
|
506
|
+
* text under \`outputSchema\`, so parse it yourself), or \`null\` when the task
|
|
507
|
+
* stored none. It is never a task record: on a string, \`result.taskId\` and
|
|
508
|
+
* \`result.status\` are undefined; on \`null\`, reading them throws a TypeError.
|
|
509
|
+
* Other shapes:
|
|
510
|
+
* - \`failOnTaskFailure: false\` and the task ends failed/cancelled/superseded:
|
|
511
|
+
* \`{ taskId, status: "failed", error }\`.
|
|
512
|
+
* - \`waitForCompletion: false\`: the first call still waits up to ~30s while
|
|
513
|
+
* the server polls the task. Done in that window: the output, as on
|
|
514
|
+
* success. Failed or cancelled in that window: throws, or
|
|
515
|
+
* \`{ taskId, status: "failed", error }\` with \`failOnTaskFailure: false\`.
|
|
516
|
+
* Still running: \`{ taskId, status }\`.
|
|
517
|
+
*
|
|
518
|
+
* To get a step's task id, query by its context key:
|
|
519
|
+
* \`SELECT id, status, output FROM agent_tasks WHERE contextKey = 'script-run:<runId>:<label>'
|
|
520
|
+
* AND taskType = 'script-run-step' ORDER BY createdAt DESC, rowid DESC LIMIT 1\`.
|
|
521
|
+
*/
|
|
499
522
|
agentTask(
|
|
500
523
|
label: string,
|
|
501
524
|
config: AgentTaskStepConfig,
|
|
@@ -76,8 +76,11 @@ import {
|
|
|
76
76
|
computeContentHash,
|
|
77
77
|
createScheduledTask,
|
|
78
78
|
getScheduledTaskByName,
|
|
79
|
+
getWorkflow,
|
|
80
|
+
listWorkflows,
|
|
79
81
|
updateScheduledTask,
|
|
80
82
|
} from "../db";
|
|
83
|
+
import { getScript } from "../scripts/db";
|
|
81
84
|
import { resolveSeededEnabled } from "./automation-toggle";
|
|
82
85
|
import type { Seeder, SeedItem } from "./types";
|
|
83
86
|
import { canonicalJson } from "./workflows-seeder";
|
|
@@ -98,8 +101,27 @@ type ScheduleBlock = {
|
|
|
98
101
|
cron: string;
|
|
99
102
|
timezone?: string;
|
|
100
103
|
enabled?: boolean;
|
|
104
|
+
/** What a fire runs. Default `agent-task`: the `## Scheduled Task` prompt becomes a task. */
|
|
105
|
+
targetType?: ScheduleTargetType;
|
|
106
|
+
/** `targetType: "workflow"`: the workflow's name, resolved to its id at apply time. */
|
|
107
|
+
workflow?: string;
|
|
108
|
+
/** `targetType: "script"`: a global script, run directly with `scriptArgs`. */
|
|
109
|
+
scriptName?: string;
|
|
110
|
+
scriptArgs?: Record<string, unknown>;
|
|
101
111
|
};
|
|
102
112
|
|
|
113
|
+
type ScheduleTargetType = "agent-task" | "workflow" | "script";
|
|
114
|
+
const TARGET_TYPES: readonly ScheduleTargetType[] = ["agent-task", "workflow", "script"];
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The non-default target of a seeded schedule. Agent-task schedules carry none
|
|
118
|
+
* of these keys, so their seed hash is unchanged from before targets existed
|
|
119
|
+
* and recorded seed state on existing installs still matches.
|
|
120
|
+
*/
|
|
121
|
+
type SeedScheduleTarget =
|
|
122
|
+
| { targetType: "workflow"; workflow: string }
|
|
123
|
+
| { targetType: "script"; scriptName: string; scriptArgs: Record<string, unknown> };
|
|
124
|
+
|
|
103
125
|
export type SeedSchedule = {
|
|
104
126
|
name: string;
|
|
105
127
|
description: string;
|
|
@@ -112,7 +134,7 @@ export type SeedSchedule = {
|
|
|
112
134
|
params: Record<string, unknown>;
|
|
113
135
|
requiredParams: string[];
|
|
114
136
|
requires: AutomationIntegrationId[];
|
|
115
|
-
};
|
|
137
|
+
} & (SeedScheduleTarget | { targetType?: undefined });
|
|
116
138
|
|
|
117
139
|
export type ScheduleTemplateSource = { config: string; content: string };
|
|
118
140
|
type ScheduleSeedItem = SeedItem & { schedule: SeedSchedule };
|
|
@@ -167,10 +189,21 @@ function parseScheduleSource(source: ScheduleTemplateSource): SeedSchedule | nul
|
|
|
167
189
|
const block = JSON.parse(blockMatch[1]) as ScheduleBlock;
|
|
168
190
|
const afterBlock = scheduleStart + blockMatch.index! + blockMatch[0].length;
|
|
169
191
|
const taskHeading = source.content.indexOf("## Scheduled Task", afterBlock);
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
192
|
+
const target = parseScheduleTarget(config.name, block);
|
|
193
|
+
let taskTemplate = "";
|
|
194
|
+
if (target) {
|
|
195
|
+
// A workflow or script fire never reads a prompt: shipping one is a mistake.
|
|
196
|
+
if (taskHeading >= 0) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
`Schedule template ${config.name} targets a ${target.targetType} but ships a ## Scheduled Task prompt it would ignore`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
taskTemplate = source.content
|
|
203
|
+
.slice(taskHeading >= 0 ? taskHeading + "## Scheduled Task".length : afterBlock)
|
|
204
|
+
.trim();
|
|
205
|
+
if (!taskTemplate) throw new Error(`Schedule template ${config.name} has no task prompt`);
|
|
206
|
+
}
|
|
174
207
|
|
|
175
208
|
const requires = config.requires ?? [];
|
|
176
209
|
const requiredParams = config.placeholders ?? [];
|
|
@@ -196,7 +229,37 @@ function parseScheduleSource(source: ScheduleTemplateSource): SeedSchedule | nul
|
|
|
196
229
|
params: {},
|
|
197
230
|
requiredParams,
|
|
198
231
|
requires,
|
|
232
|
+
...target,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Validate the block's target fields. Returns null for the default agent-task target. */
|
|
237
|
+
function parseScheduleTarget(name: string, block: ScheduleBlock): SeedScheduleTarget | null {
|
|
238
|
+
const targetType = block.targetType ?? "agent-task";
|
|
239
|
+
if (!TARGET_TYPES.includes(targetType)) {
|
|
240
|
+
throw new Error(`Schedule template ${name} has unknown targetType "${targetType}"`);
|
|
241
|
+
}
|
|
242
|
+
const stray = (fields: (keyof ScheduleBlock)[]) => {
|
|
243
|
+
const set = fields.filter((field) => block[field] !== undefined);
|
|
244
|
+
if (set.length > 0) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`Schedule template ${name} (targetType ${targetType}) sets ${set.join(", ")}, which it would ignore`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
199
249
|
};
|
|
250
|
+
switch (targetType) {
|
|
251
|
+
case "agent-task":
|
|
252
|
+
stray(["workflow", "scriptName", "scriptArgs"]);
|
|
253
|
+
return null;
|
|
254
|
+
case "workflow":
|
|
255
|
+
stray(["scriptName", "scriptArgs"]);
|
|
256
|
+
if (!block.workflow) throw new Error(`Schedule template ${name} has no workflow name`);
|
|
257
|
+
return { targetType, workflow: block.workflow };
|
|
258
|
+
case "script":
|
|
259
|
+
stray(["workflow"]);
|
|
260
|
+
if (!block.scriptName) throw new Error(`Schedule template ${name} has no scriptName`);
|
|
261
|
+
return { targetType, scriptName: block.scriptName, scriptArgs: block.scriptArgs ?? {} };
|
|
262
|
+
}
|
|
200
263
|
}
|
|
201
264
|
|
|
202
265
|
export function loadSeedSchedules(
|
|
@@ -212,7 +275,27 @@ function scheduleSeedHash(schedule: SeedSchedule): string {
|
|
|
212
275
|
return computeContentHash(canonicalJson(schedule));
|
|
213
276
|
}
|
|
214
277
|
|
|
215
|
-
function
|
|
278
|
+
async function scheduleTargetFromRow(
|
|
279
|
+
schedule: ScheduledTask,
|
|
280
|
+
): Promise<SeedScheduleTarget | undefined> {
|
|
281
|
+
switch (schedule.targetType) {
|
|
282
|
+
case "workflow": {
|
|
283
|
+
const workflow = schedule.workflowId ? await getWorkflow(schedule.workflowId) : null;
|
|
284
|
+
// A dangling id can never equal a seeded name, so the row reads as edited.
|
|
285
|
+
return { targetType: "workflow", workflow: workflow?.name ?? `id:${schedule.workflowId}` };
|
|
286
|
+
}
|
|
287
|
+
case "script":
|
|
288
|
+
return {
|
|
289
|
+
targetType: "script",
|
|
290
|
+
scriptName: schedule.scriptName ?? "",
|
|
291
|
+
scriptArgs: schedule.scriptArgs ?? {},
|
|
292
|
+
};
|
|
293
|
+
default:
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async function scheduleFromRow(schedule: ScheduledTask): Promise<SeedSchedule> {
|
|
216
299
|
return {
|
|
217
300
|
name: schedule.name,
|
|
218
301
|
description: schedule.description ?? "",
|
|
@@ -225,9 +308,48 @@ function scheduleFromRow(schedule: ScheduledTask): SeedSchedule {
|
|
|
225
308
|
params: schedule.params ?? {},
|
|
226
309
|
requiredParams: schedule.requiredParams ?? [],
|
|
227
310
|
requires: schedule.requires ?? [],
|
|
311
|
+
...(await scheduleTargetFromRow(schedule)),
|
|
228
312
|
};
|
|
229
313
|
}
|
|
230
314
|
|
|
315
|
+
/** Resolve a seeded target to row fields, failing the item when its target is missing. */
|
|
316
|
+
async function resolveTargetFields(schedule: SeedSchedule) {
|
|
317
|
+
switch (schedule.targetType) {
|
|
318
|
+
case "workflow": {
|
|
319
|
+
const name = schedule.workflow;
|
|
320
|
+
const workflow = (await listWorkflows()).find((candidate) => candidate.name === name);
|
|
321
|
+
if (!workflow) throw new Error(`Workflow "${name}" not found`);
|
|
322
|
+
return {
|
|
323
|
+
targetType: "workflow" as const,
|
|
324
|
+
workflowId: workflow.id,
|
|
325
|
+
scriptName: null,
|
|
326
|
+
scriptArgs: null,
|
|
327
|
+
taskTemplate: null,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
case "script": {
|
|
331
|
+
if (!(await getScript({ name: schedule.scriptName, scope: "global" }))) {
|
|
332
|
+
throw new Error(`Global script "${schedule.scriptName}" not found`);
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
targetType: "script" as const,
|
|
336
|
+
workflowId: null,
|
|
337
|
+
scriptName: schedule.scriptName,
|
|
338
|
+
scriptArgs: schedule.scriptArgs,
|
|
339
|
+
taskTemplate: null,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
default:
|
|
343
|
+
return {
|
|
344
|
+
targetType: "agent-task" as const,
|
|
345
|
+
workflowId: null,
|
|
346
|
+
scriptName: null,
|
|
347
|
+
scriptArgs: null,
|
|
348
|
+
taskTemplate: schedule.taskTemplate,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
231
353
|
function nextRunAt(schedule: SeedSchedule): string | null {
|
|
232
354
|
try {
|
|
233
355
|
// Disabled templates can await a required timezone parameter. Validate the
|
|
@@ -263,7 +385,7 @@ export function createSchedulesSeeder(
|
|
|
263
385
|
|
|
264
386
|
async upstreamHash(item): Promise<string | null> {
|
|
265
387
|
const existing = await getScheduledTaskByName(item.key);
|
|
266
|
-
return existing ? scheduleSeedHash(scheduleFromRow(existing)) : null;
|
|
388
|
+
return existing ? scheduleSeedHash(await scheduleFromRow(existing)) : null;
|
|
267
389
|
},
|
|
268
390
|
|
|
269
391
|
async apply(item): Promise<void> {
|
|
@@ -274,8 +396,7 @@ export function createSchedulesSeeder(
|
|
|
274
396
|
cronExpression: schedule.cronExpression,
|
|
275
397
|
timezone: schedule.timezone,
|
|
276
398
|
enabled: schedule.enabled,
|
|
277
|
-
|
|
278
|
-
taskTemplate: schedule.taskTemplate,
|
|
399
|
+
...(await resolveTargetFields(schedule)),
|
|
279
400
|
taskType: schedule.taskType,
|
|
280
401
|
tags: schedule.tags,
|
|
281
402
|
params: schedule.params,
|
|
@@ -284,11 +405,16 @@ export function createSchedulesSeeder(
|
|
|
284
405
|
};
|
|
285
406
|
const existing = await getScheduledTaskByName(schedule.name);
|
|
286
407
|
if (existing) {
|
|
408
|
+
// Nulls clear the previous target's fields when a template changes target.
|
|
287
409
|
await updateScheduledTask(existing.id, { ...data, nextRunAt: resolvedNextRunAt });
|
|
288
410
|
} else {
|
|
289
411
|
await createScheduledTask({
|
|
290
412
|
name: schedule.name,
|
|
291
413
|
...data,
|
|
414
|
+
workflowId: data.workflowId ?? undefined,
|
|
415
|
+
scriptName: data.scriptName ?? undefined,
|
|
416
|
+
scriptArgs: data.scriptArgs ?? undefined,
|
|
417
|
+
taskTemplate: data.taskTemplate ?? undefined,
|
|
292
418
|
nextRunAt: resolvedNextRunAt ?? undefined,
|
|
293
419
|
});
|
|
294
420
|
}
|
|
@@ -71,10 +71,18 @@ import type {
|
|
|
71
71
|
TriggerConfig,
|
|
72
72
|
Workflow,
|
|
73
73
|
WorkflowDefinition,
|
|
74
|
+
WorkflowSnapshot,
|
|
74
75
|
} from "../../types";
|
|
75
76
|
import { validateDefinition } from "../../workflows/definition";
|
|
76
|
-
import {
|
|
77
|
+
import {
|
|
78
|
+
computeContentHash,
|
|
79
|
+
createWorkflow,
|
|
80
|
+
getWorkflowVersions,
|
|
81
|
+
listWorkflows,
|
|
82
|
+
updateWorkflow,
|
|
83
|
+
} from "../db";
|
|
77
84
|
import { resolveSeededEnabled } from "./automation-toggle";
|
|
85
|
+
import { getSeedState } from "./state-db";
|
|
78
86
|
import type { Seeder, SeedItem } from "./types";
|
|
79
87
|
|
|
80
88
|
type AutomationTemplateConfig = {
|
|
@@ -220,6 +228,62 @@ function workflowFromRow(workflow: Workflow): SeedWorkflow {
|
|
|
220
228
|
};
|
|
221
229
|
}
|
|
222
230
|
|
|
231
|
+
function workflowFromSnapshot(snapshot: WorkflowSnapshot): SeedWorkflow {
|
|
232
|
+
return {
|
|
233
|
+
name: snapshot.name,
|
|
234
|
+
description: snapshot.description ?? "",
|
|
235
|
+
enabled: snapshot.enabled,
|
|
236
|
+
definition: snapshot.definition,
|
|
237
|
+
triggers: snapshot.triggers,
|
|
238
|
+
cooldown: snapshot.cooldown,
|
|
239
|
+
input: snapshot.input,
|
|
240
|
+
triggerSchema: snapshot.triggerSchema,
|
|
241
|
+
params: snapshot.params ?? {},
|
|
242
|
+
requiredParams: snapshot.requiredParams ?? [],
|
|
243
|
+
requires: snapshot.requires ?? [],
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* What a run executes, without the settings an operator is told to change:
|
|
249
|
+
* inputs (plain data, resolved the same for every caller), on/off, cooldown,
|
|
250
|
+
* description and event triggers.
|
|
251
|
+
*/
|
|
252
|
+
function executableSurface(workflow: SeedWorkflow): string {
|
|
253
|
+
const {
|
|
254
|
+
input: _input,
|
|
255
|
+
enabled: _enabled,
|
|
256
|
+
cooldown: _cooldown,
|
|
257
|
+
description: _description,
|
|
258
|
+
triggers: _triggers,
|
|
259
|
+
...surface
|
|
260
|
+
} = workflow;
|
|
261
|
+
return canonicalJson(surface);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Whether a live workflow still runs seed source: `pristine` while what it
|
|
266
|
+
* executes matches what the seeder last wrote for its name, `edited` once
|
|
267
|
+
* anyone changed that, `unseeded` for a workflow the seeder never wrote.
|
|
268
|
+
* Operator settings (see `executableSurface`) keep it `pristine`. The seeded
|
|
269
|
+
* state is the live row itself or, once edited, the version snapshot taken
|
|
270
|
+
* before the first edit; an edit with no such snapshot counts as `edited`.
|
|
271
|
+
*/
|
|
272
|
+
export async function seededWorkflowState(
|
|
273
|
+
workflow: Workflow,
|
|
274
|
+
): Promise<"pristine" | "edited" | "unseeded"> {
|
|
275
|
+
const state = await getSeedState("workflow", workflow.name);
|
|
276
|
+
if (!state) return "unseeded";
|
|
277
|
+
const live = workflowFromRow(workflow);
|
|
278
|
+
if (workflowSeedHash(live) === state.seededHash) return "pristine";
|
|
279
|
+
for (const version of await getWorkflowVersions(workflow.id)) {
|
|
280
|
+
const seeded = workflowFromSnapshot(version.snapshot);
|
|
281
|
+
if (workflowSeedHash(seeded) !== state.seededHash) continue;
|
|
282
|
+
return executableSurface(seeded) === executableSurface(live) ? "pristine" : "edited";
|
|
283
|
+
}
|
|
284
|
+
return "edited";
|
|
285
|
+
}
|
|
286
|
+
|
|
223
287
|
export function createWorkflowsSeeder(
|
|
224
288
|
sources: readonly WorkflowTemplateSource[] = BUILT_IN_WORKFLOW_SOURCES,
|
|
225
289
|
): Seeder<WorkflowSeedItem> {
|
package/src/be/seed-pricing.ts
CHANGED
|
@@ -299,6 +299,8 @@ export function buildModelsDevSeedRows(cache: ModelsDevCache): PricingSeedRow[]
|
|
|
299
299
|
// strips the prefix, so we seed the bare id under `pi` too. Without this
|
|
300
300
|
// every gh-copilot-backed pi run fell through to `costSource='unpriced'`.
|
|
301
301
|
add(id, projectCostBlock("pi", id, model.cost));
|
|
302
|
+
// fx runs a bare OpenAI id on its `openai` custom connection.
|
|
303
|
+
add(id, projectCostBlock("fx", id, model.cost));
|
|
302
304
|
}
|
|
303
305
|
|
|
304
306
|
// ---- Cursor (bare vendor ids through Cursor's hosted inference) --------
|
|
@@ -358,6 +360,13 @@ export function buildModelsDevSeedRows(cache: ModelsDevCache): PricingSeedRow[]
|
|
|
358
360
|
anthropicBilled: id.startsWith("anthropic/"),
|
|
359
361
|
}),
|
|
360
362
|
);
|
|
363
|
+
// fx runs `openrouter/<id>` on its OpenRouter custom connection.
|
|
364
|
+
add(
|
|
365
|
+
id,
|
|
366
|
+
projectCostBlock("fx", id, model.cost, {
|
|
367
|
+
anthropicBilled: id.startsWith("anthropic/"),
|
|
368
|
+
}),
|
|
369
|
+
);
|
|
361
370
|
// Gemini specifically: also project under the 'gemini' provider so
|
|
362
371
|
// internal-ai callers that tag with provider='gemini' find a hit.
|
|
363
372
|
if (id.startsWith("google/")) {
|
|
@@ -369,6 +378,20 @@ export function buildModelsDevSeedRows(cache: ModelsDevCache): PricingSeedRow[]
|
|
|
369
378
|
}
|
|
370
379
|
}
|
|
371
380
|
|
|
381
|
+
// ---- fx on Vercel AI Gateway (`vercel/<vendor>/<id>`) -------------------
|
|
382
|
+
// The prefix is stripped at lookup, so a gateway id lands on the same key as
|
|
383
|
+
// its OpenRouter twin. OpenRouter's row wins where both list the model (one
|
|
384
|
+
// source per key); the gateway section fills the ids OpenRouter lacks.
|
|
385
|
+
for (const [id, model] of Object.entries(cache.vercel?.models ?? {})) {
|
|
386
|
+
if (!model?.cost || openrouter[id]?.cost) continue;
|
|
387
|
+
add(
|
|
388
|
+
id,
|
|
389
|
+
projectCostBlock("fx", id, model.cost, {
|
|
390
|
+
anthropicBilled: id.startsWith("anthropic/"),
|
|
391
|
+
}),
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
372
395
|
// ---- OpenAI-compatible registry providers ($0 flat-rate subscriptions) --
|
|
373
396
|
// pi and opencode run these as `<provider>/<id>`. The models.dev `nan`
|
|
374
397
|
// section and the registry-synthesized `helmcode` section
|