@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
package/src/be/db.ts
CHANGED
|
@@ -313,7 +313,11 @@ export {
|
|
|
313
313
|
getContextVersionHistory,
|
|
314
314
|
getLatestContextVersion,
|
|
315
315
|
} from "./db/context-versions";
|
|
316
|
-
export {
|
|
316
|
+
export {
|
|
317
|
+
getScriptRunExecutionArgs,
|
|
318
|
+
getScriptRunFollowUpGuard,
|
|
319
|
+
setScriptRunFollowUpGuard,
|
|
320
|
+
} from "./db/script-runs";
|
|
317
321
|
export {
|
|
318
322
|
AssetKeyMovesBlockedError,
|
|
319
323
|
assertAssetKeyMovesAllowed,
|
|
@@ -336,12 +340,14 @@ export {
|
|
|
336
340
|
} from "./db/runtime";
|
|
337
341
|
|
|
338
342
|
export {
|
|
343
|
+
countAgentDispatchesInThread,
|
|
339
344
|
type ExistingTrackerContextWork,
|
|
340
345
|
type ExistingTrackerContextWorkReason,
|
|
341
346
|
findActiveTasksByVcs,
|
|
342
|
-
|
|
347
|
+
findCompletedTasksInThread,
|
|
343
348
|
findExistingLinearTrackerContextWork,
|
|
344
349
|
findRecentCancelledTaskInThread,
|
|
350
|
+
findRecentFailedTaskInThread,
|
|
345
351
|
findTaskByGitHub,
|
|
346
352
|
findTaskByVcs,
|
|
347
353
|
getAgentCurrentTask,
|
|
@@ -6283,7 +6289,7 @@ export interface UpdateScheduledTaskData {
|
|
|
6283
6289
|
description?: string;
|
|
6284
6290
|
cronExpression?: string | null;
|
|
6285
6291
|
intervalMs?: number | null;
|
|
6286
|
-
taskTemplate?: string;
|
|
6292
|
+
taskTemplate?: string | null;
|
|
6287
6293
|
taskType?: string;
|
|
6288
6294
|
tags?: string[];
|
|
6289
6295
|
priority?: number;
|
|
@@ -13073,6 +13079,9 @@ export async function listRecentSessions(
|
|
|
13073
13079
|
): Promise<SessionListItem[] | SessionListItemSummary[]> {
|
|
13074
13080
|
const query = sessionSummaryQuery(opts);
|
|
13075
13081
|
const threadRoots = threadRootsSql();
|
|
13082
|
+
const order = (a: string) =>
|
|
13083
|
+
`${opts.triage === "later" ? `${a}.snoozeUntil ASC,` : ""}
|
|
13084
|
+
${a}.lastActivityAt DESC, ${a}.rootCreatedAt DESC, ${a}.root_id DESC`;
|
|
13076
13085
|
const rootRows = await getDbClient().query<
|
|
13077
13086
|
AgentTaskRow & {
|
|
13078
13087
|
__chainCount: number;
|
|
@@ -13084,13 +13093,14 @@ export async function listRecentSessions(
|
|
|
13084
13093
|
__threadRoots: number;
|
|
13085
13094
|
}
|
|
13086
13095
|
>(
|
|
13096
|
+
// Page first on the summaries, then read agent_tasks for the page roots only.
|
|
13087
13097
|
`SELECT r.*, a.chainCount AS __chainCount, a.lastActivityAt AS __lastActivityAt,
|
|
13088
13098
|
a.latestStatus AS __latestStatus, a.readState AS __readState,
|
|
13089
13099
|
a.snoozeUntil AS __snoozeUntil, a.needsYou AS __needsYou,
|
|
13090
13100
|
${threadRoots.sql} AS __threadRoots
|
|
13091
|
-
FROM (${query.sql})
|
|
13092
|
-
|
|
13093
|
-
|
|
13101
|
+
FROM (SELECT * FROM (${query.sql}) page ORDER BY ${order("page")} LIMIT ? OFFSET ?) a
|
|
13102
|
+
CROSS JOIN agent_tasks r ON r.id = a.root_id
|
|
13103
|
+
ORDER BY ${order("a")}`,
|
|
13094
13104
|
[...threadRoots.params, ...query.params, opts.limit ?? 25, opts.offset ?? 0],
|
|
13095
13105
|
);
|
|
13096
13106
|
const fields = await sessionPageFields(
|
|
@@ -14112,6 +14122,7 @@ type ScriptRunRow = {
|
|
|
14112
14122
|
last_heartbeat_at: string | null;
|
|
14113
14123
|
idempotencyKey: string | null;
|
|
14114
14124
|
requestedByUserId: string | null;
|
|
14125
|
+
sourceTaskId: string | null;
|
|
14115
14126
|
created_by: string | null;
|
|
14116
14127
|
updated_by: string | null;
|
|
14117
14128
|
};
|
|
@@ -14156,6 +14167,7 @@ function rowToScriptRun(row: ScriptRunRow): ScriptRun {
|
|
|
14156
14167
|
lastHeartbeatAt: row.last_heartbeat_at ?? undefined,
|
|
14157
14168
|
idempotencyKey: row.idempotencyKey ?? undefined,
|
|
14158
14169
|
requestedByUserId: row.requestedByUserId ?? undefined,
|
|
14170
|
+
sourceTaskId: row.sourceTaskId ?? undefined,
|
|
14159
14171
|
};
|
|
14160
14172
|
}
|
|
14161
14173
|
|
|
@@ -14184,6 +14196,7 @@ export async function createScriptRun(data: {
|
|
|
14184
14196
|
scriptName?: string;
|
|
14185
14197
|
idempotencyKey?: string;
|
|
14186
14198
|
requestedByUserId?: string;
|
|
14199
|
+
sourceTaskId?: string;
|
|
14187
14200
|
createdBy?: string;
|
|
14188
14201
|
updatedBy?: string;
|
|
14189
14202
|
}): Promise<{ run: ScriptRun; existing: boolean }> {
|
|
@@ -14198,8 +14211,9 @@ export async function createScriptRun(data: {
|
|
|
14198
14211
|
|
|
14199
14212
|
const row = await client.get<ScriptRunRow>(
|
|
14200
14213
|
`INSERT INTO script_runs
|
|
14201
|
-
(id, agentId, scriptName, source, args, idempotencyKey, requestedByUserId,
|
|
14202
|
-
|
|
14214
|
+
(id, agentId, scriptName, source, args, idempotencyKey, requestedByUserId, sourceTaskId,
|
|
14215
|
+
created_by, updated_by)
|
|
14216
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
14203
14217
|
RETURNING *`,
|
|
14204
14218
|
[
|
|
14205
14219
|
data.id,
|
|
@@ -14212,6 +14226,7 @@ export async function createScriptRun(data: {
|
|
|
14212
14226
|
sealJson(data.args ?? null),
|
|
14213
14227
|
data.idempotencyKey ?? null,
|
|
14214
14228
|
data.requestedByUserId ?? null,
|
|
14229
|
+
data.sourceTaskId ?? null,
|
|
14215
14230
|
data.createdBy ?? null,
|
|
14216
14231
|
data.updatedBy ?? data.createdBy ?? null,
|
|
14217
14232
|
],
|
|
@@ -45,14 +45,18 @@ function armFetchLimit(limit: number): number {
|
|
|
45
45
|
// lookup against it is a full table scan per outer row. The old backfill used
|
|
46
46
|
// `WHERE NOT EXISTS (SELECT 1 FROM memory_fts f WHERE f.memory_id = m.id)`,
|
|
47
47
|
// which is N x N: at ~18k memories it held the main connection for minutes at
|
|
48
|
-
// boot.
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
// boot. The diff below reads each table once, in rowid batches (FTS5 seeks a
|
|
49
|
+
// rowid range), and yields the event loop between batches: one whole-table
|
|
50
|
+
// statement held the loop for seconds on a large DB. A memory_fts row is an
|
|
51
|
+
// orphan when its memory is gone; that check is a lookup on the agent_memory
|
|
52
|
+
// primary key. A NULL memory_id is never an orphan, as with the old NOT IN.
|
|
53
|
+
export const FTS_SCAN_BATCH_SQL = `SELECT rowid AS rowid, memory_id AS memoryId,
|
|
54
|
+
memory_id IS NOT NULL
|
|
55
|
+
AND NOT EXISTS (SELECT 1 FROM agent_memory m WHERE m.id = memory_fts.memory_id) AS orphan
|
|
56
|
+
FROM memory_fts WHERE rowid > ? ORDER BY rowid LIMIT ?`;
|
|
57
|
+
export const MEMORY_ID_BATCH_SQL = `SELECT rowid AS rowid, id FROM agent_memory
|
|
58
|
+
WHERE rowid > ? ORDER BY rowid LIMIT ?`;
|
|
59
|
+
const FTS_SCAN_BATCH_SIZE = 1000;
|
|
56
60
|
const FTS_POPULATE_BATCH_SIZE = 500;
|
|
57
61
|
|
|
58
62
|
export type AgentMemoryRow = {
|
|
@@ -251,12 +255,45 @@ export class SqliteMemoryStore implements MemoryStore {
|
|
|
251
255
|
private async populateFtsTable(): Promise<void> {
|
|
252
256
|
const client = getDbClient();
|
|
253
257
|
const startedAt = performance.now();
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
258
|
+
const yieldLoop = () => new Promise<void>((resolve) => setImmediate(resolve));
|
|
259
|
+
|
|
260
|
+
// Drop orphan rows and collect the memory ids the index holds.
|
|
261
|
+
const indexed = new Set<string>();
|
|
262
|
+
let removed = 0;
|
|
263
|
+
for (let after = 0; ; ) {
|
|
264
|
+
const rows = await client.query<{ rowid: number; memoryId: string | null; orphan: number }>(
|
|
265
|
+
FTS_SCAN_BATCH_SQL,
|
|
266
|
+
[after, FTS_SCAN_BATCH_SIZE],
|
|
267
|
+
);
|
|
268
|
+
if (rows.length === 0) break;
|
|
269
|
+
after = rows[rows.length - 1]!.rowid;
|
|
270
|
+
const orphans = rows.filter((row) => row.orphan).map((row) => row.rowid);
|
|
271
|
+
if (orphans.length > 0) {
|
|
272
|
+
await client.run(
|
|
273
|
+
`DELETE FROM memory_fts WHERE rowid IN (${orphans.map(() => "?").join(",")})`,
|
|
274
|
+
orphans,
|
|
275
|
+
);
|
|
276
|
+
removed += orphans.length;
|
|
277
|
+
}
|
|
278
|
+
for (const row of rows) if (!row.orphan && row.memoryId) indexed.add(row.memoryId);
|
|
279
|
+
await yieldLoop();
|
|
280
|
+
}
|
|
281
|
+
if (removed > 0) {
|
|
282
|
+
console.warn(`[memory-fts] removed_extra_rows count=${removed}`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const missing: { id: string }[] = [];
|
|
286
|
+
for (let after = 0; ; ) {
|
|
287
|
+
const rows = await client.query<{ rowid: number; id: string }>(MEMORY_ID_BATCH_SQL, [
|
|
288
|
+
after,
|
|
289
|
+
FTS_SCAN_BATCH_SIZE,
|
|
290
|
+
]);
|
|
291
|
+
if (rows.length === 0) break;
|
|
292
|
+
after = rows[rows.length - 1]!.rowid;
|
|
293
|
+
for (const row of rows) if (!indexed.has(row.id)) missing.push(row);
|
|
294
|
+
await yieldLoop();
|
|
257
295
|
}
|
|
258
296
|
|
|
259
|
-
const missing = await client.query<{ id: string }>(FTS_MISSING_IDS_SQL);
|
|
260
297
|
for (let i = 0; i < missing.length; i += FTS_POPULATE_BATCH_SIZE) {
|
|
261
298
|
const ids = missing.slice(i, i + FTS_POPULATE_BATCH_SIZE).map((row) => row.id);
|
|
262
299
|
const placeholders = ids.map(() => "?").join(",");
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
-- What the session read path needs from session_summaries (migration 253):
|
|
2
|
+
-- the key and source indexes its filters use, and openTaskCount, the number
|
|
3
|
+
-- of chain tasks that are not finished (status outside completed, failed,
|
|
4
|
+
-- cancelled, superseded).
|
|
5
|
+
--
|
|
6
|
+
-- Auto-Done (src/be/db/session-tree.ts) moves a quiet Inbox session to Done
|
|
7
|
+
-- only when every chain task is finished. The session read path now reads
|
|
8
|
+
-- session_summaries instead of walking the chain, so the summary carries the
|
|
9
|
+
-- count. Same consistency rules as migration 253:
|
|
10
|
+
-- - Inserts add the new task's count incrementally.
|
|
11
|
+
-- - A status change applies its +1 or -1 in a BEFORE trigger. An AFTER
|
|
12
|
+
-- trigger of the same statement that recomputes the chain (a rewound
|
|
13
|
+
-- lastUpdatedAt, a requester or source edit) then overwrites it with the
|
|
14
|
+
-- exact count, so the delta is never applied on top of a recompute.
|
|
15
|
+
-- - session_summary_recompute_apply recomputes it with the other aggregates.
|
|
16
|
+
|
|
17
|
+
ALTER TABLE session_summaries ADD COLUMN openTaskCount INTEGER NOT NULL DEFAULT 0;
|
|
18
|
+
|
|
19
|
+
-- The group (key range) and source filters of the list and count. Activity
|
|
20
|
+
-- writes do not touch either column, so neither index costs the hot path.
|
|
21
|
+
CREATE INDEX idx_session_summaries_key ON session_summaries("key");
|
|
22
|
+
CREATE INDEX idx_session_summaries_source ON session_summaries(source);
|
|
23
|
+
|
|
24
|
+
DROP TRIGGER session_summary_recompute_apply;
|
|
25
|
+
CREATE TRIGGER session_summary_recompute_apply
|
|
26
|
+
INSTEAD OF INSERT ON session_summary_recompute
|
|
27
|
+
BEGIN
|
|
28
|
+
UPDATE session_summaries SET
|
|
29
|
+
chainCount = (SELECT COUNT(*) FROM session_members m WHERE m.rootId = NEW.rootId),
|
|
30
|
+
lastActivityAt = (SELECT MAX(t.lastUpdatedAt) FROM session_members m
|
|
31
|
+
JOIN agent_tasks t ON t.id = m.taskId WHERE m.rootId = NEW.rootId),
|
|
32
|
+
latestStatus = (SELECT t.status FROM session_members m
|
|
33
|
+
JOIN agent_tasks t ON t.id = m.taskId WHERE m.rootId = NEW.rootId
|
|
34
|
+
ORDER BY t.lastUpdatedAt DESC LIMIT 1),
|
|
35
|
+
pendingApprovalCount = (SELECT COUNT(*) FROM session_members m
|
|
36
|
+
JOIN approval_requests ar ON ar.sourceTaskId = m.taskId
|
|
37
|
+
WHERE m.rootId = NEW.rootId AND ar.status = 'pending'),
|
|
38
|
+
openTaskCount = (SELECT COUNT(*) FROM session_members m
|
|
39
|
+
JOIN agent_tasks t ON t.id = m.taskId WHERE m.rootId = NEW.rootId
|
|
40
|
+
AND t.status NOT IN ('completed', 'failed', 'cancelled', 'superseded')),
|
|
41
|
+
lastHumanAt = (SELECT MAX(at) FROM (
|
|
42
|
+
SELECT t.createdAt AS at FROM session_members m JOIN agent_tasks t ON t.id = m.taskId
|
|
43
|
+
WHERE m.rootId = NEW.rootId AND t.requestedByUserId IS NOT NULL
|
|
44
|
+
AND t.requestedByUserIdInherited = 0
|
|
45
|
+
AND COALESCE(t.source, '') NOT IN ('mcp', 'system', 'schedule', 'workflow')
|
|
46
|
+
UNION ALL
|
|
47
|
+
SELECT sm.created_at FROM session_members m
|
|
48
|
+
JOIN task_steering_messages sm ON sm.task_id = m.taskId
|
|
49
|
+
WHERE m.rootId = NEW.rootId AND sm.created_by_kind = 'user' AND sm.status != 'cancelled'
|
|
50
|
+
UNION ALL
|
|
51
|
+
SELECT ar.createdAt FROM session_members m
|
|
52
|
+
JOIN approval_requests ar ON ar.sourceTaskId = m.taskId
|
|
53
|
+
WHERE m.rootId = NEW.rootId AND ar.status = 'pending'))
|
|
54
|
+
WHERE rootId = NEW.rootId
|
|
55
|
+
AND EXISTS (SELECT 1 FROM agent_tasks WHERE id = NEW.rootId);
|
|
56
|
+
END;
|
|
57
|
+
|
|
58
|
+
DROP TRIGGER session_task_insert;
|
|
59
|
+
CREATE TRIGGER session_task_insert
|
|
60
|
+
AFTER INSERT ON agent_tasks
|
|
61
|
+
BEGIN
|
|
62
|
+
INSERT OR REPLACE INTO session_members (taskId, rootId)
|
|
63
|
+
SELECT NEW.id, NEW.id WHERE NEW.parentTaskId IS NULL
|
|
64
|
+
UNION ALL
|
|
65
|
+
SELECT NEW.id, m.rootId FROM session_members m WHERE m.taskId = NEW.parentTaskId;
|
|
66
|
+
|
|
67
|
+
INSERT OR REPLACE INTO session_summaries (rootId, createdAt, source, "key", requestedByUserId,
|
|
68
|
+
scheduleId, taskType, slackChannelId, contextKey, chainCount, lastActivityAt, latestStatus,
|
|
69
|
+
pendingApprovalCount, openTaskCount, lastHumanAt)
|
|
70
|
+
SELECT NEW.id, NEW.createdAt, NEW.source, NEW."key", NEW.requestedByUserId, NEW.scheduleId,
|
|
71
|
+
NEW.taskType, NEW.slackChannelId, NEW.contextKey, 1, NEW.lastUpdatedAt, NEW.status, 0,
|
|
72
|
+
NEW.status NOT IN ('completed', 'failed', 'cancelled', 'superseded'),
|
|
73
|
+
CASE WHEN NEW.requestedByUserId IS NOT NULL AND NEW.requestedByUserIdInherited = 0
|
|
74
|
+
AND COALESCE(NEW.source, '') NOT IN ('mcp', 'system', 'schedule', 'workflow')
|
|
75
|
+
THEN NEW.createdAt END
|
|
76
|
+
WHERE NEW.parentTaskId IS NULL;
|
|
77
|
+
|
|
78
|
+
UPDATE session_summaries SET
|
|
79
|
+
chainCount = chainCount + 1,
|
|
80
|
+
openTaskCount = openTaskCount
|
|
81
|
+
+ (NEW.status NOT IN ('completed', 'failed', 'cancelled', 'superseded')),
|
|
82
|
+
latestStatus = CASE WHEN NEW.lastUpdatedAt >= lastActivityAt THEN NEW.status ELSE latestStatus END,
|
|
83
|
+
lastActivityAt = MAX(lastActivityAt, NEW.lastUpdatedAt),
|
|
84
|
+
lastHumanAt = CASE WHEN NEW.requestedByUserId IS NOT NULL AND NEW.requestedByUserIdInherited = 0
|
|
85
|
+
AND COALESCE(NEW.source, '') NOT IN ('mcp', 'system', 'schedule', 'workflow')
|
|
86
|
+
AND (lastHumanAt IS NULL OR NEW.createdAt > lastHumanAt)
|
|
87
|
+
THEN NEW.createdAt ELSE lastHumanAt END
|
|
88
|
+
WHERE NEW.parentTaskId IS NOT NULL
|
|
89
|
+
AND rootId = (SELECT rootId FROM session_members WHERE taskId = NEW.id);
|
|
90
|
+
END;
|
|
91
|
+
|
|
92
|
+
-- BEFORE, so an AFTER recompute in the same statement has the last word.
|
|
93
|
+
CREATE TRIGGER session_task_open
|
|
94
|
+
BEFORE UPDATE OF status ON agent_tasks
|
|
95
|
+
WHEN (OLD.status NOT IN ('completed', 'failed', 'cancelled', 'superseded'))
|
|
96
|
+
IS NOT (NEW.status NOT IN ('completed', 'failed', 'cancelled', 'superseded'))
|
|
97
|
+
BEGIN
|
|
98
|
+
UPDATE session_summaries SET
|
|
99
|
+
openTaskCount = openTaskCount
|
|
100
|
+
+ CASE WHEN NEW.status NOT IN ('completed', 'failed', 'cancelled', 'superseded') THEN 1 ELSE -1 END
|
|
101
|
+
WHERE rootId = (SELECT rootId FROM session_members WHERE taskId = NEW.id);
|
|
102
|
+
END;
|
|
103
|
+
|
|
104
|
+
-- Backfill: one pass over the unfinished tasks.
|
|
105
|
+
UPDATE session_summaries SET openTaskCount = unfinished.n
|
|
106
|
+
FROM (
|
|
107
|
+
SELECT m.rootId, COUNT(*) AS n FROM agent_tasks t JOIN session_members m ON m.taskId = t.id
|
|
108
|
+
WHERE t.status NOT IN ('completed', 'failed', 'cancelled', 'superseded')
|
|
109
|
+
GROUP BY m.rootId
|
|
110
|
+
) AS unfinished
|
|
111
|
+
WHERE unfinished.rootId = session_summaries.rootId;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- NULL preserves the ephemeral-only behavior of existing pools.
|
|
2
|
+
ALTER TABLE runtime_pools ADD COLUMN cache TEXT
|
|
3
|
+
CHECK (cache IS NULL OR json_valid(cache));
|
|
4
|
+
|
|
5
|
+
-- Retain release intent across API restarts and pool deletion.
|
|
6
|
+
CREATE TABLE runtime_cache_releases (
|
|
7
|
+
id TEXT PRIMARY KEY,
|
|
8
|
+
pool_id TEXT NOT NULL,
|
|
9
|
+
controller_id TEXT NOT NULL REFERENCES runtime_controllers(id) ON DELETE RESTRICT,
|
|
10
|
+
owner_id TEXT NOT NULL,
|
|
11
|
+
cache_key TEXT NOT NULL,
|
|
12
|
+
runtime_ids TEXT NOT NULL CHECK (json_valid(runtime_ids) AND json_type(runtime_ids) = 'array'),
|
|
13
|
+
state TEXT NOT NULL DEFAULT 'pending' CHECK (state IN ('pending', 'deleting')),
|
|
14
|
+
last_error TEXT,
|
|
15
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
16
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
17
|
+
created_by TEXT REFERENCES users(id) ON DELETE SET NULL,
|
|
18
|
+
updated_by TEXT REFERENCES users(id) ON DELETE SET NULL,
|
|
19
|
+
UNIQUE(controller_id, owner_id, cache_key)
|
|
20
|
+
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- Covering indexes for the session visibility walk (readableSessionCondition
|
|
2
|
+
-- in src/be/db/tasks/read.ts). Every task list and count a dashboard user
|
|
3
|
+
-- loads computes the set of tasks in sessions the user cannot read: roots
|
|
4
|
+
-- owned by someone else, plus all their descendants.
|
|
5
|
+
--
|
|
6
|
+
-- agent_tasks rows carry large `task` text, and parentTaskId, scheduleId and
|
|
7
|
+
-- requestedByUserId sit after it, in the overflow pages. Without these
|
|
8
|
+
-- indexes the root step read every unscheduled task row and the recursive
|
|
9
|
+
-- step read the row of every descendant. With them, both steps stay in the
|
|
10
|
+
-- index. Production has no sqlite_stat1; the planner picks both without it.
|
|
11
|
+
|
|
12
|
+
-- Root step: `scheduleId IS NULL` plus the root, owner and id columns.
|
|
13
|
+
CREATE INDEX IF NOT EXISTS idx_agent_tasks_session_owner
|
|
14
|
+
ON agent_tasks(scheduleId, parentTaskId, requestedByUserId, id);
|
|
15
|
+
|
|
16
|
+
-- Recursive step: the children of a hidden task, by id.
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_agent_tasks_parentTaskId_id
|
|
18
|
+
ON agent_tasks(parentTaskId, id);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
-- Covering index for the default task list count (getTasksCount in
|
|
2
|
+
-- src/be/db/tasks/read.ts). The task list hides heartbeat tasks by default:
|
|
3
|
+
--
|
|
4
|
+
-- IFNULL(taskType, '') NOT IN (...) AND tags NOT LIKE '%"heartbeat"%'
|
|
5
|
+
--
|
|
6
|
+
-- A NOT IN and a NOT LIKE cannot seek, so the count scans. taskType and tags
|
|
7
|
+
-- sit after the large `task` text, in the overflow pages, so the scan read
|
|
8
|
+
-- every task body. This index holds the two filter columns and `id` (the
|
|
9
|
+
-- session visibility filter), so the count scans the index only.
|
|
10
|
+
CREATE INDEX IF NOT EXISTS idx_agent_tasks_list_filter
|
|
11
|
+
ON agent_tasks(taskType, tags, id);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
-- Covering index for the Slack render-v2 watcher sweep (getSlackTreeMessages
|
|
2
|
+
-- in src/be/db.ts), which runs on every watcher tick, even when idle. For
|
|
3
|
+
-- each stored tree it tests the thread's tasks by createdAt, status, source
|
|
4
|
+
-- and lastUpdatedAt. Those columns sit after the large `task` text, in the
|
|
5
|
+
-- overflow pages, so each test read whole task rows. This index holds them,
|
|
6
|
+
-- so the tests stay in the index. Partial: only Slack tasks carry a channel.
|
|
7
|
+
CREATE INDEX IF NOT EXISTS idx_agent_tasks_slack_thread_cover
|
|
8
|
+
ON agent_tasks(slackChannelId, slackThreadTs, createdAt, status, source, lastUpdatedAt, id)
|
|
9
|
+
WHERE slackChannelId IS NOT NULL;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
-- The task whose session launched a durable script run (the caller's
|
|
2
|
+
-- X-Source-Task-Id, kept only when that task belongs to the launching agent).
|
|
3
|
+
-- The follow-up re-delegation guard reads it when the run's ctx.step.agentTask
|
|
4
|
+
-- dispatches, the same way send-task reads the caller's source task.
|
|
5
|
+
ALTER TABLE script_runs ADD COLUMN sourceTaskId TEXT;
|
|
6
|
+
|
|
7
|
+
-- The run's follow-up re-delegation guard decision, taken once for the whole
|
|
8
|
+
-- run at its first new step: NULL (not decided yet), 'passed', or the rule that
|
|
9
|
+
-- blocked it ('same-work' or 'loop-cap'). Every later step follows it, so
|
|
10
|
+
-- parallel steps of one run are dispatched all or none.
|
|
11
|
+
ALTER TABLE script_runs ADD COLUMN followUpGuard TEXT;
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* - a `latest:<provider>/<family>` alias that is valid grammar and resolves to a catalog model;
|
|
8
8
|
* - a catalog id, bare (`claude-opus-5-5`, `gpt-5.6-sol`) or provider-qualified
|
|
9
9
|
* (`openrouter/deepseek/deepseek-v4.1-flash`, `amazon-bedrock/...`);
|
|
10
|
-
* - a Claude CLI shortname (`opus`, `sonnet`, ...), which the CLI resolves itself
|
|
10
|
+
* - a Claude CLI shortname (`opus`, `sonnet`, ...), which the CLI resolves itself;
|
|
11
|
+
* - a ChatGPT subscription id `openai-codex/<id>` whose `<id>` is in the `openai` section.
|
|
11
12
|
*
|
|
12
13
|
* Escape hatch: `allowCustomModel: true` on the call (tools, REST bodies, workflow node
|
|
13
14
|
* config, the agent runtime PATCH). Custom ids skip the check and are stored as given, for
|
|
@@ -23,6 +24,7 @@
|
|
|
23
24
|
*/
|
|
24
25
|
import {
|
|
25
26
|
buildClaudeShortnameMap,
|
|
27
|
+
chatgptSubscriptionModelId,
|
|
26
28
|
type HarnessCatalogSections,
|
|
27
29
|
harnessModelMismatch,
|
|
28
30
|
isAlias,
|
|
@@ -55,6 +57,8 @@ function hasModel(catalog: CatalogSections, provider: string, modelId: string):
|
|
|
55
57
|
export function isKnownCatalogModel(model: string, catalog: CatalogSections): boolean {
|
|
56
58
|
const id = model.trim().replace(CONTEXT_SUFFIX_RE, "");
|
|
57
59
|
if (!id) return false;
|
|
60
|
+
const subscriptionId = chatgptSubscriptionModelId(id);
|
|
61
|
+
if (subscriptionId) return hasModel(catalog, "openai", subscriptionId);
|
|
58
62
|
const slash = id.indexOf("/");
|
|
59
63
|
if (slash > 0) {
|
|
60
64
|
const provider = id.slice(0, slash);
|
|
@@ -40,8 +40,9 @@ const ROUTING_PREFIXES_BY_PROVIDER: Record<PricingProvider, readonly string[]> =
|
|
|
40
40
|
// openai, … — strip whichever proxy prefix the user picked.
|
|
41
41
|
opencode: ["openrouter/", "github-copilot/"],
|
|
42
42
|
// pi-mono can hit openrouter mirrors, the github-copilot proxy, or native
|
|
43
|
-
// anthropic/openai/google providers.
|
|
44
|
-
|
|
43
|
+
// anthropic/openai/google providers. `openai-codex/<id>` is an OpenAI id
|
|
44
|
+
// served by the ChatGPT subscription pool, priced like codex on that pool.
|
|
45
|
+
pi: ["openrouter/", "github-copilot/", "openai-codex/"],
|
|
45
46
|
// codex normally reports a bare id, but a user may set MODEL_OVERRIDE to a
|
|
46
47
|
// prefixed form. Be forgiving on the lookup side.
|
|
47
48
|
codex: ["openai/", "github-copilot/"],
|
|
@@ -69,6 +70,10 @@ const ROUTING_PREFIXES_BY_PROVIDER: Record<PricingProvider, readonly string[]> =
|
|
|
69
70
|
// The Grok CLI reports a bare xAI id (`grok-4.6`) or `openrouter/<vendor>/<id>`
|
|
70
71
|
// for an OpenRouter model; stripping the prefix lands it on the openrouter rows.
|
|
71
72
|
grok: ["openrouter/", "xai/"],
|
|
73
|
+
// fx reports the swarm model string: `openrouter/<vendor>/<id>` (OpenRouter
|
|
74
|
+
// connection), `vercel/<vendor>/<id>` (AI Gateway) or a bare OpenAI id.
|
|
75
|
+
// Stripping the route prefix lands the first two on `<vendor>/<id>` rows.
|
|
76
|
+
fx: ["openrouter/", "vercel/"],
|
|
72
77
|
};
|
|
73
78
|
|
|
74
79
|
/** `gpt-5-nano-2025-08-07` -> `gpt-5-nano`. Anthropic's 8-digit dates (`-20251001`) are real catalog ids and are left alone. */
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { ControllerCommands } from "../../runtime-providers/controller";
|
|
2
|
+
import { poolCacheKey, type RuntimeCacheReference } from "../../runtime-providers/types";
|
|
3
|
+
import type { RuntimePool } from "../../types";
|
|
4
|
+
import { scrubSecrets } from "../../utils/secret-scrubber";
|
|
5
|
+
import { getDbClient, getSwarmConfigs } from "../db";
|
|
6
|
+
|
|
7
|
+
/** The stored key is private API metadata; legacy caches retain their original name. */
|
|
8
|
+
export async function getPoolCache(poolId: string): Promise<{
|
|
9
|
+
key: string;
|
|
10
|
+
mountPath: string;
|
|
11
|
+
controllerId: string;
|
|
12
|
+
} | null> {
|
|
13
|
+
const row = await getDbClient().get<{ cache: string | null; controllerId: string }>(
|
|
14
|
+
"SELECT cache, json_extract(placements, '$[0]') AS controllerId FROM runtime_pools WHERE id = ?",
|
|
15
|
+
[poolId],
|
|
16
|
+
);
|
|
17
|
+
if (!row?.cache) return null;
|
|
18
|
+
const cache = JSON.parse(row.cache);
|
|
19
|
+
return {
|
|
20
|
+
key: cache.key ?? poolCacheKey(poolId),
|
|
21
|
+
mountPath: cache.mountPath,
|
|
22
|
+
controllerId: row.controllerId,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Call in the pool write transaction; no infrastructure calls occur here. */
|
|
27
|
+
export async function queuePoolCacheRelease(
|
|
28
|
+
pool: RuntimePool,
|
|
29
|
+
actor: string | null,
|
|
30
|
+
): Promise<void> {
|
|
31
|
+
if (!pool.cache) return;
|
|
32
|
+
const owner = (await getSwarmConfigs({ scope: "global", key: "runtime_owner_id" }))[0];
|
|
33
|
+
// No runtime could have created an owned cache before the owner was allocated.
|
|
34
|
+
if (!owner) return;
|
|
35
|
+
const cache = (await getPoolCache(pool.id))!;
|
|
36
|
+
const runtimes = await getDbClient().query<{ id: string }>(
|
|
37
|
+
`SELECT id FROM runtime_instances WHERE pool_id = ?
|
|
38
|
+
AND (controller_id = ? OR controller_id IS NULL) AND status != 'offline'`,
|
|
39
|
+
[pool.id, pool.placements[0]!],
|
|
40
|
+
);
|
|
41
|
+
await getDbClient().run(
|
|
42
|
+
`INSERT INTO runtime_cache_releases
|
|
43
|
+
(id, pool_id, controller_id, owner_id, cache_key, runtime_ids, created_by, updated_by)
|
|
44
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(controller_id, owner_id, cache_key) DO NOTHING`,
|
|
45
|
+
[
|
|
46
|
+
crypto.randomUUID(),
|
|
47
|
+
pool.id,
|
|
48
|
+
pool.placements[0]!,
|
|
49
|
+
owner.value,
|
|
50
|
+
cache.key,
|
|
51
|
+
JSON.stringify(runtimes.map((r) => r.id)),
|
|
52
|
+
actor,
|
|
53
|
+
actor,
|
|
54
|
+
],
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface CacheRelease extends RuntimeCacheReference {
|
|
59
|
+
id: string;
|
|
60
|
+
controllerId: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const RELEASE_LEASE_MS = 5 * 60_000;
|
|
64
|
+
|
|
65
|
+
/** Claims are atomic across reconcilers; all network calls are outside DB transactions. */
|
|
66
|
+
export async function reconcileCacheReleases(
|
|
67
|
+
commands: (id: string) => Promise<ControllerCommands>,
|
|
68
|
+
signal: AbortSignal,
|
|
69
|
+
now?: Date,
|
|
70
|
+
): Promise<void> {
|
|
71
|
+
const jobs = await getDbClient().query<CacheRelease>(
|
|
72
|
+
`SELECT id, pool_id AS poolId, controller_id AS controllerId, owner_id AS ownerId,
|
|
73
|
+
cache_key AS key FROM runtime_cache_releases ORDER BY created_at`,
|
|
74
|
+
);
|
|
75
|
+
for (const job of jobs) {
|
|
76
|
+
if (signal.aborted) return;
|
|
77
|
+
const claimed = now ?? new Date();
|
|
78
|
+
const claimedAt = claimed.toISOString();
|
|
79
|
+
// Reclaim abandoned work after five minutes; generation keys fence late provider calls.
|
|
80
|
+
const reserved = await getDbClient().run(
|
|
81
|
+
`UPDATE runtime_cache_releases SET state = 'deleting', updated_at = ?
|
|
82
|
+
WHERE id = ? AND (state = 'pending' OR updated_at < ?) AND NOT EXISTS (
|
|
83
|
+
SELECT 1 FROM runtime_instances WHERE status != 'offline'
|
|
84
|
+
AND id IN (SELECT value FROM json_each(runtime_cache_releases.runtime_ids))
|
|
85
|
+
)`,
|
|
86
|
+
[claimedAt, job.id, new Date(claimed.getTime() - RELEASE_LEASE_MS).toISOString()],
|
|
87
|
+
);
|
|
88
|
+
if (!reserved.changes) continue;
|
|
89
|
+
try {
|
|
90
|
+
signal.throwIfAborted();
|
|
91
|
+
const controller = await commands(job.controllerId);
|
|
92
|
+
if (!controller.releaseCache)
|
|
93
|
+
throw new Error("Controller cannot release caches; upgrade it before cleanup");
|
|
94
|
+
await controller.releaseCache({ key: job.key, poolId: job.poolId, ownerId: job.ownerId });
|
|
95
|
+
await getDbClient().run(
|
|
96
|
+
"DELETE FROM runtime_cache_releases WHERE id = ? AND state = 'deleting' AND updated_at = ?",
|
|
97
|
+
[job.id, claimedAt],
|
|
98
|
+
);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
const message = scrubSecrets(String(error));
|
|
101
|
+
await getDbClient().run(
|
|
102
|
+
`UPDATE runtime_cache_releases SET state = 'pending', last_error = ?, updated_at = ?
|
|
103
|
+
WHERE id = ? AND state = 'deleting' AND updated_at = ?`,
|
|
104
|
+
[message, new Date().toISOString(), job.id, claimedAt],
|
|
105
|
+
);
|
|
106
|
+
if (!signal.aborted) console.error("[runtime-pools] Cache cleanup deferred:", message);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|