@desplega.ai/agent-swarm 2.7.5 → 2.7.7
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/dist/{acp-adapter-g3ads55s.js → acp-adapter-7b4vk3h5.js} +5 -4
- package/dist/{actions-zwfykqqk.js → actions-3tt153af.js} +18 -17
- package/dist/{amp-adapter-z3s0ssdt.js → amp-adapter-njpts0yr.js} +1 -1
- package/dist/{anthropic-messages-x4q6rfq5.js → anthropic-messages-xey2skwx.js} +7 -7
- package/dist/{app-vmp4fgep.js → app-nky4378f.js} +3 -3
- package/dist/{assistant-vwde653f.js → assistant-jnb669pa.js} +24 -22
- package/dist/{azure-openai-responses-s1w5n70z.js → azure-openai-responses-26t7j9ra.js} +5 -5
- package/dist/{boot-reembed-375t7tbk.js → boot-reembed-9mgvaztt.js} +1 -1
- package/dist/{boot-reembed-gq9rcpnj.js → boot-reembed-qs2djdej.js} +1 -1
- package/dist/{boot-scrub-sweep-nd938kyf.js → boot-scrub-sweep-yjpdj7s1.js} +3 -3
- package/dist/{boot-seeds-aykx51v3.js → boot-seeds-5cm6ambd.js} +4 -4
- package/dist/{boxd-k1j26g3h.js → boxd-nv507va8.js} +5 -5
- package/dist/{claude-adapter-8j4hm099.js → claude-adapter-fn287zmm.js} +5 -4
- package/dist/{claude-managed-setup-0wyynpn1.js → claude-managed-setup-wtdz2mj2.js} +1 -1
- package/dist/{claude-sdk-session-q6wj5hmy.js → claude-sdk-session-x91kqyhm.js} +9677 -9514
- package/dist/{cli-j3jvnpe8.js → cli-08bmz9z9.js} +1 -1
- package/dist/{cli-gjr2y2xk.js → cli-0agcv1v5.js} +4 -1
- package/dist/{cli-y24sc29k.js → cli-1c6tzt8g.js} +30 -8
- package/dist/{cli-mjzynrse.js → cli-35rsp8ye.js} +1 -1
- package/dist/{cli-rpm2sgv0.js → cli-3fg0bdcv.js} +19 -2
- package/dist/{cli-nn4d8dne.js → cli-5b456bpf.js} +2 -2
- package/dist/{cli-bhjt02p8.js → cli-5jnhv72d.js} +1 -1
- package/dist/{cli-fqzx7gwa.js → cli-5zx699j9.js} +2 -2
- package/dist/{cli-hf6zt5m6.js → cli-69szn7w7.js} +17 -1
- package/dist/{cli-vqypxzh4.js → cli-6g0hb4cp.js} +8 -8
- package/dist/{cli-tqgg40ek.js → cli-6pfd54vx.js} +9 -8
- package/dist/{cli-bsn8nhrq.js → cli-6vcp76qk.js} +3 -3
- package/dist/{cli-zppwg9p9.js → cli-72v799xn.js} +1 -1
- package/dist/cli-82j36f2m.js +45 -0
- package/dist/{cli-hn9hxmz9.js → cli-8305gbq0.js} +29 -16
- package/dist/{cli-z8eh3ex8.js → cli-8sckt74b.js} +8 -8
- package/dist/{cli-bnz7a7qf.js → cli-95jzxcp3.js} +1 -1
- package/dist/{cli-0bxqnvq9.js → cli-adeegh7d.js} +29 -7
- package/dist/{cli-g61qxmge.js → cli-afkqkrtw.js} +3 -3
- package/dist/{cli-7a42nfy0.js → cli-ayayvfg4.js} +24 -4
- package/dist/{cli-bqt1mptt.js → cli-az48n0nr.js} +4 -4
- package/dist/{cli-z6jkx23q.js → cli-bfv7vdg6.js} +9 -9
- package/dist/{cli-pywf3eh5.js → cli-caj33vcq.js} +5 -5
- package/dist/{cli-5pag6rmn.js → cli-cb2a8pna.js} +1 -1
- package/dist/{cli-wvaawacd.js → cli-cppkf404.js} +5236 -72
- package/dist/{cli-mry0qzym.js → cli-ddaanqkd.js} +123 -10
- package/dist/{cli-0gey5fzy.js → cli-e0kmphd6.js} +2 -2
- package/dist/{cli-zh3fp8v2.js → cli-e46hg0xw.js} +1 -1
- package/dist/{cli-jm2vfpfd.js → cli-e5bzrn01.js} +1 -1
- package/dist/{cli-qxnf8z2z.js → cli-excnnjg2.js} +165 -153
- package/dist/{cli-71ptav7x.js → cli-ferkkrg9.js} +1 -1
- package/dist/{cli-vpf89f34.js → cli-fmjd0t1y.js} +3 -3
- package/dist/{cli-rm6hqj3z.js → cli-fyt2g4wd.js} +33 -139
- package/dist/cli-g08zdaec.js +55 -0
- package/dist/{cli-ehq736cn.js → cli-g7s47cc5.js} +4 -4
- package/dist/{cli-82ae98pz.js → cli-gz6q4eve.js} +3 -3
- package/dist/{cli-82jg7fp9.js → cli-he5nxaa8.js} +1 -1
- package/dist/{cli-rqkxrmgw.js → cli-je518txm.js} +39 -14
- package/dist/{cli-8hs41zy2.js → cli-jtjtabxe.js} +1 -1
- package/dist/{cli-611a1294.js → cli-k0wwfqbg.js} +18 -1
- package/dist/{cli-nzxr556s.js → cli-k1nje3v3.js} +38 -43
- package/dist/{cli-3h4nhjn8.js → cli-k8196sh2.js} +9 -7
- package/dist/{cli-afrrppsw.js → cli-k899ehvh.js} +2 -2
- package/dist/{cli-0731hvbw.js → cli-m0da8j35.js} +23 -9
- package/dist/cli-m2z8jagn.js +12078 -0
- package/dist/{cli-8veytvbh.js → cli-mkk9rnzz.js} +5 -5
- package/dist/{cli-7wspm5gm.js → cli-n6bv12jn.js} +528 -6
- package/dist/{cli-spknvxbq.js → cli-nmzhp1b5.js} +14 -12
- package/dist/{cli-p599f1qm.js → cli-p82cnnjq.js} +1 -1
- package/dist/{cli-xtjhfmpc.js → cli-pc6n7fre.js} +1 -1
- package/dist/{cli-vbyfwh92.js → cli-pjctyea3.js} +2 -2
- package/dist/{cli-nmbmy64z.js → cli-pq73h5mz.js} +3 -3
- package/dist/{cli-aa6yxc7m.js → cli-pz4rk021.js} +1 -1
- package/dist/{cli-7wcyk2s5.js → cli-q3tkegby.js} +1 -1
- package/dist/cli-qghe0h66.js +67 -0
- package/dist/{cli-6s7vdcta.js → cli-qh4d4tvk.js} +4 -4
- package/dist/{cli-mmwm7zbf.js → cli-rtt8g4v9.js} +1 -1
- package/dist/{cli-kg8hchyg.js → cli-sa02jsqt.js} +1 -1
- package/dist/{cli-1h3wcpbw.js → cli-snpj5zbj.js} +2 -2
- package/dist/{cli-a7v854rf.js → cli-tgrv9cjx.js} +263 -12
- package/dist/{cli-a47xy8eq.js → cli-vz22fyqv.js} +186 -54
- package/dist/{cli-1ayk1eas.js → cli-wk5yrfwd.js} +285 -42
- package/dist/{cli-1338796d.js → cli-ws19768n.js} +1755 -119
- package/dist/{cli-70r77n04.js → cli-xae4g51r.js} +1 -1
- package/dist/{cli-gf5hbmdr.js → cli-yjdgea32.js} +3 -3
- package/dist/{cli-xbkdm710.js → cli-yndmfxb3.js} +3 -3
- package/dist/{cli-rhw40jgb.js → cli-yyj3j5m4.js} +2 -2
- package/dist/cli.js +26 -25
- package/dist/{cloudflare-workers-ai-system-one-bm60fjm0.js → cloudflare-workers-ai-system-one-ef2a980q.js} +2 -2
- package/dist/{codex-adapter-q6k3gnyg.js → codex-adapter-cf2yxe9m.js} +3 -2
- package/dist/{codex-hook-9efm2x1m.js → codex-hook-a0c6g6t6.js} +1 -1
- package/dist/{codex-session-runner-3mwmk3d9.js → codex-session-runner-6vfzdgbd.js} +3 -2
- package/dist/{commands-evrenjn9.js → commands-t3mfy7vn.js} +9 -3
- package/dist/{controller-xjp8vswh.js → controller-q7xrbaz0.js} +8 -3
- package/dist/{controllers-connect-pp5jnxvc.js → controllers-connect-c6nm70kg.js} +20 -19
- package/dist/{cursor-adapter-yf47w4t0.js → cursor-adapter-ftd81ga8.js} +1 -1
- package/dist/{db-zxpw6aht.js → db-dk572x25.js} +3 -3
- package/dist/{db-rgfsh91f.js → db-vemtpmcc.js} +14 -4
- package/dist/{devtools-82fzqra8.js → devtools-qxfgbsqz.js} +1 -1
- package/dist/{dispatcher-smasgws7.js → dispatcher-kh7ecxh9.js} +17 -16
- package/dist/{docker-rrjhaqt9.js → docker-c1a3gqmf.js} +89 -22
- package/dist/{dsh-adapter-ftyy8dnp.js → dsh-adapter-v8628r89.js} +1 -1
- package/dist/{e2b-w1p0bnpm.js → e2b-fr12yccx.js} +5 -5
- package/dist/{e2b-qcaj675y.js → e2b-x6ywzak3.js} +3 -3
- package/dist/{easter-egg-3d-yyts7p16.js → easter-egg-3d-nenw4x63.js} +1 -1
- package/dist/{embedding-space-1z0mbg95.js → embedding-space-0jepb239.js} +6 -6
- package/dist/{embeddings-ygeg7tm7.js → embeddings-xxt49yxg.js} +1 -1
- package/dist/{execute-14qhqp4d.js → execute-1mhw6e4x.js} +1 -1
- package/dist/{external-channel-ttgrs3sx.js → external-channel-55v12e60.js} +2 -2
- package/dist/{fx-adapter-gt6pncj2.js → fx-adapter-bs8wqrk5.js} +6 -5
- package/dist/{google-generative-ai-yvmdxyxa.js → google-generative-ai-v94hr7h0.js} +5 -5
- package/dist/{google-vertex-jrtdh9gs.js → google-vertex-g7vr6cf8.js} +5 -5
- package/dist/{grok-adapter-sm4m5k4g.js → grok-adapter-gpjqmkcq.js} +6 -5
- package/dist/handlers-zxmp1ta4.js +32 -0
- package/dist/{heartbeat-qahz4p62.js → heartbeat-k42svnr0.js} +20 -19
- package/dist/{hook-4fd5wq5w.js → hook-114m7hap.js} +4 -3
- package/dist/{hook-kj3d398m.js → hook-6nphweh0.js} +24 -9
- package/dist/{http-1ted0j1k.js → http-r8cc4wz7.js} +1196 -211
- package/dist/index-a8phxqnk.js +37 -0
- package/dist/{index-q4b917zs.js → index-e3s8wmxy.js} +1 -1
- package/dist/{index-6rq3r5zb.js → index-fpk4bcd9.js} +1 -1
- package/dist/{index-5rk6rfzr.js → index-fvzke5kx.js} +1 -1
- package/dist/index-k6h00t4c.js +30 -0
- package/dist/{index-7v51awbn.js → index-nh3wyqdt.js} +1027 -577
- package/dist/{index-yqhz88yy.js → index-pxn30mse.js} +8 -8
- package/dist/index-xrz7te8h.js +33 -0
- package/dist/{keepalive-6cf311t5.js → keepalive-x060nt9p.js} +3 -3
- package/dist/lead-956sd2yc.js +71 -0
- package/dist/{maintenance-a4bm1bmt.js → maintenance-2xxfexhf.js} +1 -1
- package/dist/{mistral-conversations-4pmwj1c0.js → mistral-conversations-3gjpdb4b.js} +4 -4
- package/dist/{oauth-refresh-sweep-3h0dfw1k.js → oauth-refresh-sweep-fckc4ar5.js} +2 -2
- package/dist/{onboard-myn5hraa.js → onboard-zc48j237.js} +5 -5
- package/dist/{openai-codex-responses-44j573nm.js → openai-codex-responses-8pbxq9h5.js} +5 -5
- package/dist/{openai-completions-a4gee2a8.js → openai-completions-rm00s912.js} +4 -4
- package/dist/{openai-decisions-w4qkrcar.js → openai-decisions-8v957r2r.js} +1 -1
- package/dist/{openai-responses-rbp6h5kr.js → openai-responses-agxpf96k.js} +5 -5
- package/dist/{opencode-adapter-p2nkb5am.js → opencode-adapter-r690h1cr.js} +4 -3
- package/dist/{otel-impl-a1a0ef6b.js → otel-impl-r5xbh81q.js} +4 -4
- package/dist/{pi-mono-adapter-rzmpb72a.js → pi-mono-adapter-c7v8sdec.js} +7 -6
- package/dist/{pricing-refresh-83drrnxj.js → pricing-refresh-3pmre11v.js} +3 -3
- package/dist/{rbac-roles-s8y6ecb5.js → rbac-roles-3vbshwsb.js} +1 -1
- package/dist/{rbac-roles-0fb6e7pj.js → rbac-roles-910mmkgz.js} +2 -2
- package/dist/{render-v2-twmbgrz4.js → render-v2-pxykkbab.js} +3 -3
- package/dist/{resume-zfqc36xj.js → resume-adda0bwy.js} +6 -6
- package/dist/{runtime-qwggdpqr.js → runtime-fnbm7stk.js} +1 -1
- package/dist/{session-access-sc9pn9da.js → session-access-2fvamrdc.js} +4 -4
- package/dist/{session-source-backfill-qh07xr8g.js → session-source-backfill-08xvv0sm.js} +2 -2
- package/dist/{setup-4fr0m7a1.js → setup-1aqmvmh1.js} +3 -3
- package/dist/{typesafe-system-one-5mavm259.js → typesafe-system-one-0bmqcxfg.js} +2 -2
- package/dist/{virtual-modules-fh4ha15b.js → virtual-modules-d2pjk9j1.js} +5 -5
- package/dist/{work-objects-0wr8zxct.js → work-objects-9s0yrm5j.js} +20 -19
- package/dist/{worker-follow-up-ktyyazyp.js → worker-follow-up-3q78fqmr.js} +17 -16
- package/dist/worker-y15hy08h.js +71 -0
- package/openapi.json +289 -4
- package/package.json +9 -8
- package/src/apps/definition.ts +6 -5
- package/src/be/db/page-theme-tokens.ts +12 -0
- package/src/be/db/script-runs.ts +23 -0
- package/src/be/db/tasks/read.ts +131 -4
- package/src/be/db/tasks/write.ts +19 -0
- package/src/be/db-client.ts +114 -11
- package/src/be/db.ts +49 -6
- package/src/be/memory/providers/sqlite-store.ts +49 -12
- 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/migrations/269_pages_design_system.sql +23 -0
- 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 +22 -0
- package/src/be/seed/schedules-seeder.ts +0 -10
- package/src/be/seed-scripts/catalog/catalog-report.inline.ts +2 -0
- package/src/be/seed-scripts/catalog/catalog-report.ts +2 -0
- package/src/be/seed-scripts/catalog/memory-eval.ts +2 -0
- package/src/be/seed-scripts/catalog/swarm-update-check.ts +150 -0
- package/src/be/seed-scripts/catalog/task-failure-audit.ts +5 -1
- package/src/be/seed-scripts/index.ts +9 -0
- package/src/be/seed-skills/bundled-files.generated.json +3 -3
- package/src/be/swarm-config-guard.ts +32 -0
- package/src/be/task-auto-title-listener.ts +127 -0
- package/src/commands/controller.ts +4 -0
- package/src/commands/entrypoint-api-readiness.ts +74 -0
- package/src/commands/onboard/agent-fs-version.ts +1 -1
- package/src/commands/profile-sync.ts +151 -16
- package/src/commands/runner.ts +15 -7
- package/src/hooks/hook.ts +20 -9
- package/src/http/index.ts +4 -1
- package/src/http/list-total-cache.ts +14 -7
- package/src/http/memory.ts +38 -9
- package/src/http/pages-public.ts +93 -129
- package/src/http/pages.ts +94 -21
- package/src/http/script-runs.ts +174 -5
- package/src/http/status.ts +10 -2
- package/src/http/tasks.ts +4 -2
- package/src/pages/browser-sdk.ts +48 -21
- package/src/pages/color-literals.ts +115 -0
- package/src/pages/page-head.ts +766 -0
- package/src/pages/version.ts +3 -0
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +1 -0
- package/src/providers/codex-app-server.ts +27 -1
- package/src/providers/fx-adapter.ts +1 -1
- package/src/providers/pi-mono-extension.ts +30 -15
- 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/script-workflows/workflow-ctx.ts +8 -0
- package/src/scripts-runtime/types/stdlib.d.ts +22 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +22 -0
- package/src/server-user.ts +1 -0
- package/src/slack/commands.ts +5 -1
- package/src/slack/handlers.ts +37 -2
- package/src/slack/left-threads.ts +27 -4
- package/src/tasks/follow-up-gate.ts +318 -0
- package/src/tasks/follow-up-redelegation-guard.ts +155 -0
- package/src/tasks/worker-follow-up.ts +21 -1
- package/src/tests/claude-artifacts-read-script.test.ts +209 -0
- package/src/tests/codex-app-server.test.ts +59 -0
- package/src/tests/codex-gateway-route.test.ts +50 -1
- package/src/tests/create-page-tool.test.ts +501 -0
- package/src/tests/db-client-busy-retry.test.ts +81 -0
- package/src/tests/db-client.test.ts +21 -0
- package/src/tests/db-task-read-contract.test.ts +9 -2
- package/src/tests/db-task-write-contract.test.ts +7 -1
- package/src/tests/db-tasks-read.test.ts +21 -0
- package/src/tests/entrypoint-api-readiness.test.ts +416 -210
- package/src/tests/explicit-model-validation.test.ts +14 -8
- package/src/tests/fixtures/pages/head-v1.html +56 -0
- package/src/tests/follow-up-gate-shadow.test.ts +513 -0
- package/src/tests/follow-up-redelivery-guard.test.ts +432 -202
- package/src/tests/identity-field-budget.test.ts +1 -1
- package/src/tests/internal-ai/credentials.test.ts +62 -0
- package/src/tests/internal-ai/task-title.test.ts +283 -0
- package/src/tests/list-total-cache.test.ts +27 -2
- package/src/tests/memory-fts-populate.test.ts +11 -9
- package/src/tests/memory-retrieval-purge.test.ts +80 -0
- package/src/tests/merge-gate-detect-changes.test.ts +145 -0
- package/src/tests/migration-260-runtime-pool-cache.test.ts +118 -0
- package/src/tests/page-proxy.test.ts +1 -1
- package/src/tests/page-session-dev-cookie.test.ts +188 -0
- package/src/tests/pages-frame-ancestors.test.ts +28 -1
- package/src/tests/pages-head-v2.test.ts +534 -0
- package/src/tests/pages-http.test.ts +330 -0
- package/src/tests/pages-public-html.test.ts +43 -0
- package/src/tests/pages-storage.test.ts +182 -0
- package/src/tests/pages-versioning.test.ts +72 -0
- package/src/tests/profile-sync-stale-writeback.test.ts +334 -0
- package/src/tests/profile-sync.test.ts +11 -15
- package/src/tests/query-audit-plans.test.ts +27 -0
- 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/scheduled-task-required-params-migration.test.ts +3 -1
- package/src/tests/script-connections.test.ts +13 -0
- package/src/tests/script-runs-http.test.ts +684 -2
- package/src/tests/seed-registry.test.ts +28 -6
- package/src/tests/seed-schedules.test.ts +61 -8
- package/src/tests/seed-scripts.test.ts +168 -2
- package/src/tests/slack-leave-thread.test.ts +145 -5
- package/src/tests/slack-render-v2-query-plans.test.ts +24 -2
- package/src/tests/status.test.ts +11 -0
- package/src/tests/swarm-diff.test.ts +121 -2
- package/src/tests/task-auto-title-config-guard.test.ts +37 -0
- package/src/tests/task-auto-title-listener.test.ts +165 -0
- package/src/tests/tasks-readable-row.test.ts +155 -0
- package/src/tests/theme-import.test.ts +758 -0
- package/src/tests/themes-page-theme.test.ts +155 -0
- package/src/tests/themes-presets.test.ts +172 -0
- package/src/tests/workflow-script-principal.test.ts +2 -1
- package/src/themes/classics.ts +949 -0
- package/src/themes/import.ts +420 -0
- package/src/themes/page-fields.ts +40 -0
- package/src/themes/page-theme.ts +102 -0
- package/src/themes/presets.ts +561 -0
- package/src/themes/tokens.ts +218 -0
- package/src/tools/app-upsert.ts +1 -1
- package/src/tools/create-page.ts +118 -12
- package/src/tools/send-task.ts +35 -47
- package/src/tools/slack-leave-thread.ts +1 -1
- package/src/tools/task-dedup.ts +45 -21
- package/src/tools/utils.ts +33 -0
- package/src/types.ts +83 -3
- package/src/utils/constants.ts +19 -0
- package/src/utils/follow-up-gate-schedule-ids.ts +13 -0
- package/src/utils/internal-ai/credentials.ts +146 -93
- package/src/utils/internal-ai/index.ts +10 -0
- package/src/utils/internal-ai/models.ts +18 -0
- package/src/utils/internal-ai/task-title.ts +282 -0
- package/src/utils/page-session.ts +37 -0
- package/templates/schedules/daily-status-report/content.md +88 -2
- package/templates/schedules/daily-swarm-update-check/content.md +16 -40
- package/templates/skills/apps/SKILL.md +1 -1
- package/templates/skills/apps/content.md +1 -1
- package/templates/skills/composio-google-docs/content.md +10 -0
- package/templates/skills/heartbeat-runbook/content.md +1 -1
- package/templates/skills/pages/config.json +1 -1
- package/templates/skills/pages/content.md +539 -86
- package/templates/skills/pages/files/examples/annotated-pr.html +21 -12
- package/templates/skills/pages/files/examples/multiplayer-board.html +41 -41
- package/templates/skills/pages/files/examples/report-page.html +125 -124
- package/templates/skills/script-workflows/content.md +19 -1
- package/templates/skills/swarm-docs/content.md +1 -0
- package/templates/skills/swarm-scripts/SKILL.md +2 -0
- package/templates/skills/swarm-scripts/content.md +2 -0
- package/dist/cli-2pnswgh8.js +0 -189
- package/dist/cli-dznrgaep.js +0 -30
- package/dist/cli-w3hcrxht.js +0 -6985
- package/dist/handlers-vwmba1m2.js +0 -31
- package/dist/index-5r71rf98.js +0 -32
- package/dist/index-anmwxs87.js +0 -29
- package/dist/index-spp0ra5c.js +0 -36
- package/dist/lead-fjy418cw.js +0 -69
- package/dist/worker-0c5tyzqe.js +0 -69
- package/templates/schedules/daily-workflow-health-audit/config.json +0 -19
- package/templates/schedules/daily-workflow-health-audit/content.md +0 -191
package/src/tools/task-dedup.ts
CHANGED
|
@@ -34,11 +34,53 @@ const HIGH_SIMILARITY_THRESHOLD = 0.8;
|
|
|
34
34
|
/** Lower threshold used when the tasks also target the same agent */
|
|
35
35
|
const SAME_AGENT_SIMILARITY_THRESHOLD = 0.6;
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Rows one duplicate check scans. The JS similarity match needs more than the
|
|
39
|
+
* query's default 50 (an older duplicate can sit behind newer unrelated tasks),
|
|
40
|
+
* but the scan must stay bounded: send-task runs it twice per call, once inside
|
|
41
|
+
* the write transaction.
|
|
42
|
+
*/
|
|
43
|
+
export const DEDUP_SCAN_LIMIT = 500;
|
|
44
|
+
|
|
37
45
|
export interface DedupMatch {
|
|
38
46
|
task: AgentTask;
|
|
39
47
|
reason: string;
|
|
40
48
|
}
|
|
41
49
|
|
|
50
|
+
/**
|
|
51
|
+
* The description-similarity rules shared by the duplicate check and the
|
|
52
|
+
* follow-up re-delegation guard: a very similar description matches any
|
|
53
|
+
* target, a moderately similar one matches only the same target agent.
|
|
54
|
+
*/
|
|
55
|
+
export function matchSimilarTask(
|
|
56
|
+
existing: AgentTask,
|
|
57
|
+
opts: { taskDescription: string; targetAgentId?: string },
|
|
58
|
+
): DedupMatch | null {
|
|
59
|
+
const similarity = jaccardSimilarity(existing.task, opts.taskDescription);
|
|
60
|
+
|
|
61
|
+
// Very similar task description: likely duplicate regardless of target
|
|
62
|
+
if (similarity > HIGH_SIMILARITY_THRESHOLD) {
|
|
63
|
+
return {
|
|
64
|
+
task: existing,
|
|
65
|
+
reason: `similar task description (${(similarity * 100).toFixed(0)}% match)`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Same target agent + moderately similar task: lower threshold
|
|
70
|
+
if (
|
|
71
|
+
opts.targetAgentId &&
|
|
72
|
+
existing.agentId === opts.targetAgentId &&
|
|
73
|
+
similarity > SAME_AGENT_SIMILARITY_THRESHOLD
|
|
74
|
+
) {
|
|
75
|
+
return {
|
|
76
|
+
task: existing,
|
|
77
|
+
reason: `similar task to same agent (${(similarity * 100).toFixed(0)}% match)`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
42
84
|
/**
|
|
43
85
|
* Check for duplicate tasks created recently.
|
|
44
86
|
* Returns the first matching duplicate, or null if none found.
|
|
@@ -54,6 +96,7 @@ export async function findDuplicateTask(opts: {
|
|
|
54
96
|
const recentTasks = await findRecentSimilarTasks({
|
|
55
97
|
creatorAgentId: opts.creatorAgentId,
|
|
56
98
|
windowMinutes: opts.windowMinutes ?? 10,
|
|
99
|
+
limit: DEDUP_SCAN_LIMIT,
|
|
57
100
|
});
|
|
58
101
|
|
|
59
102
|
for (const existing of recentTasks) {
|
|
@@ -70,27 +113,8 @@ export async function findDuplicateTask(opts: {
|
|
|
70
113
|
};
|
|
71
114
|
}
|
|
72
115
|
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
// 2. Very similar task description — likely duplicate regardless of target
|
|
76
|
-
if (similarity > HIGH_SIMILARITY_THRESHOLD) {
|
|
77
|
-
return {
|
|
78
|
-
task: existing,
|
|
79
|
-
reason: `similar task description (${(similarity * 100).toFixed(0)}% match)`,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// 3. Same target agent + moderately similar task — lower threshold
|
|
84
|
-
if (
|
|
85
|
-
opts.targetAgentId &&
|
|
86
|
-
existing.agentId === opts.targetAgentId &&
|
|
87
|
-
similarity > SAME_AGENT_SIMILARITY_THRESHOLD
|
|
88
|
-
) {
|
|
89
|
-
return {
|
|
90
|
-
task: existing,
|
|
91
|
-
reason: `similar task to same agent (${(similarity * 100).toFixed(0)}% match)`,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
116
|
+
const match = matchSimilarTask(existing, opts);
|
|
117
|
+
if (match) return match;
|
|
94
118
|
}
|
|
95
119
|
|
|
96
120
|
return null;
|
package/src/tools/utils.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { upsertKv } from "../be/db";
|
|
|
22
22
|
import { MCP_OVERFLOW_NAMESPACE, mcpOverflowNamespace } from "../kv-overflow";
|
|
23
23
|
import { joinKvPath, type KvShapeEntry, splitKvPath, summarizeKvShape } from "../kv-view";
|
|
24
24
|
import { withSpan } from "../otel";
|
|
25
|
+
import { COLOR_LITERAL_THRESHOLD } from "../pages/color-literals";
|
|
25
26
|
import type { PermissionVerb } from "../rbac/permissions";
|
|
26
27
|
import { SCRIPT_LONG_TIMEOUT_HINT_MS } from "../scripts-runtime/executors/types";
|
|
27
28
|
import { scrubObject, scrubSecrets } from "../utils/secret-scrubber";
|
|
@@ -314,6 +315,37 @@ const workflowLongScriptTimeoutNudge = (r: SwarmToolResult): string | undefined
|
|
|
314
315
|
return hint ? WORKFLOW_LONG_SCRIPT_TIMEOUT_NUDGE : undefined;
|
|
315
316
|
};
|
|
316
317
|
|
|
318
|
+
/**
|
|
319
|
+
* One call can fall into more than one create_page trap, and a result carries
|
|
320
|
+
* one nudge, so this entry joins one sentence per trap that applies: a
|
|
321
|
+
* design version 1 page that uses the design system, theme tokens that set
|
|
322
|
+
* light colors only, and hardcoded colors. The tool computes the flags.
|
|
323
|
+
*/
|
|
324
|
+
const createPageNudge = (r: SwarmToolResult): string | undefined => {
|
|
325
|
+
if (!r.ok) return undefined;
|
|
326
|
+
const data = r.data as
|
|
327
|
+
| {
|
|
328
|
+
designVersion?: unknown;
|
|
329
|
+
usesDesignSystem?: unknown;
|
|
330
|
+
lightOnlyColors?: unknown;
|
|
331
|
+
colorLiteralCount?: unknown;
|
|
332
|
+
}
|
|
333
|
+
| undefined;
|
|
334
|
+
const count = data?.colorLiteralCount;
|
|
335
|
+
const sentences = [
|
|
336
|
+
data?.designVersion === 1 && data.usesDesignSystem === true
|
|
337
|
+
? "This page is on designVersion 1 (the original look), so design system classes, tokens and themes have no effect. Pass designVersion: 2."
|
|
338
|
+
: undefined,
|
|
339
|
+
data?.lightOnlyColors === true
|
|
340
|
+
? "Your themeTokens set colors for light mode only. Dark mode keeps the preset's dark colors; add a dark map (or a .dark block) to restyle it too."
|
|
341
|
+
: undefined,
|
|
342
|
+
typeof count === "number" && count >= COLOR_LITERAL_THRESHOLD
|
|
343
|
+
? `This page hardcodes ${count} color values. Use theme tokens (var(--color-primary), bg-primary, text-muted-foreground) so theme and dark-mode switches restyle it. Until then the page stays light in a dark dashboard.`
|
|
344
|
+
: undefined,
|
|
345
|
+
].filter((sentence) => sentence !== undefined);
|
|
346
|
+
return sentences.length > 0 ? sentences.join(" ") : undefined;
|
|
347
|
+
};
|
|
348
|
+
|
|
317
349
|
/**
|
|
318
350
|
* Central conditional nudges, keyed by tool name. Applied by the finalize
|
|
319
351
|
* pipeline when the tool did not set an explicit nudge. Keep entries to a
|
|
@@ -333,6 +365,7 @@ export const NUDGES: Record<string, (result: SwarmToolResult) => string | undefi
|
|
|
333
365
|
"patch-workflow-node": workflowLongScriptTimeoutNudge,
|
|
334
366
|
"slack-download-file": slackDownloadFileNudge,
|
|
335
367
|
"slack-read": slackReadNudge,
|
|
368
|
+
create_page: createPageNudge,
|
|
336
369
|
"script-search": (r) => {
|
|
337
370
|
if (!r.ok) return undefined;
|
|
338
371
|
// proxyScriptsApi wraps the parsed HTTP body as data = { status, data },
|
package/src/types.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { normalizeAssetKey } from "./assets/key";
|
|
6
6
|
import { isSafeBundlePath } from "./extensions/bundle-path";
|
|
7
|
+
import { THEME_IMPORT_FORMATS } from "./themes/import";
|
|
7
8
|
import { MAX_PROFILE_FILE_LENGTH } from "./utils/constants";
|
|
8
9
|
import { z } from "./utils/zod-openapi";
|
|
9
10
|
|
|
@@ -1695,6 +1696,8 @@ export const AgentLogEventTypeSchema = z.enum([
|
|
|
1695
1696
|
"task_deferred_wait_woke",
|
|
1696
1697
|
// The lead follow-up was skipped because the woken waiter already carries the result
|
|
1697
1698
|
"task_follow_up_suppressed",
|
|
1699
|
+
// Shadow follow-up gate: what a verifier said about a gated run (never skips the follow-up)
|
|
1700
|
+
"task_follow_up_gate_shadow",
|
|
1698
1701
|
// store-progress refused a completion once because its citations were inaccurate
|
|
1699
1702
|
"task_citation_check_refused",
|
|
1700
1703
|
"channel_message",
|
|
@@ -2212,6 +2215,39 @@ export const RuntimeControllerSchema = RuntimeControllerCreateSchema.omit({ secr
|
|
|
2212
2215
|
.openapi("RuntimeController");
|
|
2213
2216
|
export type RuntimeController = z.infer<typeof RuntimeControllerSchema>;
|
|
2214
2217
|
|
|
2218
|
+
export const RuntimePoolCacheSchema = z
|
|
2219
|
+
.object({
|
|
2220
|
+
mountPath: z
|
|
2221
|
+
.string()
|
|
2222
|
+
.min(2)
|
|
2223
|
+
.refine((path) => {
|
|
2224
|
+
const parts = path.split("/").filter(Boolean);
|
|
2225
|
+
const normalized = `/${parts.join("/")}`;
|
|
2226
|
+
const protectedPaths = [
|
|
2227
|
+
"/workspace/personal",
|
|
2228
|
+
"/workspace/shared",
|
|
2229
|
+
...["/home/worker", "/root"].flatMap((home) =>
|
|
2230
|
+
[".ssh", ".codex", ".claude", ".claude.json", ".pi", ".config", ".local"].map(
|
|
2231
|
+
(directory) => `${home}/${directory}`,
|
|
2232
|
+
),
|
|
2233
|
+
),
|
|
2234
|
+
];
|
|
2235
|
+
return (
|
|
2236
|
+
path.startsWith("/") &&
|
|
2237
|
+
parts.length > 0 &&
|
|
2238
|
+
!path.includes("\0") &&
|
|
2239
|
+
!parts.some((part) => part === "." || part === "..") &&
|
|
2240
|
+
!protectedPaths.some(
|
|
2241
|
+
(protectedPath) =>
|
|
2242
|
+
protectedPath === normalized ||
|
|
2243
|
+
protectedPath.startsWith(`${normalized}/`) ||
|
|
2244
|
+
normalized.startsWith(`${protectedPath}/`),
|
|
2245
|
+
)
|
|
2246
|
+
);
|
|
2247
|
+
}, "Cache mountPath must be absolute, without . or .., and must not cover worker homes, workspaces or credential directories"),
|
|
2248
|
+
})
|
|
2249
|
+
.strict();
|
|
2250
|
+
|
|
2215
2251
|
export const RuntimePoolCreateSchema = z
|
|
2216
2252
|
.object({
|
|
2217
2253
|
name: z.string().min(1).max(200),
|
|
@@ -2226,6 +2262,7 @@ export const RuntimePoolCreateSchema = z
|
|
|
2226
2262
|
delivery: DeliveryModeSchema.nullable().default(null),
|
|
2227
2263
|
env: z.record(z.string(), z.string()).default({}),
|
|
2228
2264
|
resources: RuntimeResourcesSchema.nullable().default(null),
|
|
2265
|
+
cache: RuntimePoolCacheSchema.nullable().default(null),
|
|
2229
2266
|
ttlSec: z.number().int().positive().nullable().default(null),
|
|
2230
2267
|
enabled: z.boolean().default(true),
|
|
2231
2268
|
})
|
|
@@ -2239,6 +2276,7 @@ export const RuntimePoolUpdateSchema = RuntimePoolCreateSchema.partial().extend(
|
|
|
2239
2276
|
delivery: DeliveryModeSchema.nullable().optional(),
|
|
2240
2277
|
env: z.record(z.string(), z.string()).optional(),
|
|
2241
2278
|
resources: RuntimeResourcesSchema.nullable().optional(),
|
|
2279
|
+
cache: RuntimePoolCacheSchema.nullable().optional(),
|
|
2242
2280
|
ttlSec: z.number().int().positive().nullable().optional(),
|
|
2243
2281
|
enabled: z.boolean().optional(),
|
|
2244
2282
|
});
|
|
@@ -2738,10 +2776,36 @@ export type PageContentType = z.infer<typeof PageContentTypeSchema>;
|
|
|
2738
2776
|
export const PageAuthModeSchema = z.enum(["public", "authed", "password"]);
|
|
2739
2777
|
export type PageAuthMode = z.infer<typeof PageAuthModeSchema>;
|
|
2740
2778
|
|
|
2779
|
+
// Per-page theme overrides as stored: page token names (`--color-primary`,
|
|
2780
|
+
// `--radius-lg`) mapped to CSS values, per mode. Writes go through
|
|
2781
|
+
// `importThemeTokens` (src/themes/import.ts), which maps outside names onto the
|
|
2782
|
+
// token contract and checks every value, so the stored form is always normalized.
|
|
2783
|
+
export const PageThemeTokensSchema = z
|
|
2784
|
+
.object({
|
|
2785
|
+
light: z.record(z.string(), z.string()),
|
|
2786
|
+
dark: z.record(z.string(), z.string()),
|
|
2787
|
+
})
|
|
2788
|
+
.openapi("PageThemeTokens");
|
|
2789
|
+
|
|
2790
|
+
// Write-side `themeTokens` input, mirroring `ThemeImportInput` (src/themes/import.ts)
|
|
2791
|
+
// as one flat object: the default `tokens` format sends `light` / `dark` maps,
|
|
2792
|
+
// the `css` format sends a CSS variable block. `importThemeTokens` checks which
|
|
2793
|
+
// fields go with which format, so a mismatch gets a precise error message. The
|
|
2794
|
+
// `format` enum is the importer's own list, so a new importer needs no change
|
|
2795
|
+
// here unless it takes a new payload field.
|
|
2796
|
+
export const PageThemeTokensInputSchema = z.strictObject({
|
|
2797
|
+
format: z.enum(THEME_IMPORT_FORMATS).optional(),
|
|
2798
|
+
light: z.record(z.string(), z.string()).optional(),
|
|
2799
|
+
dark: z.record(z.string(), z.string()).optional(),
|
|
2800
|
+
css: z.string().optional(),
|
|
2801
|
+
});
|
|
2802
|
+
export type PageThemeTokensInput = z.infer<typeof PageThemeTokensInputSchema>;
|
|
2803
|
+
|
|
2741
2804
|
// PageSnapshot captures the mutable content fields frozen per-version in
|
|
2742
2805
|
// page_versions.snapshot. Omits id / agentId / slug / timestamps (these are
|
|
2743
2806
|
// invariant across versions for a given page id; the slug is a parent-only
|
|
2744
|
-
// identifier).
|
|
2807
|
+
// identifier). The design fields are optional because snapshots taken before
|
|
2808
|
+
// design version 2 do not carry them (they read as design version 1).
|
|
2745
2809
|
export const PageSnapshotSchema = z
|
|
2746
2810
|
.object({
|
|
2747
2811
|
title: z.string(),
|
|
@@ -2751,6 +2815,9 @@ export const PageSnapshotSchema = z
|
|
|
2751
2815
|
passwordHash: z.string().optional(),
|
|
2752
2816
|
body: z.string(),
|
|
2753
2817
|
needsCredentials: z.array(z.string()).optional(),
|
|
2818
|
+
theme: z.string().nullable().optional(),
|
|
2819
|
+
themeTokens: PageThemeTokensSchema.nullable().optional(),
|
|
2820
|
+
designVersion: z.number().int().min(1).optional(),
|
|
2754
2821
|
})
|
|
2755
2822
|
.openapi("PageSnapshot");
|
|
2756
2823
|
export type PageSnapshot = z.infer<typeof PageSnapshotSchema>;
|
|
@@ -2768,6 +2835,9 @@ export const PageSchema = z
|
|
|
2768
2835
|
passwordHash: z.string().optional(),
|
|
2769
2836
|
body: z.string(),
|
|
2770
2837
|
needsCredentials: z.array(z.string()).optional(),
|
|
2838
|
+
theme: z.string().nullable(),
|
|
2839
|
+
themeTokens: PageThemeTokensSchema.nullable(),
|
|
2840
|
+
designVersion: z.number().int().min(1),
|
|
2771
2841
|
viewCount: z.number().int().min(0).default(0),
|
|
2772
2842
|
createdAt: z.string(),
|
|
2773
2843
|
updatedAt: z.string(),
|
|
@@ -2791,8 +2861,15 @@ export type WorkflowSummary = Omit<
|
|
|
2791
2861
|
"definition" | "triggers" | "cooldown" | "input" | "triggerSchema"
|
|
2792
2862
|
> & { nodeCount: number };
|
|
2793
2863
|
|
|
2794
|
-
/**
|
|
2795
|
-
|
|
2864
|
+
/**
|
|
2865
|
+
* `/api/pages` list item: drops the (potentially huge) `body` and `passwordHash`,
|
|
2866
|
+
* plus the design fields (`theme`, `themeTokens`, `designVersion`). List views do
|
|
2867
|
+
* not render a page, so those only come with `?fields=full` or the get-by-id route.
|
|
2868
|
+
*/
|
|
2869
|
+
export type PageSummary = Omit<
|
|
2870
|
+
Page,
|
|
2871
|
+
"body" | "passwordHash" | "theme" | "themeTokens" | "designVersion"
|
|
2872
|
+
>;
|
|
2796
2873
|
|
|
2797
2874
|
/** `/api/schedules` list item — swaps the full `taskTemplate` for a short preview. */
|
|
2798
2875
|
export type ScheduledTaskSummary = Omit<ScheduledTask, "taskTemplate"> & {
|
|
@@ -3114,6 +3191,8 @@ export const ScriptRunSchema = z
|
|
|
3114
3191
|
lastHeartbeatAt: z.string().optional(),
|
|
3115
3192
|
idempotencyKey: z.string().optional(),
|
|
3116
3193
|
requestedByUserId: z.string().optional(),
|
|
3194
|
+
/** Task whose session launched the run; the follow-up re-delegation guard reads it. */
|
|
3195
|
+
sourceTaskId: z.string().optional(),
|
|
3117
3196
|
})
|
|
3118
3197
|
.openapi("ScriptRun");
|
|
3119
3198
|
export type ScriptRun = z.infer<typeof ScriptRunSchema>;
|
|
@@ -3122,6 +3201,7 @@ export const ScriptRunListItemSchema = ScriptRunSchema.omit({
|
|
|
3122
3201
|
source: true,
|
|
3123
3202
|
args: true,
|
|
3124
3203
|
output: true,
|
|
3204
|
+
sourceTaskId: true,
|
|
3125
3205
|
});
|
|
3126
3206
|
export type ScriptRunListItem = z.infer<typeof ScriptRunListItemSchema>;
|
|
3127
3207
|
|
package/src/utils/constants.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { isEnvFlagEnabled, parseEnvFlag } from "./env-flag";
|
|
6
|
+
import { isDevOrTestNodeEnv } from "./node-env";
|
|
6
7
|
|
|
7
8
|
/** Maximum profile-file length in JavaScript characters (not UTF-8 bytes). */
|
|
8
9
|
export const MAX_PROFILE_FILE_LENGTH = 256 * 1024;
|
|
@@ -45,6 +46,24 @@ export function getAppUrl(fallback = DEFAULT_APP_URL): string {
|
|
|
45
46
|
return (getConfiguredAppUrls()[0] || fallback).replace(/\/+$/, "");
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Local dashboard origins (Vite ports and portless `*.localhost`) that may
|
|
51
|
+
* iframe pages without being listed in `APP_URL`. Returned only when NODE_ENV
|
|
52
|
+
* is explicitly development or test: deployed images leave NODE_ENV unset, and
|
|
53
|
+
* an unset value must not let a localhost origin frame production pages.
|
|
54
|
+
*/
|
|
55
|
+
export function getLocalDevAppOrigins(): string[] {
|
|
56
|
+
if (!isDevOrTestNodeEnv()) return [];
|
|
57
|
+
return [
|
|
58
|
+
"http://localhost:5274",
|
|
59
|
+
"http://localhost:5175",
|
|
60
|
+
"http://127.0.0.1:5274",
|
|
61
|
+
"http://127.0.0.1:5175",
|
|
62
|
+
"https://*.localhost",
|
|
63
|
+
"http://*.localhost",
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
|
|
48
67
|
/**
|
|
49
68
|
* Internal API/MCP base URL — how workers/agents and in-process callers reach
|
|
50
69
|
* the API server. May be a private/cluster address (e.g. the Helm ClusterIP
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2
|
+
|
|
3
|
+
/** Split `FOLLOW_UP_GATE_SCHEDULE_IDS`, a comma-separated id list. Blank entries are dropped. */
|
|
4
|
+
export function parseFollowUpGateScheduleIds(raw: string | undefined): string[] {
|
|
5
|
+
return (raw ?? "")
|
|
6
|
+
.split(",")
|
|
7
|
+
.map((id) => id.trim())
|
|
8
|
+
.filter(Boolean);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function isFollowUpGateScheduleId(id: string): boolean {
|
|
12
|
+
return UUID_RE.test(id);
|
|
13
|
+
}
|
|
@@ -108,6 +108,30 @@ export interface ResolveCredentialOptions {
|
|
|
108
108
|
_persistCodexOAuth?: typeof persistCodexOAuth;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Credential kinds in precedence order. `resolveCredentials` walks this list;
|
|
113
|
+
* `resolveCredential` returns the first hit.
|
|
114
|
+
*/
|
|
115
|
+
export const CREDENTIAL_KIND_PRECEDENCE: readonly CredentialKind[] = [
|
|
116
|
+
"openrouter",
|
|
117
|
+
"anthropic",
|
|
118
|
+
"openai",
|
|
119
|
+
"openai-codex",
|
|
120
|
+
"claude-cli",
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
export interface ResolveCredentialsOptions extends ResolveCredentialOptions {
|
|
124
|
+
/**
|
|
125
|
+
* Only resolve these kinds (in precedence order). Lets a caller probe one
|
|
126
|
+
* provider at a time, so the codex OAuth lookup (an HTTP call that can
|
|
127
|
+
* rotate a refresh token) runs only when that kind is actually needed.
|
|
128
|
+
* Default: every kind.
|
|
129
|
+
*/
|
|
130
|
+
kinds?: readonly CredentialKind[];
|
|
131
|
+
/** Stop after this many credentials. Default: no limit. */
|
|
132
|
+
limit?: number;
|
|
133
|
+
}
|
|
134
|
+
|
|
111
135
|
/**
|
|
112
136
|
* Resolve a credential according to the documented precedence. Returns `null`
|
|
113
137
|
* if no credential could be resolved — callers MUST treat null as a graceful
|
|
@@ -125,112 +149,141 @@ export interface ResolveCredentialOptions {
|
|
|
125
149
|
export async function resolveCredential(
|
|
126
150
|
opts: ResolveCredentialOptions = {},
|
|
127
151
|
): Promise<ResolvedCredential | null> {
|
|
152
|
+
const [first] = await resolveCredentials({ ...opts, limit: 1 });
|
|
153
|
+
return first ?? null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Every credential the environment offers, in the precedence order documented
|
|
158
|
+
* on {@link resolveCredential}. Callers that can fall back across providers
|
|
159
|
+
* (task titles) walk the list; `resolveCredential` is the `limit: 1` case.
|
|
160
|
+
* The codex OAuth probe runs only when `openai-codex` is reached, so an env
|
|
161
|
+
* key ahead of it (or `kinds` that exclude it) never triggers the HTTP call.
|
|
162
|
+
*/
|
|
163
|
+
export async function resolveCredentials(
|
|
164
|
+
opts: ResolveCredentialsOptions = {},
|
|
165
|
+
): Promise<ResolvedCredential[]> {
|
|
128
166
|
const env = opts.env ?? process.env;
|
|
129
167
|
const getEnvKey = opts._getEnvApiKey ?? getEnvApiKey;
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
const
|
|
168
|
+
const wanted = opts.kinds ?? CREDENTIAL_KIND_PRECEDENCE;
|
|
169
|
+
const limit = opts.limit ?? Number.POSITIVE_INFINITY;
|
|
170
|
+
const found: ResolvedCredential[] = [];
|
|
133
171
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
apiKey: selectCredential(openrouterKey, undefined, "OPENROUTER_API_KEY").selected,
|
|
140
|
-
modelDefault: resolveRaterModelString("openrouter"),
|
|
141
|
-
};
|
|
172
|
+
for (const kind of CREDENTIAL_KIND_PRECEDENCE) {
|
|
173
|
+
if (found.length >= limit) break;
|
|
174
|
+
if (!wanted.includes(kind)) continue;
|
|
175
|
+
const cred = await resolveKind(kind, opts, env, getEnvKey);
|
|
176
|
+
if (cred) found.push(cred);
|
|
142
177
|
}
|
|
178
|
+
return found;
|
|
179
|
+
}
|
|
143
180
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
181
|
+
async function resolveKind(
|
|
182
|
+
kind: CredentialKind,
|
|
183
|
+
opts: ResolveCredentialOptions,
|
|
184
|
+
env: NodeJS.ProcessEnv,
|
|
185
|
+
getEnvKey: typeof getEnvApiKey,
|
|
186
|
+
): Promise<ResolvedCredential | null> {
|
|
187
|
+
switch (kind) {
|
|
188
|
+
case "openrouter": {
|
|
189
|
+
const key = env.OPENROUTER_API_KEY ?? getEnvKey("openrouter");
|
|
190
|
+
if (!key) return null;
|
|
191
|
+
return {
|
|
192
|
+
kind,
|
|
193
|
+
apiKey: selectCredential(key, undefined, "OPENROUTER_API_KEY").selected,
|
|
194
|
+
modelDefault: resolveRaterModelString(kind),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
case "anthropic": {
|
|
198
|
+
// pi-ai sends this key to api.anthropic.com, so skip it when
|
|
199
|
+
// ANTHROPIC_BASE_URL names a gateway: the key belongs to the gateway (#1800).
|
|
200
|
+
const anthropicBaseUrl = env.ANTHROPIC_BASE_URL?.trim();
|
|
201
|
+
const keyIsForGateway =
|
|
202
|
+
Boolean(anthropicBaseUrl) && !isFirstPartyAnthropicUrl(anthropicBaseUrl ?? "");
|
|
203
|
+
const key = keyIsForGateway ? undefined : (env.ANTHROPIC_API_KEY ?? getEnvKey("anthropic"));
|
|
204
|
+
if (!key) return null;
|
|
205
|
+
return {
|
|
206
|
+
kind,
|
|
207
|
+
apiKey: selectCredential(key, undefined, "ANTHROPIC_API_KEY").selected,
|
|
208
|
+
modelDefault: resolveRaterModelString(kind),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
case "openai": {
|
|
212
|
+
const key = env.OPENAI_API_KEY ?? getEnvKey("openai");
|
|
213
|
+
if (!key) return null;
|
|
214
|
+
return {
|
|
215
|
+
kind,
|
|
216
|
+
apiKey: selectCredential(key, undefined, "OPENAI_API_KEY").selected,
|
|
217
|
+
modelDefault: resolveRaterModelString(kind),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
case "openai-codex":
|
|
221
|
+
// Only if we have apiUrl + apiKey to probe the config store.
|
|
222
|
+
if (!opts.apiUrl || !opts.apiKey) return null;
|
|
223
|
+
return resolveCodexOAuthCredential(opts, opts.apiUrl, opts.apiKey);
|
|
224
|
+
case "claude-cli":
|
|
225
|
+
// `AGENT_SWARM_CLAUDE_OAUTH_TOKEN` is the mirror set by claude-adapter.ts
|
|
226
|
+
// before spawning `claude` — the CLI strips `CLAUDE_CODE_OAUTH_TOKEN` from
|
|
227
|
+
// hook subprocesses (security), so the hook reads the mirror instead.
|
|
228
|
+
if (!env.AGENT_SWARM_CLAUDE_OAUTH_TOKEN && !env.CLAUDE_CODE_OAUTH_TOKEN) return null;
|
|
229
|
+
return { kind, modelDefault: resolveRaterModelString(kind) };
|
|
158
230
|
}
|
|
231
|
+
}
|
|
159
232
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
233
|
+
async function resolveCodexOAuthCredential(
|
|
234
|
+
opts: ResolveCredentialOptions,
|
|
235
|
+
apiUrl: string,
|
|
236
|
+
apiKey: string,
|
|
237
|
+
): Promise<ResolvedCredential | null> {
|
|
238
|
+
const getCodex = opts._getValidCodexOAuth ?? getValidCodexOAuth;
|
|
239
|
+
const getOAuth = opts._getOAuthApiKey ?? resolveOAuthApiKey;
|
|
240
|
+
const persistCodex = opts._persistCodexOAuth ?? persistCodexOAuth;
|
|
241
|
+
try {
|
|
242
|
+
const codexCreds = await getCodex(apiUrl, apiKey);
|
|
243
|
+
if (!codexCreds) return null;
|
|
244
|
+
// pi-ai expects a Record<providerID, OAuthCredentials>.
|
|
245
|
+
const credMap: Record<string, OAuthCredentials> = {
|
|
246
|
+
"openai-codex": {
|
|
247
|
+
access: codexCreds.access,
|
|
248
|
+
refresh: codexCreds.refresh,
|
|
249
|
+
expires: codexCreds.expires,
|
|
250
|
+
},
|
|
167
251
|
};
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
// failure must never prevent the current call from succeeding.
|
|
189
|
-
if (oauthResult.newCredentials) {
|
|
190
|
-
const updated = oauthResult.newCredentials;
|
|
191
|
-
try {
|
|
192
|
-
await persistCodex(opts.apiUrl, opts.apiKey, {
|
|
193
|
-
access: String(updated.access),
|
|
194
|
-
refresh: String(updated.refresh),
|
|
195
|
-
expires: Number(updated.expires),
|
|
196
|
-
accountId: codexCreds.accountId,
|
|
197
|
-
});
|
|
198
|
-
} catch (err) {
|
|
199
|
-
console.error(
|
|
200
|
-
`internal-ai: persistCodexOAuth failed (callerTag=${opts.callerTag ?? "<unset>"}):`,
|
|
201
|
-
err,
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return {
|
|
206
|
-
kind: "openai-codex",
|
|
207
|
-
apiKey: oauthResult.apiKey,
|
|
208
|
-
modelDefault: resolveRaterModelString("openai-codex"),
|
|
209
|
-
};
|
|
210
|
-
}
|
|
252
|
+
const oauthResult = await getOAuth("openai-codex", credMap);
|
|
253
|
+
if (!oauthResult) return null;
|
|
254
|
+
// Persist any rotated refresh token — best-effort, must not block.
|
|
255
|
+
// Wrap defensively here even though the production helper already
|
|
256
|
+
// swallows; tests may inject a throwing hook, and persistence
|
|
257
|
+
// failure must never prevent the current call from succeeding.
|
|
258
|
+
if (oauthResult.newCredentials) {
|
|
259
|
+
const updated = oauthResult.newCredentials;
|
|
260
|
+
try {
|
|
261
|
+
await persistCodex(apiUrl, apiKey, {
|
|
262
|
+
access: String(updated.access),
|
|
263
|
+
refresh: String(updated.refresh),
|
|
264
|
+
expires: Number(updated.expires),
|
|
265
|
+
accountId: codexCreds.accountId,
|
|
266
|
+
});
|
|
267
|
+
} catch (err) {
|
|
268
|
+
console.error(
|
|
269
|
+
`internal-ai: persistCodexOAuth failed (callerTag=${opts.callerTag ?? "<unset>"}):`,
|
|
270
|
+
err,
|
|
271
|
+
);
|
|
211
272
|
}
|
|
212
|
-
} catch (err) {
|
|
213
|
-
console.error(
|
|
214
|
-
`internal-ai: codex OAuth probe failed (callerTag=${opts.callerTag ?? "<unset>"}):`,
|
|
215
|
-
err,
|
|
216
|
-
);
|
|
217
|
-
// Fall through to claude-cli fallback below.
|
|
218
273
|
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// 5. CLAUDE_CODE_OAUTH_TOKEN → claude-cli fallback.
|
|
222
|
-
// `AGENT_SWARM_CLAUDE_OAUTH_TOKEN` is the mirror set by claude-adapter.ts
|
|
223
|
-
// before spawning `claude` — the CLI strips `CLAUDE_CODE_OAUTH_TOKEN` from
|
|
224
|
-
// hook subprocesses (security), so the hook reads the mirror instead.
|
|
225
|
-
if (env.AGENT_SWARM_CLAUDE_OAUTH_TOKEN || env.CLAUDE_CODE_OAUTH_TOKEN) {
|
|
226
274
|
return {
|
|
227
|
-
kind: "
|
|
228
|
-
|
|
275
|
+
kind: "openai-codex",
|
|
276
|
+
apiKey: oauthResult.apiKey,
|
|
277
|
+
modelDefault: resolveRaterModelString("openai-codex"),
|
|
229
278
|
};
|
|
279
|
+
} catch (err) {
|
|
280
|
+
console.error(
|
|
281
|
+
`internal-ai: codex OAuth probe failed (callerTag=${opts.callerTag ?? "<unset>"}):`,
|
|
282
|
+
err,
|
|
283
|
+
);
|
|
284
|
+
// The caller falls through to the claude-cli fallback.
|
|
285
|
+
return null;
|
|
230
286
|
}
|
|
231
|
-
|
|
232
|
-
// 6. No creds.
|
|
233
|
-
return null;
|
|
234
287
|
}
|
|
235
288
|
|
|
236
289
|
// Re-export for convenience to keep imports flat.
|
|
@@ -19,11 +19,14 @@ export {
|
|
|
19
19
|
completeStructuredWithModel,
|
|
20
20
|
} from "./complete-structured.js";
|
|
21
21
|
export {
|
|
22
|
+
CREDENTIAL_KIND_PRECEDENCE,
|
|
22
23
|
type CredentialKind,
|
|
23
24
|
DEFAULT_MODEL,
|
|
24
25
|
type ResolveCredentialOptions,
|
|
26
|
+
type ResolveCredentialsOptions,
|
|
25
27
|
type ResolvedCredential,
|
|
26
28
|
resolveCredential,
|
|
29
|
+
resolveCredentials,
|
|
27
30
|
} from "./credentials.js";
|
|
28
31
|
export {
|
|
29
32
|
MEMORY_RATER_DEFAULT_MODEL,
|
|
@@ -36,3 +39,10 @@ export {
|
|
|
36
39
|
summarizeSession,
|
|
37
40
|
summaryToolSchema,
|
|
38
41
|
} from "./summarize-session.js";
|
|
42
|
+
export {
|
|
43
|
+
type GenerateTaskTitleOptions,
|
|
44
|
+
type GenerateTaskTitleResult,
|
|
45
|
+
generateTaskTitle,
|
|
46
|
+
TASK_TITLE_MODEL_CANDIDATES,
|
|
47
|
+
TASK_TITLE_MODEL_ENV,
|
|
48
|
+
} from "./task-title.js";
|
|
@@ -59,6 +59,24 @@ export function resolveRaterModelString(kind: CredentialKind): string {
|
|
|
59
59
|
return process.env.MEMORY_RATER_MODEL ?? MEMORY_RATER_DEFAULT_MODEL[kind];
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
const PROVIDER_TO_KIND: Record<string, CredentialKind> = {
|
|
63
|
+
openrouter: "openrouter",
|
|
64
|
+
anthropic: "anthropic",
|
|
65
|
+
openai: "openai",
|
|
66
|
+
"openai-codex": "openai-codex",
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Credential kind a model string belongs to. `provider/model-id` maps through
|
|
71
|
+
* the provider prefix; a bare alias (`haiku`) is a `claude -p` model. `null`
|
|
72
|
+
* for a provider the internal-ai layer cannot call.
|
|
73
|
+
*/
|
|
74
|
+
export function kindForModelString(modelStr: string): CredentialKind | null {
|
|
75
|
+
const slash = modelStr.indexOf("/");
|
|
76
|
+
if (slash < 0) return "claude-cli";
|
|
77
|
+
return PROVIDER_TO_KIND[modelStr.slice(0, slash)] ?? null;
|
|
78
|
+
}
|
|
79
|
+
|
|
62
80
|
/**
|
|
63
81
|
* Split a `provider/model-id` string on the FIRST `/` so that OpenRouter
|
|
64
82
|
* compound IDs like `openrouter/deepseek/deepseek-v4.1-flash` parse as
|