@desplega.ai/agent-swarm 1.131.1 → 1.134.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 +3 -3
- package/dist/{actions-jgcy477k.js → actions-7kast243.js} +20 -18
- package/dist/{anthropic-messages-rjmd2e7n.js → anthropic-messages-706yad38.js} +33 -17
- package/dist/app-0g84t8wf.js +28 -0
- package/dist/{artifact-jysj3wrg.js → artifact-dje5ke4h.js} +37 -131
- package/dist/{assistant-f2vkk1hj.js → assistant-ac4htcky.js} +25 -22
- package/dist/{azure-openai-responses-c0yjmbfd.js → azure-openai-responses-be59fbsz.js} +13 -13
- package/dist/{boot-reembed-q7mp8hcr.js → boot-reembed-b1rcqn85.js} +13 -11
- package/dist/{boot-reembed-xmw8wcx6.js → boot-reembed-kw2541c4.js} +14 -12
- package/dist/{boot-scrub-logs-dsqrv79n.js → boot-scrub-logs-ja29t92g.js} +11 -9
- package/dist/{claude-adapter-q6x7yspx.js → claude-adapter-5cg1fv8s.js} +33 -29
- package/dist/{claude-managed-adapter-wsg37bt8.js → claude-managed-adapter-9z9sdytx.js} +9 -9
- package/dist/{claude-managed-setup-52z4mq9w.js → claude-managed-setup-3zqp51cr.js} +5 -5
- package/dist/{claude-1ex232fc.js → claude-wqn3dtca.js} +1 -1
- package/dist/{cli-4wgngmwv.js → cli-00w4hbam.js} +1 -1
- package/dist/{cli-er3a4bb0.js → cli-15429qdw.js} +6 -1
- package/dist/{cli-e5dabkx0.js → cli-1cwhyvta.js} +4 -4
- package/dist/{cli-zsspvvm2.js → cli-1ybr6vw2.js} +237 -237
- package/dist/{cli-xpxde7rm.js → cli-2wv4kj0e.js} +60 -31
- package/dist/cli-34w2584r.js +530 -0
- package/dist/{cli-se2p6rkb.js → cli-38taxznr.js} +6 -6
- package/dist/{cli-9sbhdpvb.js → cli-3cgfyfzm.js} +14 -14
- package/dist/{cli-5n6e5yfn.js → cli-3zmdzfjf.js} +79 -74
- package/dist/{cli-3ty0t082.js → cli-43x36mty.js} +197 -8
- package/dist/{cli-ycrnynpw.js → cli-480nehnh.js} +29 -22
- package/dist/{cli-0258203w.js → cli-492e7ap5.js} +140 -20
- package/dist/{cli-3fj60etf.js → cli-497b685t.js} +3 -3
- package/dist/{cli-pxfxkx5m.js → cli-4bap00k0.js} +1 -1
- package/dist/{cli-w2wxd56v.js → cli-4bc8vmhm.js} +848 -770
- package/dist/{cli-aca5pz9f.js → cli-589599p9.js} +1 -1
- package/dist/{cli-t930g0xh.js → cli-5tnr64pm.js} +1 -1
- package/dist/{cli-jxg3s9ey.js → cli-65d93ves.js} +7 -7
- package/dist/{cli-6dpytdr2.js → cli-6x363v1z.js} +2 -2
- package/dist/{cli-dxak0fqd.js → cli-787ph852.js} +11 -11
- package/dist/{cli-793jvbsh.js → cli-7bv8nckk.js} +16 -11
- package/dist/{cli-zyxs93y9.js → cli-7jcc9y1r.js} +3 -3
- package/dist/{cli-f1am9szv.js → cli-87t2gstf.js} +1 -1
- package/dist/{cli-9aryf9tv.js → cli-95m51ztz.js} +1 -1
- package/dist/{cli-4w4fmq9x.js → cli-9w49enmh.js} +2 -2
- package/dist/{cli-v69vk5h0.js → cli-9y5b02vr.js} +13 -13
- package/dist/{cli-x23me2mg.js → cli-a1rrkrb4.js} +1 -1
- package/dist/{cli-psrqtw2m.js → cli-a3pkfpqs.js} +2 -2
- package/dist/{cli-h0rgxd24.js → cli-a4ajmfvp.js} +6 -6
- package/dist/{cli-ygrbmjja.js → cli-aw8bb6sf.js} +6 -6
- package/dist/{cli-g74vpjp2.js → cli-brm3et47.js} +55 -22
- package/dist/{cli-czkae1fp.js → cli-c5ddbdfj.js} +9 -9
- package/dist/{cli-114fvtmj.js → cli-c79a9zrc.js} +1 -1
- package/dist/{cli-49n9f49x.js → cli-dx3dz0ms.js} +3 -3
- package/dist/{cli-6dp3ydxa.js → cli-e07exmsb.js} +18 -14
- package/dist/cli-et1bbwqf.js +13 -0
- package/dist/{cli-yazx6hap.js → cli-f3s9dry0.js} +14 -14
- package/dist/{cli-t8d349cy.js → cli-f4wj0s3m.js} +3 -3
- package/dist/{cli-d6n0thxw.js → cli-f64gdfxc.js} +1 -1
- package/dist/{cli-pwfsqc2x.js → cli-fyd58xgb.js} +55 -55
- package/dist/{cli-7dm2zqy2.js → cli-g0j14czv.js} +2 -2
- package/dist/{cli-b0p7rfnd.js → cli-g2p9bd1z.js} +14 -14
- package/dist/{cli-z905tvmm.js → cli-g814jbst.js} +2 -2
- package/dist/{cli-5dsbcjjw.js → cli-ghexzd2d.js} +970 -970
- package/dist/{cli-jvv0g8bh.js → cli-gpt9kf01.js} +15 -15
- package/dist/{cli-rgrfzg8z.js → cli-grt6qv91.js} +380 -380
- package/dist/{cli-wz9115x0.js → cli-gtafvdy0.js} +8 -8
- package/dist/{cli-q5dzzak0.js → cli-gy07xk4s.js} +2 -2
- package/dist/{cli-sy7jk3tx.js → cli-h0djenzm.js} +2 -2
- package/dist/{cli-nrkkknat.js → cli-h37gbjbf.js} +2 -281
- package/dist/{cli-jevgrb4w.js → cli-h39xxnq6.js} +1200 -491
- package/dist/{cli-gz9sbyk3.js → cli-jepmgn7r.js} +1 -1
- package/dist/{cli-ccf9vy7q.js → cli-jmvk6et8.js} +7 -7
- package/dist/{cli-2rk0cx7s.js → cli-jxj92gkt.js} +51 -41
- package/dist/{cli-bqw0mt1n.js → cli-k46t4664.js} +18 -3
- package/dist/{cli-50vbt00w.js → cli-m07313tr.js} +1153 -696
- package/dist/{cli-4j2mrzag.js → cli-mkq1rk7b.js} +1 -1
- package/dist/{cli-1z7048dy.js → cli-mnm7w46e.js} +1 -1
- package/dist/{cli-kd1ck4rx.js → cli-mp9sg4cg.js} +14 -8
- package/dist/{cli-jbcva0xj.js → cli-mrammd8c.js} +60 -35
- package/dist/cli-nanaxrz6.js +27 -0
- package/dist/{cli-p9swy5t3.js → cli-pq2ee5ja.js} +8 -6
- package/dist/{cli-2wadx7p2.js → cli-pt43pdp1.js} +2 -2
- package/dist/{cli-7sgrxg9a.js → cli-q6d0vvey.js} +4 -4
- package/dist/{cli-k9qkf1hw.js → cli-qf9kpsve.js} +9 -9
- package/dist/cli-qh4sqfws.js +15 -0
- package/dist/{cli-e15jhafb.js → cli-qntkxysp.js} +14 -14
- package/dist/{cli-sajgpkqg.js → cli-qs9hhsvb.js} +1723 -325
- package/dist/{cli-z2zcxes1.js → cli-qt5bt3vk.js} +1 -1
- package/dist/{cli-wx82tw1k.js → cli-qtekrty1.js} +3 -3
- package/dist/{cli-g1re8r05.js → cli-rh6f3s9n.js} +3 -3
- package/dist/{cli-n0krnc6d.js → cli-rzhcrw2p.js} +2 -2
- package/dist/{cli-gecy3z2s.js → cli-snw62ze5.js} +4 -4
- package/dist/{cli-n6h8qt8n.js → cli-snz3p4yf.js} +15 -10
- package/dist/{cli-r6kqqbj0.js → cli-tkf8n6cw.js} +11 -11
- package/dist/{cli-4rkfg1wx.js → cli-tkrr24mz.js} +14 -9
- package/dist/{cli-9dr5qzc0.js → cli-tv9f1kyb.js} +54 -43
- package/dist/{cli-gm9cbhbq.js → cli-vtz6yj3v.js} +28 -9
- package/dist/{cli-2f07m0xr.js → cli-wf86d5t7.js} +1 -1
- package/dist/{cli-7zd05xb4.js → cli-whrvct19.js} +45 -45
- package/dist/{cli-ap1czx8f.js → cli-wrrea8xm.js} +3 -3
- package/dist/{cli-57khth04.js → cli-xmyqv00z.js} +120 -4
- package/dist/{cli-cwy5hqbq.js → cli-yjmk7tzc.js} +1 -1
- package/dist/{cli-6pcz9h15.js → cli-zykjrwcx.js} +180 -394
- package/dist/cli.js +45 -45
- package/dist/{codex-adapter-htjkn22b.js → codex-adapter-hsq6mz3m.js} +21 -19
- package/dist/{codex-hook-hq54t9ft.js → codex-hook-8c500gq8.js} +8 -7
- package/dist/{codex-login-z6yhbkmk.js → codex-login-x7603481.js} +3 -3
- package/dist/{codex-session-runner-wjfc7smz.js → codex-session-runner-c1x3x143.js} +15 -13
- package/dist/{commands-xhrfwqsk.js → commands-rwtddtst.js} +11 -9
- package/dist/{db-anvwb22f.js → db-j2b355vz.js} +458 -444
- package/dist/{devin-adapter-v1sarqtw.js → devin-adapter-8qmhqpy3.js} +3 -3
- package/dist/{devtools-2y1r7yqk.js → devtools-j2rnj8w7.js} +4 -4
- package/dist/{e2b-jdkwemv2.js → e2b-znpx78eg.js} +16 -16
- package/dist/{es5-vjgtkgp6.js → es5-fmqrjzy3.js} +2 -2
- package/dist/{event-bus-axn823e9.js → event-bus-n8sb35pw.js} +4 -4
- package/dist/{getMachineId-bsd-6tc9wxdj.js → getMachineId-bsd-wpzbhkm6.js} +4 -4
- package/dist/{getMachineId-darwin-sjt9sgnc.js → getMachineId-darwin-45zabjy0.js} +4 -4
- package/dist/{getMachineId-linux-v5n18y9p.js → getMachineId-linux-warnp5pd.js} +3 -3
- package/dist/{getMachineId-unsupported-yrjyqw3d.js → getMachineId-unsupported-5spent2a.js} +3 -3
- package/dist/{getMachineId-win-w3r8063c.js → getMachineId-win-v4bvcw7a.js} +4 -4
- package/dist/{google-generative-ai-hysrd8qx.js → google-generative-ai-b0wzss55.js} +17 -14
- package/dist/{google-vertex-p5arrga0.js → google-vertex-e8q4dpya.js} +18 -15
- package/dist/{handlers-b9kbc621.js → handlers-hchgrxkh.js} +32 -29
- package/dist/{hook-xkvn6c5h.js → hook-p49egqc0.js} +66 -52
- package/dist/hook-spb00w76.js +34 -0
- package/dist/{http-9xh6ta4r.js → http-nh14kkms.js} +663 -181
- package/dist/index-1qthq7vc.js +6 -0
- package/dist/{index-0g68f0p6.js → index-20xqmn3v.js} +3 -3
- package/dist/{index-vdkr5z2w.js → index-3e03k6e4.js} +114 -114
- package/dist/{index-ywds4812.js → index-4cbyhq2g.js} +43 -41
- package/dist/index-67pzqfrb.js +36 -0
- package/dist/{index-bccrkdkn.js → index-7m8yxkw5.js} +16 -16
- package/dist/{index-eaxyqcmj.js → index-dvsk64sm.js} +163 -83
- package/dist/{index-dqkpnmqm.js → index-e19s2m8k.js} +18 -18
- package/dist/{index-j30k5zxn.js → index-emvxcy98.js} +16 -16
- package/dist/{index-7ndw6ecy.js → index-gqnmr60d.js} +9 -9
- package/dist/{index-9znhar3x.js → index-j02xvjtd.js} +184 -185
- package/dist/{index-1s2442yw.js → index-ns5kksc1.js} +7 -7
- package/dist/{index-dpy2hth8.js → index-pdxc8kfh.js} +71 -71
- package/dist/{index-z1m7pw0v.js → index-ptcqm8bc.js} +107 -107
- package/dist/{index-070v4eyb.js → index-pyp7rexm.js} +5 -6
- package/dist/{index-86znjz1p.js → index-shne41sd.js} +13 -13
- package/dist/{index-2qn2qndv.js → index-t36q2v4t.js} +183 -183
- package/dist/index-tcnvv8w6.js +7 -0
- package/dist/index-w91qbfv1.js +7 -0
- package/dist/{index-fzc0mwvj.js → index-yqweff5n.js} +32 -32
- package/dist/index-yrfphye1.js +39 -0
- package/dist/{keepalive-vc7s2aa6.js → keepalive-9k51nqhc.js} +21 -15
- package/dist/lead-2xq3dxbz.js +88 -0
- package/dist/maintenance-0c0g6nym.js +28 -0
- package/dist/mistral-conversations-2njwdwhj.js +717 -0
- package/dist/{multipart-parser-qaktc7p8.js → multipart-parser-6gftesw0.js} +2 -2
- package/dist/{oauth-refresh-sweep-yecgjpk4.js → oauth-refresh-sweep-273a64r2.js} +14 -12
- package/dist/{onboard-xj0f73f9.js → onboard-tsv9wwhz.js} +12 -12
- package/dist/{openai-codex-responses-5g27pd6f.js → openai-codex-responses-kt84eq86.js} +26 -26
- package/dist/{openai-completions-3vkmfz2a.js → openai-completions-mrypq7qp.js} +17 -16
- package/dist/{openai-responses-dg4t7j7a.js → openai-responses-qhrk21ez.js} +20 -17
- package/dist/{opencode-adapter-1gm38aba.js → opencode-adapter-rar9676j.js} +13 -11
- package/dist/{openrouter-images-g3gm8931.js → openrouter-images-684fysx9.js} +5 -5
- package/dist/{otel-impl-nxg0n0b3.js → otel-impl-wp1mfzd2.js} +752 -537
- package/dist/{photon_rs-ky7dj5qw.js → photon_rs-8dwt2pzy.js} +581 -581
- package/dist/{pi-messages-tqj8q6b9.js → pi-messages-txp9f97r.js} +7 -7
- package/dist/{pi-mono-adapter-4g5kby31.js → pi-mono-adapter-yz47kk8e.js} +1910 -1180
- package/dist/pricing-refresh-jw1yw2fn.js +22 -0
- package/dist/rbac-roles-dg964c8p.js +32 -0
- package/dist/{rbac-roles-1hcxdtg7.js → rbac-roles-p53cg5cv.js} +19 -17
- package/dist/{registry-02cy9z5z.js → registry-mh12y17v.js} +4 -4
- package/dist/render-v2-56vn2rk9.js +36 -0
- package/dist/seed-pricing-52xx6z63.js +19 -0
- package/dist/{setup-46xtgq3c.js → setup-e8gp79sy.js} +7 -7
- package/dist/token-util-zy908s1a.js +6 -0
- package/dist/{token-fxkn2zf2.js → token-xg5711x5.js} +4 -4
- package/dist/worker-s17f1g46.js +88 -0
- package/dist/{x-6mg4kfpe.js → x-xkfh0w0v.js} +5 -5
- package/openapi.json +566 -22
- package/package.json +6 -6
- package/src/be/budget-refusal-notify.ts +14 -7
- package/src/be/db-queries/task-artifacts.ts +182 -0
- package/src/be/db.ts +834 -171
- package/src/be/events.ts +5 -0
- package/src/be/migrations/131_session_logs_created_at_index.sql +2 -0
- package/src/be/migrations/132_multi_runtime_instances.sql +44 -0
- package/src/be/migrations/133_workflow_run_trigger_type.sql +12 -0
- package/src/be/migrations/135_backfill_task_pull_request_attachments.sql +228 -0
- package/src/be/migrations/136_task_requester_provenance.sql +37 -0
- package/src/be/migrations/137_memory_retrieval_composite_index.sql +1 -0
- package/src/be/migrations/runner.ts +17 -11
- package/src/be/multi-runtime.ts +430 -0
- package/src/be/scripts/extract-schema.ts +3 -1
- package/src/be/scripts/typecheck.ts +6 -2
- package/src/be/seed-prompt-templates.ts +25 -2
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/steering.ts +1 -1
- package/src/be/swarm-config-guard.ts +18 -0
- package/src/commands/credential-wait.ts +54 -0
- package/src/commands/onboard/steps/core-credentials.tsx +1 -1
- package/src/commands/onboard/steps/custom-templates.tsx +1 -1
- package/src/commands/onboard/steps/harness-credentials.tsx +1 -1
- package/src/commands/onboard/steps/health-check.tsx +1 -1
- package/src/commands/onboard/steps/start.tsx +1 -1
- package/src/commands/profile-sync-audit.ts +56 -0
- package/src/commands/profile-sync.ts +287 -15
- package/src/commands/provider-credentials.ts +46 -19
- package/src/commands/runner.ts +199 -33
- package/src/commands/templates.ts +22 -0
- package/src/github/handlers.ts +55 -9
- package/src/heartbeat/heartbeat.ts +109 -17
- package/src/hooks/hook.ts +49 -33
- package/src/http/active-sessions.ts +11 -1
- package/src/http/agents.ts +261 -30
- package/src/http/all-routes.ts +1 -0
- package/src/http/config.ts +13 -2
- package/src/http/core.ts +102 -4
- package/src/http/db-query-bounded.ts +313 -0
- package/src/http/db-query-shared.ts +249 -0
- package/src/http/db-query.ts +54 -42
- package/src/http/events.ts +2 -0
- package/src/http/index.ts +23 -2
- package/src/http/mcp-bridge.ts +37 -3
- package/src/http/mcp.ts +12 -0
- package/src/http/memory.ts +13 -2
- package/src/http/openapi.ts +1 -0
- package/src/http/poll.ts +70 -27
- package/src/http/prompt-templates.ts +36 -13
- package/src/http/route-def.ts +43 -9
- package/src/http/schedules.ts +2 -2
- package/src/http/scripts.ts +10 -1
- package/src/http/session-data.ts +44 -0
- package/src/http/tasks.ts +2 -2
- package/src/http/users.ts +74 -52
- package/src/http/webhooks.ts +5 -2
- package/src/http/workflows.ts +1 -4
- package/src/integrations/kapso/inbound.ts +1 -1
- package/src/jira/outbound.ts +39 -8
- package/src/jira/webhook-lifecycle.ts +13 -2
- package/src/linear/outbound.ts +43 -10
- package/src/oauth/keepalive.ts +13 -2
- package/src/prompts/base-prompt.ts +1 -1
- package/src/prompts/default-drift.ts +21 -0
- package/src/prompts/session-templates.ts +56 -7
- package/src/providers/claude-adapter.ts +6 -0
- package/src/providers/codex-adapter.ts +6 -0
- package/src/providers/opencode-adapter.ts +6 -0
- package/src/providers/pi-mono-adapter.ts +8 -0
- package/src/providers/pricing-sources.md +24 -0
- package/src/queue-stall-alarm.ts +216 -0
- package/src/rbac/legacy-policy.ts +3 -0
- package/src/rbac/permissions.ts +12 -0
- package/src/scheduler/scheduler.ts +7 -0
- package/src/script-workflows/executor.ts +2 -1
- package/src/scripts-runtime/executors/native.ts +2 -1
- package/src/scripts-runtime/executors/types.ts +5 -0
- package/src/scripts-runtime/loader.ts +5 -0
- package/src/scripts-runtime/sdk-allowlist.ts +3 -0
- package/src/scripts-runtime/swarm-config.ts +8 -0
- package/src/scripts-runtime/swarm-sdk.ts +7 -0
- package/src/scripts-runtime/types/stdlib.d.ts +4 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +4 -0
- package/src/server.ts +6 -0
- package/src/slack/channel-join.ts +22 -3
- package/src/slack/channel-lifecycle.ts +114 -0
- package/src/tests/budget-claim-gate.test.ts +20 -0
- package/src/tests/claude-adapter.test.ts +32 -0
- package/src/tests/codex-adapter.test.ts +30 -0
- package/src/tests/create-task-validation.test.ts +128 -0
- package/src/tests/credential-wait.test.ts +67 -1
- package/src/tests/db-query.test.ts +711 -2
- package/src/tests/heartbeat-checklist.test.ts +28 -0
- package/src/tests/http-api-integration.test.ts +81 -0
- package/src/tests/http-users.test.ts +166 -1
- package/src/tests/identity-field-budget.test.ts +176 -0
- package/src/tests/mcp-tools-user.test.ts +34 -0
- package/src/tests/mcp-user-route.test.ts +17 -0
- package/src/tests/metrics-http.test.ts +44 -0
- package/src/tests/migration-failure.test.ts +86 -0
- package/src/tests/multi-runtime-registration.test.ts +2810 -0
- package/src/tests/openapi-response-contract.test.ts +24 -0
- package/src/tests/openapi-route-headers.test.ts +83 -0
- package/src/tests/opencode-adapter.test.ts +40 -0
- package/src/tests/outbound-listener-rejection.test.ts +138 -0
- package/src/tests/pi-mono-adapter.test.ts +98 -0
- package/src/tests/preload.ts +2 -0
- package/src/tests/profile-sync-warning.test.ts +277 -0
- package/src/tests/profile-sync.test.ts +232 -6
- package/src/tests/prompt-template-resolver.test.ts +177 -6
- package/src/tests/prompt-template-session.test.ts +39 -3
- package/src/tests/queue-stall-alarm.test.ts +226 -0
- package/src/tests/rbac-charact-slack.test.ts +29 -0
- package/src/tests/rbac-engine.test.ts +3 -0
- package/src/tests/requester-comms.test.ts +32 -0
- package/src/tests/runner-requester-profile.test.ts +38 -0
- package/src/tests/schedule-target-type.test.ts +38 -0
- package/src/tests/script-connections.test.ts +13 -1
- package/src/tests/scripts-runtime-identity.test.ts +375 -0
- package/src/tests/scripts-runtime.test.ts +4 -3
- package/src/tests/sdk-allowlist.test.ts +47 -0
- package/src/tests/send-task-requested-by.test.ts +9 -0
- package/src/tests/session-costs.test.ts +506 -1
- package/src/tests/shutdown-error-scrubbing.test.ts +92 -0
- package/src/tests/slack-channel-lifecycle.test.ts +241 -0
- package/src/tests/slack-render-v2.test.ts +43 -0
- package/src/tests/steering-core.test.ts +28 -0
- package/src/tests/swarm-config-reserved-keys.test.ts +22 -0
- package/src/tests/task-artifacts.test.ts +386 -0
- package/src/tests/task-attachments-schema.test.ts +17 -1
- package/src/tests/task-completion-idempotency.test.ts +57 -0
- package/src/tests/task-pull-request-backfill-migration.test.ts +321 -0
- package/src/tests/tool-annotations.test.ts +4 -0
- package/src/tests/update-profile-api.test.ts +67 -39
- package/src/tests/update-profile-auth.test.ts +48 -4
- package/src/tests/user-attribution-migration.test.ts +54 -0
- package/src/tests/user-identity.test.ts +72 -0
- package/src/tests/workflow-async-v2.test.ts +39 -1
- package/src/tests/workflow-http-v2.test.ts +3 -2
- package/src/tests/workflow-mcp-trigger-schema.test.ts +52 -3
- package/src/tests/workflow-retry-v2.test.ts +45 -1
- package/src/tools/db-query.ts +18 -8
- package/src/tools/get-task-details.ts +24 -3
- package/src/tools/manage-user.ts +36 -3
- package/src/tools/poll-task.ts +93 -14
- package/src/tools/script-common.ts +5 -0
- package/src/tools/send-task.ts +1 -4
- package/src/tools/slack-archive-channel.ts +70 -0
- package/src/tools/slack-create-channel.ts +74 -0
- package/src/tools/slack-invite-to-channel.ts +78 -0
- package/src/tools/store-progress.ts +13 -2
- package/src/tools/swarm-config/delete-config.ts +9 -0
- package/src/tools/swarm-config/set-config.ts +3 -2
- package/src/tools/task-action.ts +30 -0
- package/src/tools/task-tool-ctx.ts +8 -1
- package/src/tools/tool-config.ts +4 -1
- package/src/tools/update-profile.ts +16 -21
- package/src/tools/utils.ts +5 -0
- package/src/tools/workflows/trigger-workflow.ts +7 -1
- package/src/types.ts +165 -1
- package/src/utils/github-pull-request.ts +76 -0
- package/src/utils/identity-field-budget.ts +73 -0
- package/src/utils/multi-runtime.ts +20 -0
- package/src/utils/requester-comms.ts +24 -0
- package/src/workflows/engine.ts +22 -2
- package/src/workflows/resume.ts +19 -11
- package/src/workflows/retry-poller.ts +1 -0
- package/src/workflows/triggers.ts +4 -0
- package/templates/skills/db-query-guidance/config.json +14 -0
- package/templates/skills/db-query-guidance/content.md +57 -0
- package/dist/app-5da78bd4.js +0 -26
- package/dist/cli-3te4jdkm.js +0 -225
- package/dist/cli-bmze986d.js +0 -7
- package/dist/cli-kh3ba2zf.js +0 -607
- package/dist/hook-cgk4wxmx.js +0 -31
- package/dist/index-25wjsdv2.js +0 -34
- package/dist/index-76at2ymp.js +0 -7
- package/dist/index-hase6zbe.js +0 -6
- package/dist/index-vg4snay4.js +0 -36
- package/dist/index-vy8edt27.js +0 -7
- package/dist/lead-r2yqfmx7.js +0 -85
- package/dist/maintenance-mw70k468.js +0 -26
- package/dist/mistral-conversations-81fwav0j.js +0 -30578
- package/dist/otel-nqa12wf3.js +0 -3149
- package/dist/pricing-refresh-2cn6effr.js +0 -20
- package/dist/rbac-roles-vynday5g.js +0 -30
- package/dist/render-v2-tc0bh8kk.js +0 -34
- package/dist/seed-pricing-8kceb43f.js +0 -17
- package/dist/token-util-pqyc8kng.js +0 -6
- package/dist/worker-qrbz6hmv.js +0 -85
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistence for multi-runtime agents: the runtime-instance lifecycle and the
|
|
3
|
+
* logical task-concurrency policy that sits above it.
|
|
4
|
+
*
|
|
5
|
+
* Server-side by design — these helpers own SQLite statements, so they live
|
|
6
|
+
* under `src/be` rather than beside the DB-free flag reader in
|
|
7
|
+
* `src/utils/multi-runtime.ts`, keeping the worker/API database boundary
|
|
8
|
+
* intact (`scripts/check-db-boundary.sh`).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Agent, RuntimeInstance, RuntimeInstanceStatus, SwarmConfig } from "../types";
|
|
12
|
+
import { isMultiRuntimeEnabled } from "../utils/multi-runtime";
|
|
13
|
+
import {
|
|
14
|
+
getActiveTaskCount,
|
|
15
|
+
getAgentById,
|
|
16
|
+
getDb,
|
|
17
|
+
getSwarmConfigs,
|
|
18
|
+
updateAgentMaxTasks,
|
|
19
|
+
updateAgentStatus,
|
|
20
|
+
upsertSwarmConfig,
|
|
21
|
+
} from "./db";
|
|
22
|
+
|
|
23
|
+
// ─── Logical task policy ─────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Agent-scoped swarm_config key holding the logical maxTasks policy in
|
|
27
|
+
* multi-runtime mode. Operators edit this row; `agents.maxTasks` stays the
|
|
28
|
+
* enforcement mirror that hasCapacity()/getRemainingCapacity() read. Same
|
|
29
|
+
* column-vs-config split as HARNESS_PROVIDER.
|
|
30
|
+
*/
|
|
31
|
+
export const AGENT_MAX_TASKS_CONFIG_KEY = "AGENT_MAX_TASKS";
|
|
32
|
+
|
|
33
|
+
/** Bounds match AgentSchema.maxTasks, so a mirrored write stays schema-valid. */
|
|
34
|
+
function parseAgentMaxTasksValue(raw: string): number | null {
|
|
35
|
+
const str = raw.trim();
|
|
36
|
+
if (!/^\d+$/.test(str)) return null;
|
|
37
|
+
const value = Number(str);
|
|
38
|
+
return value >= 1 && value <= 100 ? value : null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Concurrency an agent falls back to with no explicit policy. Mirrors the
|
|
43
|
+
* role defaults the runner resolves for itself (`resolveMaxConcurrent`), so a
|
|
44
|
+
* lead keeps room for the follow-up work it waits on.
|
|
45
|
+
*/
|
|
46
|
+
export function defaultMaxTasksForAgent(agent: Pick<Agent, "isLead">): number {
|
|
47
|
+
return agent.isLead ? 2 : 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function getAgentMaxTasksConfig(agentId: string): SwarmConfig | null {
|
|
51
|
+
return (
|
|
52
|
+
getSwarmConfigs({ scope: "agent", scopeId: agentId, key: AGENT_MAX_TASKS_CONFIG_KEY })[0] ??
|
|
53
|
+
null
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Reconcile the AGENT_MAX_TASKS policy row with its agents.maxTasks mirror,
|
|
59
|
+
* never consuming the runtime-reported concurrency.
|
|
60
|
+
*
|
|
61
|
+
* With no row yet, seed from the persisted agents.maxTasks — the value the
|
|
62
|
+
* swarm is already enforcing — so enabling multi-runtime mode cannot change
|
|
63
|
+
* behaviour. An existing row is authoritative and repairs the mirror instead.
|
|
64
|
+
*
|
|
65
|
+
* Runs inside the registration transaction so concurrent runtimes serialize:
|
|
66
|
+
* the first seeds, the rest observe.
|
|
67
|
+
*/
|
|
68
|
+
export function reconcileAgentMaxTasksPolicy(agentId: string): void {
|
|
69
|
+
const agent = getAgentById(agentId);
|
|
70
|
+
if (!agent) return;
|
|
71
|
+
const existing = getAgentMaxTasksConfig(agentId);
|
|
72
|
+
if (existing) {
|
|
73
|
+
const authoritative = parseAgentMaxTasksValue(existing.value);
|
|
74
|
+
if (authoritative !== null && authoritative !== (agent.maxTasks ?? 1)) {
|
|
75
|
+
updateAgentMaxTasks(agentId, authoritative);
|
|
76
|
+
}
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
upsertSwarmConfig({
|
|
80
|
+
scope: "agent",
|
|
81
|
+
scopeId: agentId,
|
|
82
|
+
key: AGENT_MAX_TASKS_CONFIG_KEY,
|
|
83
|
+
value: String(agent.maxTasks ?? defaultMaxTasksForAgent(agent)),
|
|
84
|
+
description: "Logical agent max-tasks policy (seeded from agents.maxTasks)",
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Deleting the policy returns the agent to its role default rather than to a
|
|
90
|
+
* fixed single slot — otherwise resetting a lead would leave it unable to
|
|
91
|
+
* start the follow-up its own task waits on.
|
|
92
|
+
*/
|
|
93
|
+
export function resetAgentMaxTasksMirror(agentId: string): void {
|
|
94
|
+
const agent = getAgentById(agentId);
|
|
95
|
+
if (!agent) return;
|
|
96
|
+
updateAgentMaxTasks(agentId, defaultMaxTasksForAgent(agent));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Config upsert that keeps the agents.maxTasks enforcement mirror in step
|
|
101
|
+
* with an agent-scoped AGENT_MAX_TASKS write, in one transaction, so policy
|
|
102
|
+
* and enforcement cannot diverge — including with no runtime connected.
|
|
103
|
+
* Every other key passes straight through.
|
|
104
|
+
*/
|
|
105
|
+
export function upsertSwarmConfigWithPolicyMirror(
|
|
106
|
+
data: Parameters<typeof upsertSwarmConfig>[0],
|
|
107
|
+
): SwarmConfig {
|
|
108
|
+
const scopeId = data.scope === "agent" ? data.scopeId : null;
|
|
109
|
+
if (!scopeId || data.key !== AGENT_MAX_TASKS_CONFIG_KEY) {
|
|
110
|
+
return upsertSwarmConfig(data);
|
|
111
|
+
}
|
|
112
|
+
return getDb().transaction(() => {
|
|
113
|
+
const row = upsertSwarmConfig(data);
|
|
114
|
+
// Guard, not validation — both write paths already ran validateConfigValue.
|
|
115
|
+
const value = parseAgentMaxTasksValue(data.value);
|
|
116
|
+
if (value !== null) updateAgentMaxTasks(scopeId, value);
|
|
117
|
+
return row;
|
|
118
|
+
})();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ─── Runtime instances ───────────────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
interface RuntimeInstanceRow {
|
|
124
|
+
id: string;
|
|
125
|
+
agent_id: string;
|
|
126
|
+
status: string;
|
|
127
|
+
reported_slots: number;
|
|
128
|
+
credential_ready: number | null;
|
|
129
|
+
metadata: string | null;
|
|
130
|
+
last_seen_at: string;
|
|
131
|
+
created_at: string;
|
|
132
|
+
updated_at: string;
|
|
133
|
+
created_by: string | null;
|
|
134
|
+
updated_by: string | null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function rowToRuntimeInstance(row: RuntimeInstanceRow): RuntimeInstance {
|
|
138
|
+
let metadata: Record<string, unknown> | null = null;
|
|
139
|
+
if (row.metadata) {
|
|
140
|
+
try {
|
|
141
|
+
metadata = JSON.parse(row.metadata) as Record<string, unknown>;
|
|
142
|
+
} catch {
|
|
143
|
+
metadata = null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
id: row.id,
|
|
148
|
+
agentId: row.agent_id,
|
|
149
|
+
status: row.status as RuntimeInstanceStatus,
|
|
150
|
+
reportedSlots: row.reported_slots,
|
|
151
|
+
credentialReady: row.credential_ready === null ? null : row.credential_ready === 1,
|
|
152
|
+
metadata,
|
|
153
|
+
lastSeenAt: row.last_seen_at,
|
|
154
|
+
createdAt: row.created_at,
|
|
155
|
+
updatedAt: row.updated_at,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Minutes without a ping before a runtime stops counting as live. The runner
|
|
161
|
+
* pings every poll iteration (~2s), so this is a wide margin that will not
|
|
162
|
+
* expire a healthy idle worker; it matches the "worker clearly dead" window
|
|
163
|
+
* the stalled-task classifier already uses.
|
|
164
|
+
*/
|
|
165
|
+
export function runtimeStaleThresholdMinutes(): number {
|
|
166
|
+
return Number(process.env.RUNTIME_STALE_THRESHOLD_MIN) || 5;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function runtimeLivenessCutoff(): string {
|
|
170
|
+
return new Date(Date.now() - runtimeStaleThresholdMinutes() * 60 * 1000).toISOString();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Register (or refresh) a runtime instance serving a logical agent. A
|
|
175
|
+
* re-registration from the same process refreshes its row in place rather
|
|
176
|
+
* than accumulating rows.
|
|
177
|
+
*
|
|
178
|
+
* The `DO UPDATE ... WHERE` guard makes runtime ownership permanent: an id
|
|
179
|
+
* already held by one agent can never be reassigned to another, so a
|
|
180
|
+
* duplicate id cannot move a live runtime between agents. Returns null in
|
|
181
|
+
* that case (no row written).
|
|
182
|
+
*/
|
|
183
|
+
export function upsertRuntimeInstance(instance: {
|
|
184
|
+
id: string;
|
|
185
|
+
agentId: string;
|
|
186
|
+
reportedSlots: number;
|
|
187
|
+
metadata?: Record<string, unknown> | null;
|
|
188
|
+
}): RuntimeInstance | null {
|
|
189
|
+
const metadataJson = instance.metadata ? JSON.stringify(instance.metadata) : null;
|
|
190
|
+
const row = getDb()
|
|
191
|
+
.prepare<RuntimeInstanceRow, [string, string, number, string | null]>(
|
|
192
|
+
`INSERT INTO runtime_instances (id, agent_id, status, reported_slots, metadata)
|
|
193
|
+
VALUES (?, ?, 'active', ?, ?)
|
|
194
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
195
|
+
status = 'active',
|
|
196
|
+
reported_slots = excluded.reported_slots,
|
|
197
|
+
metadata = COALESCE(excluded.metadata, runtime_instances.metadata),
|
|
198
|
+
last_seen_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
|
|
199
|
+
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
200
|
+
WHERE runtime_instances.agent_id = excluded.agent_id
|
|
201
|
+
RETURNING *`,
|
|
202
|
+
)
|
|
203
|
+
.get(instance.id, instance.agentId, instance.reportedSlots, metadataJson);
|
|
204
|
+
return row ? rowToRuntimeInstance(row) : null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function getRuntimeInstanceById(id: string): RuntimeInstance | null {
|
|
208
|
+
const row = getDb()
|
|
209
|
+
.prepare<RuntimeInstanceRow, [string]>("SELECT * FROM runtime_instances WHERE id = ?")
|
|
210
|
+
.get(id);
|
|
211
|
+
return row ? rowToRuntimeInstance(row) : null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Refresh a live runtime instance's liveness from the worker's ping cadence.
|
|
216
|
+
*
|
|
217
|
+
* Matches only a row that is already live and owned by the caller, so worker
|
|
218
|
+
* traffic can refresh liveness but never resurrect a closed or expired
|
|
219
|
+
* runtime — registration stays the only path back to `active`. Returns false
|
|
220
|
+
* when nothing matched, which callers use as "this identity is not live".
|
|
221
|
+
*/
|
|
222
|
+
export function touchRuntimeInstance(id: string, agentId: string): boolean {
|
|
223
|
+
const result = getDb()
|
|
224
|
+
.prepare(
|
|
225
|
+
`UPDATE runtime_instances
|
|
226
|
+
SET last_seen_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
|
|
227
|
+
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
228
|
+
WHERE id = ? AND agent_id = ? AND status = 'active' AND last_seen_at >= ?`,
|
|
229
|
+
)
|
|
230
|
+
.run(id, agentId, runtimeLivenessCutoff());
|
|
231
|
+
return result.changes > 0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Mark one runtime instance offline (scoped to its agent, like touch). */
|
|
235
|
+
export function markRuntimeInstanceOffline(id: string, agentId: string): RuntimeInstance | null {
|
|
236
|
+
const row = getDb()
|
|
237
|
+
.prepare<RuntimeInstanceRow, [string, string]>(
|
|
238
|
+
`UPDATE runtime_instances
|
|
239
|
+
SET status = 'offline', updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
240
|
+
WHERE id = ? AND agent_id = ? RETURNING *`,
|
|
241
|
+
)
|
|
242
|
+
.get(id, agentId);
|
|
243
|
+
return row ? rowToRuntimeInstance(row) : null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Runtimes currently serving an agent. A crashed process never reaches
|
|
248
|
+
* `/close`, so `active` alone would keep a dead runtime counted forever;
|
|
249
|
+
* liveness is therefore the conjunction of status and a fresh `last_seen_at`.
|
|
250
|
+
*/
|
|
251
|
+
export function countActiveRuntimeInstancesForAgent(agentId: string): number {
|
|
252
|
+
const result = getDb()
|
|
253
|
+
.prepare<{ count: number }, [string, string]>(
|
|
254
|
+
`SELECT COUNT(*) as count FROM runtime_instances
|
|
255
|
+
WHERE agent_id = ? AND status = 'active' AND last_seen_at >= ?`,
|
|
256
|
+
)
|
|
257
|
+
.get(agentId, runtimeLivenessCutoff());
|
|
258
|
+
return result?.count ?? 0;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Read-only listing for operator observability. `isLive` applies the same
|
|
263
|
+
* status+freshness conjunction as `isRuntimeInstanceLive`, so a stale row
|
|
264
|
+
* that the sweep has not pruned yet is never reported as live.
|
|
265
|
+
*/
|
|
266
|
+
export function listRuntimeInstancesForAgent(
|
|
267
|
+
agentId: string,
|
|
268
|
+
): Array<RuntimeInstance & { isLive: boolean }> {
|
|
269
|
+
const rows = getDb()
|
|
270
|
+
.prepare<RuntimeInstanceRow & { is_live: number }, [string, string]>(
|
|
271
|
+
`SELECT *, (status = 'active' AND last_seen_at >= ?) AS is_live
|
|
272
|
+
FROM runtime_instances
|
|
273
|
+
WHERE agent_id = ?
|
|
274
|
+
ORDER BY created_at ASC, id ASC`,
|
|
275
|
+
)
|
|
276
|
+
.all(runtimeLivenessCutoff(), agentId);
|
|
277
|
+
return rows.map((row) => ({ ...rowToRuntimeInstance(row), isLive: row.is_live === 1 }));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Whether this identity names a runtime of `agentId` that is still reporting.
|
|
282
|
+
* Dispatch uses it so a retired process cannot be handed work alongside its
|
|
283
|
+
* replacement; an absent identity is never live.
|
|
284
|
+
*/
|
|
285
|
+
export function isRuntimeInstanceLive(id: string | undefined, agentId: string): boolean {
|
|
286
|
+
if (!id) return false;
|
|
287
|
+
const row = getDb()
|
|
288
|
+
.prepare<{ c: number }, [string, string, string]>(
|
|
289
|
+
`SELECT COUNT(*) c FROM runtime_instances
|
|
290
|
+
WHERE id = ? AND agent_id = ? AND status = 'active' AND last_seen_at >= ?`,
|
|
291
|
+
)
|
|
292
|
+
.get(id, agentId, runtimeLivenessCutoff());
|
|
293
|
+
return (row?.c ?? 0) > 0;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Record this runtime's credential readiness. Process-local by design: a
|
|
298
|
+
* sibling's state is never touched. Only a live runtime may report.
|
|
299
|
+
*/
|
|
300
|
+
export function setRuntimeCredentialReady(id: string, agentId: string, ready: boolean): boolean {
|
|
301
|
+
const result = getDb()
|
|
302
|
+
.prepare(
|
|
303
|
+
`UPDATE runtime_instances
|
|
304
|
+
SET credential_ready = ?, updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
305
|
+
WHERE id = ? AND agent_id = ? AND status = 'active' AND last_seen_at >= ?`,
|
|
306
|
+
)
|
|
307
|
+
.run(ready ? 1 : 0, id, agentId, runtimeLivenessCutoff());
|
|
308
|
+
return result.changes > 0;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Whether any live runtime can currently execute work. A runtime that never
|
|
313
|
+
* reported (NULL) counts as ready, so workers opting out of credential checks
|
|
314
|
+
* stay dispatchable.
|
|
315
|
+
*/
|
|
316
|
+
export function hasReadyLiveRuntime(agentId: string): boolean {
|
|
317
|
+
const row = getDb()
|
|
318
|
+
.prepare<{ c: number }, [string, string]>(
|
|
319
|
+
`SELECT COUNT(*) c FROM runtime_instances
|
|
320
|
+
WHERE agent_id = ? AND status = 'active' AND last_seen_at >= ?
|
|
321
|
+
AND (credential_ready IS NULL OR credential_ready = 1)`,
|
|
322
|
+
)
|
|
323
|
+
.get(agentId, runtimeLivenessCutoff());
|
|
324
|
+
return (row?.c ?? 0) > 0;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Recompute the logical agent's state from its live runtimes.
|
|
329
|
+
*
|
|
330
|
+
* Precedence: no live runtime wins (offline), then active work (busy), then
|
|
331
|
+
* credential availability, then idle. Aggregating here is what stops one
|
|
332
|
+
* process's report from overwriting a sibling's — a waiting runtime cannot
|
|
333
|
+
* park an agent a ready sibling is serving, and a ready one cannot pull a
|
|
334
|
+
* busy agent back to idle.
|
|
335
|
+
*/
|
|
336
|
+
export function reconcileAgentStatusFromRuntimes(agentId: string): void {
|
|
337
|
+
const agent = getAgentById(agentId);
|
|
338
|
+
if (!agent) return;
|
|
339
|
+
if (countActiveRuntimeInstancesForAgent(agentId) === 0) {
|
|
340
|
+
if (agent.status !== "offline") updateAgentStatus(agentId, "offline");
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (!hasReadyLiveRuntime(agentId)) {
|
|
344
|
+
if (agent.status !== "waiting_for_credentials") {
|
|
345
|
+
updateAgentStatus(agentId, "waiting_for_credentials");
|
|
346
|
+
}
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const next = getActiveTaskCount(agentId) > 0 ? "busy" : "idle";
|
|
350
|
+
if (agent.status !== next) updateAgentStatus(agentId, next);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Agents with at least one runtime still reporting. While multi-runtime mode
|
|
355
|
+
* is on this is the eligibility set for dispatch: an agent with no live
|
|
356
|
+
* runtime — whether its runtimes died or it has not re-registered since the
|
|
357
|
+
* flag was enabled — cannot poll for work, so assigning to it would strand
|
|
358
|
+
* the task.
|
|
359
|
+
*/
|
|
360
|
+
export function agentsWithLiveRuntime(): Set<string> {
|
|
361
|
+
const rows = getDb()
|
|
362
|
+
.prepare<{ agent_id: string }, [string]>(
|
|
363
|
+
`SELECT DISTINCT agent_id FROM runtime_instances
|
|
364
|
+
WHERE status = 'active' AND last_seen_at >= ?`,
|
|
365
|
+
)
|
|
366
|
+
.all(runtimeLivenessCutoff());
|
|
367
|
+
return new Set(rows.map((r) => r.agent_id));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Retire runtimes that stopped pinging and take their agents offline when
|
|
372
|
+
* nothing live remains.
|
|
373
|
+
*
|
|
374
|
+
* Inert unless multi-runtime mode is on: after a rollback, workers stop
|
|
375
|
+
* refreshing their rows, and expiring them would offline agents that are
|
|
376
|
+
* running perfectly well under legacy semantics.
|
|
377
|
+
*
|
|
378
|
+
* Deleting the retired rows rather than keeping them is deliberate. Runtime
|
|
379
|
+
* identity is per boot, so retaining them would grow the table once per boot
|
|
380
|
+
* per agent forever; nothing reads a runtime after it stops being live.
|
|
381
|
+
*
|
|
382
|
+
* Expiry deliberately does NOT touch active_sessions. Runtime liveness and
|
|
383
|
+
* in-flight work liveness answer different questions: a stale runtime row is
|
|
384
|
+
* enough to stop NEW dispatch, but sessions are heartbeated by tool activity
|
|
385
|
+
* only — a healthy worker inside a long model call or shell command can be
|
|
386
|
+
* quiet past this cutoff — so it is not evidence the CURRENT work is dead.
|
|
387
|
+
* Crash classification stays owned by the heartbeat's stalled-task
|
|
388
|
+
* remediation, which requires both a stale session heartbeat and a stale
|
|
389
|
+
* task before reclaiming (and cleans the session when it does); the sweep's
|
|
390
|
+
* 30-minute stale-session cleanup backstops any leftover row.
|
|
391
|
+
*/
|
|
392
|
+
export function expireStaleRuntimeInstances(): {
|
|
393
|
+
expired: number;
|
|
394
|
+
agentsOffline: number;
|
|
395
|
+
pruned: number;
|
|
396
|
+
} {
|
|
397
|
+
if (!isMultiRuntimeEnabled()) {
|
|
398
|
+
return { expired: 0, agentsOffline: 0, pruned: 0 };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return getDb().transaction(() => {
|
|
402
|
+
const cutoff = runtimeLivenessCutoff();
|
|
403
|
+
// Still-active rows past the window never reached /close; retiring them
|
|
404
|
+
// removes the process from new-work eligibility. Whether its in-flight
|
|
405
|
+
// work is dead is decided separately (see doc comment above).
|
|
406
|
+
const stale = getDb()
|
|
407
|
+
.prepare<{ id: string; agent_id: string }, [string]>(
|
|
408
|
+
`SELECT id, agent_id FROM runtime_instances
|
|
409
|
+
WHERE status = 'active' AND last_seen_at < ?`,
|
|
410
|
+
)
|
|
411
|
+
.all(cutoff);
|
|
412
|
+
|
|
413
|
+
// Prune every row past the window, closed ones included. Runtime identity
|
|
414
|
+
// is per boot, so keeping them would add a row per boot per agent forever
|
|
415
|
+
// and nothing reads a runtime once it stops being live.
|
|
416
|
+
const pruned = getDb()
|
|
417
|
+
.prepare("DELETE FROM runtime_instances WHERE last_seen_at < ?")
|
|
418
|
+
.run(cutoff).changes;
|
|
419
|
+
|
|
420
|
+
// Recompute every affected agent from what survived: losing the only
|
|
421
|
+
// ready runtime has to park the agent even though a waiting sibling is
|
|
422
|
+
// still live, or the same sweep would assign work it cannot execute.
|
|
423
|
+
let agentsOffline = 0;
|
|
424
|
+
for (const agentId of new Set(stale.map((r) => r.agent_id))) {
|
|
425
|
+
reconcileAgentStatusFromRuntimes(agentId);
|
|
426
|
+
if (getAgentById(agentId)?.status === "offline") agentsOffline++;
|
|
427
|
+
}
|
|
428
|
+
return { expired: stale.length, agentsOffline, pruned };
|
|
429
|
+
})();
|
|
430
|
+
}
|
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
* Extraction is best-effort and non-blocking — failures return null.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
10
12
|
const TIMEOUT_MS = 5_000;
|
|
11
13
|
|
|
12
14
|
function extractorPath(): string {
|
|
13
|
-
return new URL("../../scripts-runtime/extract-args-schema.ts", import.meta.url)
|
|
15
|
+
return fileURLToPath(new URL("../../scripts-runtime/extract-args-schema.ts", import.meta.url));
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export async function extractArgsJsonSchema(source: string): Promise<string | null> {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
1
2
|
import ts from "typescript";
|
|
2
3
|
import { getScriptAppTypes } from "@/apps/script-types";
|
|
3
4
|
import { getScriptApiTypes, getScriptMcpTypes } from "@/be/script-connections";
|
|
@@ -127,7 +128,7 @@ export interface SwarmSdk {
|
|
|
127
128
|
// --- repos ---
|
|
128
129
|
repo_list(args?: Record<string, unknown>): Promise<unknown>;
|
|
129
130
|
// --- schedules ---
|
|
130
|
-
schedule_list(args?: { enabled?: boolean; name?: string; scheduleType?: "recurring" | "one_time"; targetType?: "agent-task" | "workflow" | "script"; workflowId?: string; scriptName?: string; hideCompleted?: boolean; consecutiveErrorsMin?: number; lastRunStatus?: "failed" | "succeeded" }): Promise<unknown>;
|
|
131
|
+
schedule_list(args?: { enabled?: boolean; name?: string; scheduleType?: "recurring" | "one_time"; targetType?: "agent-task" | "workflow" | "script"; workflowId?: string; scriptName?: string; hideCompleted?: boolean; consecutiveErrorsMin?: number; lastRunStatus?: "failed" | "succeeded"; includeFull?: boolean }): Promise<unknown>;
|
|
131
132
|
// --- scripts ---
|
|
132
133
|
script_search(args: { query?: string; scope?: ScriptScope; limit?: number }): Promise<unknown>;
|
|
133
134
|
script_run(args: { name?: string; source?: string; args?: unknown; intent?: string; scope?: ScriptScope; fsMode?: ScriptFsMode; idempotencyKey?: string }): Promise<unknown>;
|
|
@@ -194,6 +195,9 @@ export interface SwarmSdk {
|
|
|
194
195
|
slack_post(args: { channelId: string; message: string; blocks?: unknown }): Promise<unknown>;
|
|
195
196
|
slack_reply(args: { channelId?: string; threadTs?: string; message: string; taskId?: string; blocks?: unknown }): Promise<unknown>;
|
|
196
197
|
slack_startThread(args: { channelId: string; message: string; blocks?: unknown }): Promise<unknown>;
|
|
198
|
+
slack_createChannel(args: { name: string; isPrivate?: boolean }): Promise<unknown>;
|
|
199
|
+
slack_inviteToChannel(args: { channelId: string; userIds: string[] }): Promise<unknown>;
|
|
200
|
+
slack_archiveChannel(args: { channelId: string }): Promise<unknown>;
|
|
197
201
|
slack_uploadFile(args: Record<string, unknown>): Promise<unknown>;
|
|
198
202
|
slack_downloadFile(args: { url: string }): Promise<unknown>;
|
|
199
203
|
slack_delete(args: { channelId: string; messageTs: string }): Promise<unknown>;
|
|
@@ -799,7 +803,7 @@ const RUNTIME_GLOBALS_FILE = "/virtual/runtime-globals.d.ts";
|
|
|
799
803
|
function scriptTypesBase(): string {
|
|
800
804
|
const dir = process.env.SCRIPT_TYPES_DIR;
|
|
801
805
|
if (dir) return `${dir}/index.ts`;
|
|
802
|
-
return new URL("../../index.ts", import.meta.url)
|
|
806
|
+
return fileURLToPath(new URL("../../index.ts", import.meta.url));
|
|
803
807
|
}
|
|
804
808
|
|
|
805
809
|
function createCompilerHost(
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* prompt_templates table.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { getPromptTemplateDefaultDrift } from "../prompts/default-drift";
|
|
9
10
|
import { getAllTemplateDefinitions } from "../prompts/registry";
|
|
10
11
|
import { getPromptTemplates, resetPromptTemplateToDefault, upsertPromptTemplate } from "./db";
|
|
11
12
|
|
|
@@ -23,7 +24,8 @@ import "../tools/templates";
|
|
|
23
24
|
* For each registered EventTemplateDefinition:
|
|
24
25
|
* - If no global record exists at all, insert one as default (isDefault=true, state=enabled)
|
|
25
26
|
* - If a global default (isDefault=true) exists and its body differs from code, update it
|
|
26
|
-
* -
|
|
27
|
+
* - Reclassify a customized record as default when its body is byte-identical to code
|
|
28
|
+
* - Warn, but never overwrite, when a genuinely customized record differs from code
|
|
27
29
|
*/
|
|
28
30
|
export function seedDefaultTemplates(): void {
|
|
29
31
|
const definitions = getAllTemplateDefinitions();
|
|
@@ -55,8 +57,29 @@ export function seedDefaultTemplates(): void {
|
|
|
55
57
|
// Global default exists but body has drifted from code — update it.
|
|
56
58
|
// Only update if the record is still marked as default (not user-customized).
|
|
57
59
|
resetPromptTemplateToDefault(globalRecord.id, def.defaultBody);
|
|
60
|
+
} else if (!globalRecord.isDefault) {
|
|
61
|
+
const drift = getPromptTemplateDefaultDrift(globalRecord, def.defaultBody);
|
|
62
|
+
if (drift.defaultDrifted) {
|
|
63
|
+
const ageMs = Math.max(0, Date.now() - Date.parse(globalRecord.updatedAt));
|
|
64
|
+
const frozenDays = Math.floor(ageMs / 86_400_000);
|
|
65
|
+
const frozenHours = Math.floor(ageMs / 3_600_000);
|
|
66
|
+
const frozenFor =
|
|
67
|
+
frozenDays > 0
|
|
68
|
+
? `${frozenDays} day${frozenDays === 1 ? "" : "s"}`
|
|
69
|
+
: `${frozenHours} hour${frozenHours === 1 ? "" : "s"}`;
|
|
70
|
+
const signedByteDelta = drift.byteDelta > 0 ? `+${drift.byteDelta}` : `${drift.byteDelta}`;
|
|
71
|
+
|
|
72
|
+
console.warn(
|
|
73
|
+
`[prompt-templates] Customized template "${def.eventType}" has drifted from its code default; frozen for ${frozenFor} since ${globalRecord.updatedAt}; byte delta ${signedByteDelta} (code default ${drift.defaultBytes} bytes, customization ${drift.customizedBytes} bytes). The customization was preserved; reset it manually to reconcile.`,
|
|
74
|
+
);
|
|
75
|
+
} else if (globalRecord.state === "enabled") {
|
|
76
|
+
// The row was marked customized without any content change. Restore default ownership so
|
|
77
|
+
// future code-default updates continue to reconcile it. Non-enabled states are deliberate
|
|
78
|
+
// operator choices, and resetPromptTemplateToDefault would silently enable them.
|
|
79
|
+
resetPromptTemplateToDefault(globalRecord.id, def.defaultBody);
|
|
80
|
+
}
|
|
58
81
|
}
|
|
59
|
-
// If record exists with isDefault=false
|
|
82
|
+
// If record exists with isDefault=false and a different body: never overwrite it
|
|
60
83
|
// If record exists with isDefault=true and body matches: leave it alone
|
|
61
84
|
}
|
|
62
85
|
}
|
|
@@ -58,6 +58,12 @@ import composioGoogleDocsConfig from "../../../templates/skills/composio-google-
|
|
|
58
58
|
import composioGoogleDocsContent from "../../../templates/skills/composio-google-docs/content.md" with {
|
|
59
59
|
type: "text",
|
|
60
60
|
};
|
|
61
|
+
import dbQueryGuidanceConfig from "../../../templates/skills/db-query-guidance/config.json" with {
|
|
62
|
+
type: "text",
|
|
63
|
+
};
|
|
64
|
+
import dbQueryGuidanceContent from "../../../templates/skills/db-query-guidance/content.md" with {
|
|
65
|
+
type: "text",
|
|
66
|
+
};
|
|
61
67
|
import delegateWorkConfig from "../../../templates/skills/delegate-work/config.json" with {
|
|
62
68
|
type: "text",
|
|
63
69
|
};
|
|
@@ -270,6 +276,7 @@ const BUILT_IN_SKILL_SOURCES = [
|
|
|
270
276
|
{ config: composioGmailConfig, body: composioGmailContent },
|
|
271
277
|
{ config: composioGoogleCalendarConfig, body: composioGoogleCalendarContent },
|
|
272
278
|
{ config: composioGoogleDocsConfig, body: composioGoogleDocsContent },
|
|
279
|
+
{ config: dbQueryGuidanceConfig, body: dbQueryGuidanceContent },
|
|
273
280
|
{ config: delegateWorkConfig, body: delegateWorkContent },
|
|
274
281
|
{ config: designDocsConfig, body: designDocsContent },
|
|
275
282
|
{ config: downloadTaskAttachmentConfig, body: downloadTaskAttachmentContent },
|
package/src/be/steering.ts
CHANGED
|
@@ -76,7 +76,7 @@ export function promoteSteeringToTask(task: AgentTask, message: SteeringMessage)
|
|
|
76
76
|
source: message.source === "script" ? "api" : message.source,
|
|
77
77
|
taskType: "follow-up",
|
|
78
78
|
parentTaskId: task.id,
|
|
79
|
-
requestedByUserId: message.createdByUserId
|
|
79
|
+
requestedByUserId: message.createdByUserId,
|
|
80
80
|
bypassTrackerContextDedup: true,
|
|
81
81
|
});
|
|
82
82
|
}
|
|
@@ -126,7 +126,19 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
|
|
|
126
126
|
if (value === "sdk" || value === "bearer") return null;
|
|
127
127
|
return "Invalid BEDROCK_AUTH_MODE value (must be one of: sdk, bearer)";
|
|
128
128
|
},
|
|
129
|
+
// Logical agent concurrency policy (agent scope) — the authoritative source
|
|
130
|
+
// that upsertSwarmConfigWithPolicyMirror mirrors into agents.maxTasks.
|
|
131
|
+
// Bounded to AgentSchema's maxTasks range [1, 100] so the mirrored column
|
|
132
|
+
// stays schema-valid.
|
|
133
|
+
AGENT_MAX_TASKS: (value) => {
|
|
134
|
+
const str = String(value).trim();
|
|
135
|
+
if (!/^\d+$/.test(str) || Number(str) < 1 || Number(str) > 100) {
|
|
136
|
+
return "Invalid AGENT_MAX_TASKS (must be an integer between 1 and 100)";
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
},
|
|
129
140
|
...booleanValidators([
|
|
141
|
+
"MULTI_RUNTIME_ENABLED",
|
|
130
142
|
"STEERING_ENABLED",
|
|
131
143
|
"MEMORY_HYBRID_SEARCH",
|
|
132
144
|
"MEMORY_GRAPH_EXPANSION",
|
|
@@ -150,6 +162,7 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
|
|
|
150
162
|
"OTEL_TRACE_POLL",
|
|
151
163
|
"ANONYMIZED_TELEMETRY",
|
|
152
164
|
"SWARM_HIDE_CLOUD_PROMO",
|
|
165
|
+
"DB_QUERY_BOUNDED_ENABLED",
|
|
153
166
|
]),
|
|
154
167
|
...enumValidator("SLACK_THREAD_STEERING", ["lead", "all"]),
|
|
155
168
|
...enumValidator("SLACK_THREAD_STEERING_MODE", ["steer", "queue"]),
|
|
@@ -167,8 +180,13 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
|
|
|
167
180
|
"WORKFLOW_MAX_ITERATIONS",
|
|
168
181
|
"WORKFLOW_MAX_STEPS_PER_RUN",
|
|
169
182
|
"SCHEDULER_INTERVAL_MS",
|
|
183
|
+
"RUNTIME_STALE_THRESHOLD_MIN",
|
|
170
184
|
"SCRIPT_RUN_CONCURRENCY_CAP",
|
|
171
185
|
"WORKER_API_READY_TIMEOUT_SECONDS",
|
|
186
|
+
"DB_QUERY_HTTP_BUDGET_MS",
|
|
187
|
+
"DB_QUERY_HTTP_MAX_ROWS",
|
|
188
|
+
"DB_QUERY_MCP_BUDGET_MS",
|
|
189
|
+
"DB_QUERY_MCP_MAX_ROWS",
|
|
172
190
|
],
|
|
173
191
|
1,
|
|
174
192
|
),
|
|
@@ -209,3 +209,57 @@ export async function awaitCredentials(opts: AwaitCredentialsOptions): Promise<C
|
|
|
209
209
|
}
|
|
210
210
|
return status;
|
|
211
211
|
}
|
|
212
|
+
|
|
213
|
+
/** Options for {@link retryBootStep}; injectable sleep/log keep tests deterministic. */
|
|
214
|
+
export interface RetryBootStepOptions {
|
|
215
|
+
/** Names the step in retry logs and the exhaustion error. */
|
|
216
|
+
label?: string;
|
|
217
|
+
attempts?: number;
|
|
218
|
+
initialDelayMs?: number;
|
|
219
|
+
maxDelayMs?: number;
|
|
220
|
+
sleep?: (ms: number) => Promise<void>;
|
|
221
|
+
log?: (line: string) => void;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Bounded retry for a recovery-critical boot step after the credential wait.
|
|
226
|
+
* A runtime that went stale mid-wait is only revived by a SUCCESSFUL
|
|
227
|
+
* registration, and the readiness report that follows is the only transition
|
|
228
|
+
* out of `waiting_for_credentials` — so unlike the runner's best-effort
|
|
229
|
+
* periodic paths, these steps retry with bounded backoff and propagate
|
|
230
|
+
* failure once exhausted. The caller decides what exhaustion means (the
|
|
231
|
+
* runner treats it like boot-registration failure rather than entering the
|
|
232
|
+
* work loop as though recovery succeeded).
|
|
233
|
+
*/
|
|
234
|
+
export async function retryBootStep(
|
|
235
|
+
step: () => Promise<void>,
|
|
236
|
+
opts: RetryBootStepOptions = {},
|
|
237
|
+
): Promise<void> {
|
|
238
|
+
const label = opts.label ?? "boot step";
|
|
239
|
+
const attempts = opts.attempts ?? 5;
|
|
240
|
+
const sleep = opts.sleep ?? ((ms: number) => Bun.sleep(ms));
|
|
241
|
+
const log = opts.log ?? ((line: string) => console.warn(line));
|
|
242
|
+
const maxDelayMs = opts.maxDelayMs ?? 10_000;
|
|
243
|
+
let delayMs = opts.initialDelayMs ?? 1_000;
|
|
244
|
+
let lastError: unknown;
|
|
245
|
+
|
|
246
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
247
|
+
try {
|
|
248
|
+
await step();
|
|
249
|
+
return;
|
|
250
|
+
} catch (err) {
|
|
251
|
+
lastError = err;
|
|
252
|
+
if (attempt < attempts) {
|
|
253
|
+
log(
|
|
254
|
+
`[boot] ${label} failed (attempt ${attempt}/${attempts}, retry in ${delayMs}ms): ${err}`,
|
|
255
|
+
);
|
|
256
|
+
await sleep(delayMs);
|
|
257
|
+
delayMs = Math.min(delayMs * 2, maxDelayMs);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
throw lastError instanceof Error
|
|
263
|
+
? lastError
|
|
264
|
+
: new Error(`${label} failed after ${attempts} attempt(s): ${lastError}`);
|
|
265
|
+
}
|
|
@@ -61,7 +61,7 @@ export function CoreCredentialsStep({ state, goToNext, addLog }: StepProps) {
|
|
|
61
61
|
setTimeout(() => setReady(true), 1000);
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
-
generate();
|
|
64
|
+
generate().catch((err) => addLog(`Credential generation failed: ${err}`));
|
|
65
65
|
}, [state.services, addLog]);
|
|
66
66
|
|
|
67
67
|
useEffect(() => {
|