@desplega.ai/agent-swarm 2.5.4 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/README.md +2 -2
  2. package/dist/{acp-adapter-ek38cwsv.js → acp-adapter-dgfakfpw.js} +9 -9
  3. package/dist/{actions-92mepq03.js → actions-fbqma8aa.js} +21 -21
  4. package/dist/{amp-adapter-9gaxg015.js → amp-adapter-en0jy6hs.js} +4 -4
  5. package/dist/{app-ctn8gsgx.js → app-k8a5cgpp.js} +3 -3
  6. package/dist/{assistant-zyr5kz47.js → assistant-a16433gr.js} +29 -25
  7. package/dist/{boot-reembed-dwzfttdr.js → boot-reembed-6c1sjfnr.js} +1 -1
  8. package/dist/{boot-reembed-w75maga2.js → boot-reembed-f4nn561n.js} +1 -1
  9. package/dist/{boot-scrub-sweep-e9kq43kf.js → boot-scrub-sweep-d4w9gajf.js} +4 -4
  10. package/dist/{boot-seeds-x28v3fhb.js → boot-seeds-9bmkspyw.js} +4 -4
  11. package/dist/{claude-adapter-f0afqnne.js → claude-adapter-nys1kjxa.js} +9 -9
  12. package/dist/{claude-managed-adapter-x81a7r65.js → claude-managed-adapter-55207973.js} +2 -2
  13. package/dist/{claude-managed-setup-bqzwjd9x.js → claude-managed-setup-3cm35gey.js} +10 -3
  14. package/dist/{claude-sdk-session-7b7whkaq.js → claude-sdk-session-nw5yd8cp.js} +11 -11
  15. package/dist/{cli-c61mbdds.js → cli-03gg9pd1.js} +4 -4
  16. package/dist/{cli-kxpfx2xr.js → cli-0z1xjzwa.js} +2 -2
  17. package/dist/cli-1tem6ns9.js +894 -0
  18. package/dist/{cli-jbnn4vxb.js → cli-24j1jeeb.js} +3 -3
  19. package/dist/{cli-pzatvw94.js → cli-2sjhpsns.js} +2 -2
  20. package/dist/{cli-dew6x255.js → cli-39aet655.js} +2 -2
  21. package/dist/{cli-g1cw7xtt.js → cli-39rrw8yp.js} +1 -1
  22. package/dist/{cli-x0f2v69c.js → cli-3aqd4jaa.js} +12 -7
  23. package/dist/{cli-gbfxp48k.js → cli-3fwqy71j.js} +15 -15
  24. package/dist/{cli-7p4jxgtt.js → cli-3gfvmzht.js} +6 -6
  25. package/dist/{cli-03vpq45j.js → cli-3ndmpxxv.js} +2 -2
  26. package/dist/{cli-asq5ydm4.js → cli-4hxrf0p4.js} +131 -50
  27. package/dist/{cli-mz9afxxf.js → cli-4rf0f5vg.js} +15 -3
  28. package/dist/{cli-k4tjsdx9.js → cli-5rj83bam.js} +272 -195
  29. package/dist/{cli-sbwsxvtd.js → cli-5sckpqcx.js} +2 -2
  30. package/dist/{cli-cz5nmdxt.js → cli-683y0b3a.js} +2 -2
  31. package/dist/{cli-ethgy7ha.js → cli-6y51ryva.js} +2 -2
  32. package/dist/{cli-n4c8e3s5.js → cli-708wdy32.js} +1 -1
  33. package/dist/{cli-pasyvjmm.js → cli-737pyqxr.js} +1 -1
  34. package/dist/{cli-gx4rm0qx.js → cli-7enjwya9.js} +1 -1
  35. package/dist/{cli-6x9ma5w5.js → cli-803188pb.js} +1 -1
  36. package/dist/{cli-hh5we352.js → cli-9abdgn5t.js} +7 -7
  37. package/dist/{cli-ff1tm9n9.js → cli-a2rr0fmg.js} +1 -1
  38. package/dist/{cli-c952c7ac.js → cli-a34pr3w1.js} +2 -2
  39. package/dist/{cli-s682zgqz.js → cli-abkm71vr.js} +1 -1
  40. package/dist/{cli-mmhj8vd7.js → cli-ag2yrnt5.js} +11 -2
  41. package/dist/{cli-bvpgr9ny.js → cli-aj8djyqt.js} +5 -5
  42. package/dist/{cli-gwhjmh8g.js → cli-art7c1zh.js} +5 -5
  43. package/dist/{cli-5bnsc0e1.js → cli-az25fn5f.js} +4 -4
  44. package/dist/{cli-9s05422w.js → cli-bqztfyve.js} +1 -1
  45. package/dist/{cli-bn0rewsy.js → cli-c081bj3b.js} +2 -2
  46. package/dist/{cli-1r5ad7j5.js → cli-c2habvsf.js} +12 -12
  47. package/dist/{cli-64mh9mf3.js → cli-ca6banwz.js} +69 -17
  48. package/dist/{cli-25n3mv5d.js → cli-cz37jqss.js} +5 -5
  49. package/dist/{cli-6n8axjv1.js → cli-d2dpf95n.js} +2 -2
  50. package/dist/{cli-gn5af0x5.js → cli-emhjcj2y.js} +11 -11
  51. package/dist/{cli-6a1xg1p7.js → cli-esakngh8.js} +2 -2
  52. package/dist/{cli-ha9rmwgq.js → cli-fb8m699c.js} +2 -2
  53. package/dist/{cli-4b2z6n0m.js → cli-ffvsj2qw.js} +17 -8
  54. package/dist/{cli-abs8ndtr.js → cli-g20jjwt4.js} +26 -26
  55. package/dist/{cli-jb30dzvk.js → cli-g8ka5j4j.js} +446 -164
  56. package/dist/{cli-19202kew.js → cli-gf95q3bm.js} +5 -5
  57. package/dist/{cli-eftzyh9y.js → cli-gnsb1zxa.js} +2 -2
  58. package/dist/{cli-nwyw3zhg.js → cli-gxee0bqa.js} +1 -1
  59. package/dist/{cli-e42vss7b.js → cli-hfeb68z4.js} +5 -5
  60. package/dist/{cli-g9vd9ece.js → cli-k21gx5ht.js} +14 -16
  61. package/dist/{cli-tsma7p8v.js → cli-kf4jree9.js} +2 -2
  62. package/dist/{cli-dabehxhp.js → cli-kt907da6.js} +1 -1
  63. package/dist/{cli-wx2nj19m.js → cli-m5a9m0ak.js} +6 -6
  64. package/dist/{cli-621ce7kw.js → cli-m70bajzr.js} +3 -3
  65. package/dist/{cli-9j129brq.js → cli-mtnj4fwa.js} +6 -6
  66. package/dist/{cli-xzfq4at3.js → cli-mvjz5an3.js} +15 -11
  67. package/dist/{cli-277c31dw.js → cli-n7asmce6.js} +225 -3
  68. package/dist/{cli-xtrz6bzs.js → cli-nn6vx2mq.js} +1 -1
  69. package/dist/{cli-2655k16q.js → cli-nq677csy.js} +1 -1
  70. package/dist/{cli-0shx9j93.js → cli-pfk92eqz.js} +2 -2
  71. package/dist/{cli-wghjkr0e.js → cli-sqma6vw1.js} +10 -7
  72. package/dist/{cli-nzfnt2gs.js → cli-tf1n0r0b.js} +1 -1
  73. package/dist/{cli-qqzmjxkf.js → cli-v49fkghp.js} +11 -6
  74. package/dist/{cli-x77gen2p.js → cli-w2656ka1.js} +7 -7
  75. package/dist/{cli-5kqnemen.js → cli-yvy48785.js} +1 -1
  76. package/dist/{cli-m4ecdxvz.js → cli-zqne1qcj.js} +7 -7
  77. package/dist/cli.js +17 -17
  78. package/dist/{codex-adapter-wch12q99.js → codex-adapter-d95q4qsn.js} +7 -7
  79. package/dist/{codex-login-rn7c5gxt.js → codex-login-8cg0vhf2.js} +1 -1
  80. package/dist/{codex-session-runner-41pfcr95.js → codex-session-runner-gdwd8h0c.js} +8 -8
  81. package/dist/{commands-9849dkj6.js → commands-jqmgk54b.js} +2 -2
  82. package/dist/{controller-ja0sd95p.js → controller-z3jq589f.js} +4 -4
  83. package/dist/{controllers-connect-xp05dj6x.js → controllers-connect-r2za39ef.js} +23 -23
  84. package/dist/{cursor-adapter-623qxqew.js → cursor-adapter-4mr4jgkw.js} +2 -2
  85. package/dist/{db-47vj40dy.js → db-ppv2tze7.js} +3 -3
  86. package/dist/{db-w759qmx0.js → db-w36wwb5g.js} +4 -2
  87. package/dist/{devin-adapter-4ycy4vrf.js → devin-adapter-byen2cxt.js} +1 -1
  88. package/dist/{dispatcher-7b8e4z2j.js → dispatcher-pv5h77vf.js} +20 -20
  89. package/dist/{docker-s84hjs91.js → docker-fdzyewpz.js} +2 -2
  90. package/dist/{dsh-adapter-2ezw3xea.js → dsh-adapter-d4pdpb3r.js} +2 -2
  91. package/dist/{e2b-9gkdhm3c.js → e2b-222hsajc.js} +2 -2
  92. package/dist/{e2b-cjtf8k1z.js → e2b-fn3zacvt.js} +2 -2
  93. package/dist/{easter-egg-3d-e4qv8dxj.js → easter-egg-3d-4sp4dvmc.js} +1 -1
  94. package/dist/{event-bus-3z78ep5n.js → event-bus-pghg8hqy.js} +1 -1
  95. package/dist/{execute-0xpz8950.js → execute-jev839y1.js} +1 -1
  96. package/dist/{external-channel-e2s028da.js → external-channel-y6nkzhnn.js} +2 -2
  97. package/dist/{grok-adapter-ys7gaqee.js → grok-adapter-t08b9ntm.js} +10 -10
  98. package/dist/handlers-eqsnmv95.js +41 -0
  99. package/dist/{heartbeat-w3mntghj.js → heartbeat-vn31xwdk.js} +23 -23
  100. package/dist/{hook-g7kvv6x9.js → hook-6d0zbvvt.js} +6 -6
  101. package/dist/{hook-3jjywnpb.js → hook-cpjs8q51.js} +8 -8
  102. package/dist/{http-tkp7d4kb.js → http-9c6medda.js} +856 -99
  103. package/dist/{index-xdaahthb.js → index-38t35h0n.js} +20 -20
  104. package/dist/{index-3n8m9ea2.js → index-g6k9tctj.js} +1 -1
  105. package/dist/{index-1ef1myq2.js → index-k0yt1tvq.js} +20 -20
  106. package/dist/{index-31ceyf42.js → index-pkma1yf8.js} +1 -1
  107. package/dist/{index-q7m26kvs.js → index-qhjf2htm.js} +13 -10
  108. package/dist/index-yqgbfgaj.js +43 -0
  109. package/dist/{keepalive-v4ta8wgg.js → keepalive-em0117bp.js} +4 -4
  110. package/dist/lead-23e7z59t.js +79 -0
  111. package/dist/{maintenance-84n1ex21.js → maintenance-kbme3qy9.js} +1 -1
  112. package/dist/{oauth-refresh-sweep-vjnnandw.js → oauth-refresh-sweep-xysj4yse.js} +3 -3
  113. package/dist/{onboard-sfmpe4np.js → onboard-p6q0vz97.js} +3 -3
  114. package/dist/{opencode-adapter-jmpst4q4.js → opencode-adapter-esv3k9yn.js} +7 -7
  115. package/dist/{otel-impl-v6cc0zve.js → otel-impl-54qhqh1z.js} +2 -2
  116. package/dist/{pi-mono-adapter-fk8fkcf6.js → pi-mono-adapter-mymbwhj0.js} +10 -10
  117. package/dist/{pricing-refresh-fsk1e1zc.js → pricing-refresh-yb75wv33.js} +3 -3
  118. package/dist/{rbac-roles-prmkdnkw.js → rbac-roles-1vp2mw4p.js} +2 -2
  119. package/dist/{rbac-roles-zxmxqa39.js → rbac-roles-dexhc9x8.js} +1 -1
  120. package/dist/render-v2-tjay5hhf.js +16 -0
  121. package/dist/{resume-frdtgfa0.js → resume-8ayf37g5.js} +9 -9
  122. package/dist/{runtime-bt5pkpm1.js → runtime-hqe5cqcd.js} +1 -1
  123. package/dist/{session-access-fpez4y5q.js → session-access-fdhp987s.js} +4 -4
  124. package/dist/session-source-backfill-qgtc86z1.js +110 -0
  125. package/dist/{setup-mq6m3m8q.js → setup-4rrmqcq3.js} +1 -1
  126. package/dist/{virtual-modules-mnfbh5mr.js → virtual-modules-52km00d6.js} +2 -2
  127. package/dist/{work-objects-kfahhjsq.js → work-objects-p28fgd70.js} +23 -23
  128. package/dist/worker-e3w7vye1.js +79 -0
  129. package/dist/{x-pbpezr0s.js → x-wxrzvzx8.js} +2 -2
  130. package/openapi.json +1274 -32
  131. package/package.json +1 -1
  132. package/src/assets/source-key.ts +95 -0
  133. package/src/be/db/api-keys.ts +80 -0
  134. package/src/be/db/session-labels.ts +28 -0
  135. package/src/be/db/session-moves.ts +5 -6
  136. package/src/be/db/session-tree.ts +153 -14
  137. package/src/be/db/tasks/read.ts +11 -0
  138. package/src/be/db/tasks/write.ts +6 -1
  139. package/src/be/db/workflow-runs.ts +183 -1
  140. package/src/be/db-retention.ts +61 -6
  141. package/src/be/db.ts +59 -114
  142. package/src/be/memory/providers/openai-embedding.ts +5 -1
  143. package/src/be/migrations/244_api_key_validation.sql +4 -0
  144. package/src/be/onboarding.ts +4 -0
  145. package/src/be/scripts/typecheck.ts +1 -1
  146. package/src/be/session-source-backfill.ts +163 -0
  147. package/src/be/swarm-config-guard.ts +1 -0
  148. package/src/comb/review-batch.ts +28 -1
  149. package/src/comb/templates.ts +7 -2
  150. package/src/commands/claude-managed-setup.ts +7 -1
  151. package/src/commands/credential-refresh.ts +76 -1
  152. package/src/commands/credential-wait.ts +2 -2
  153. package/src/commands/provider-credentials.ts +210 -170
  154. package/src/commands/runner.ts +25 -4
  155. package/src/extensions/apply-task-create.ts +8 -1
  156. package/src/extensions/contract-types.generated.ts +1 -1
  157. package/src/extensions/contract.ts +5 -0
  158. package/src/http/all-routes.ts +2 -0
  159. package/src/http/api-keys.ts +53 -0
  160. package/src/http/assets.ts +15 -0
  161. package/src/http/codex-oauth-accounts.ts +191 -0
  162. package/src/http/comb.ts +15 -2
  163. package/src/http/config.ts +1 -1
  164. package/src/http/core.ts +6 -0
  165. package/src/http/fleet-benchmark.ts +5 -2
  166. package/src/http/index.ts +11 -0
  167. package/src/http/integrations.ts +5 -1
  168. package/src/http/kv.ts +8 -3
  169. package/src/http/onboarding.ts +6 -1
  170. package/src/http/pool-keys.ts +354 -0
  171. package/src/http/sessions.ts +37 -12
  172. package/src/http/stats.ts +1 -0
  173. package/src/http/status.ts +26 -12
  174. package/src/http/tasks.ts +3 -0
  175. package/src/http/used-by.ts +7 -2
  176. package/src/http/workflows.ts +78 -9
  177. package/src/integrations/chat-sdk/inbound.ts +27 -0
  178. package/src/kv-reserved-namespaces.ts +19 -0
  179. package/src/providers/acp-targets.ts +1 -1
  180. package/src/providers/amp-adapter.ts +1 -1
  181. package/src/providers/claude-adapter.ts +5 -1
  182. package/src/providers/claude-managed-adapter.ts +16 -9
  183. package/src/providers/codex-adapter.ts +71 -11
  184. package/src/scheduler/scheduler.ts +3 -2
  185. package/src/scripts-runtime/swarm-sdk.ts +7 -1
  186. package/src/scripts-runtime/types/stdlib.d.ts +1 -1
  187. package/src/scripts-runtime/types/swarm-extension.d.ts +17 -8
  188. package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -1
  189. package/src/slack/assistant.ts +7 -0
  190. package/src/slack/channel-labels.ts +74 -0
  191. package/src/slack/handlers.ts +8 -0
  192. package/src/slack/inbound-files.ts +5 -2
  193. package/src/slack/thread-buffer.ts +4 -0
  194. package/src/tasks/sibling-awareness.ts +12 -3
  195. package/src/telemetry-context.ts +5 -6
  196. package/src/telemetry-identity.ts +3 -3
  197. package/src/tests/api-keys-validation.test.ts +170 -0
  198. package/src/tests/claude-managed-adapter.test.ts +46 -0
  199. package/src/tests/claude-routes-e2e.test.ts +7 -2
  200. package/src/tests/codex-credential-pools.test.ts +120 -0
  201. package/src/tests/codex-gateway-route.test.ts +165 -0
  202. package/src/tests/codex-oauth-accounts.test.ts +357 -0
  203. package/src/tests/comb-review-batch.test.ts +63 -2
  204. package/src/tests/config-pool-keys.test.ts +533 -0
  205. package/src/tests/credential-check.test.ts +290 -2
  206. package/src/tests/credential-refresh.test.ts +172 -1
  207. package/src/tests/db-retention.test.ts +9 -3
  208. package/src/tests/db-task-read-contract.test.ts +1 -0
  209. package/src/tests/db-task-write-contract.test.ts +1 -3
  210. package/src/tests/docs-index-footprint.test.js +3 -3
  211. package/src/tests/extensions-pre-task.test.ts +53 -1
  212. package/src/tests/fixtures/extensions/record-task-create/hooks.ts +9 -0
  213. package/src/tests/fixtures/extensions/record-task-create/manifest.json +7 -0
  214. package/src/tests/fleet-benchmark.test.ts +8 -0
  215. package/src/tests/get-workflow-run-tool.test.ts +98 -0
  216. package/src/tests/integrations-http.test.ts +32 -2
  217. package/src/tests/internal-ai/complete-structured.test.ts +37 -1
  218. package/src/tests/internal-ai/credentials.test.ts +23 -0
  219. package/src/tests/kv-http.test.ts +32 -0
  220. package/src/tests/kv-tool.test.ts +25 -0
  221. package/src/tests/multi-runtime-registration.test.ts +12 -12
  222. package/src/tests/onboarding.test.ts +108 -1
  223. package/src/tests/openai-embedding-credentials.test.ts +35 -0
  224. package/src/tests/openrouter-attribution-call-sites.test.ts +2 -2
  225. package/src/tests/org-website.test.ts +36 -0
  226. package/src/tests/provider-key-probes.test.ts +181 -0
  227. package/src/tests/rbac-e2e-helpers.test.ts +88 -1
  228. package/src/tests/rbac-e2e-helpers.ts +36 -11
  229. package/src/tests/script-connections.test.ts +2 -0
  230. package/src/tests/script-runs-retention.test.ts +212 -0
  231. package/src/tests/sdk-allowlist.test.ts +39 -0
  232. package/src/tests/session-source-backfill.test.ts +429 -0
  233. package/src/tests/session-source-keys.test.ts +236 -0
  234. package/src/tests/session-source-labels.test.ts +419 -0
  235. package/src/tests/sessions-detail-batch.test.ts +2 -0
  236. package/src/tests/sessions-move.test.ts +131 -0
  237. package/src/tests/sessions-tree.test.ts +299 -2
  238. package/src/tests/slack-inbound-files.test.ts +103 -23
  239. package/src/tests/status.test.ts +101 -18
  240. package/src/tests/tasks-workflow-run-filter.test.ts +146 -0
  241. package/src/tests/test-net.ts +31 -0
  242. package/src/tests/transcription.test.ts +8 -0
  243. package/src/tests/used-by-route.test.ts +11 -0
  244. package/src/tests/workflow-cascade-cancel.test.ts +172 -0
  245. package/src/tests/workflow-http-v2.test.ts +131 -0
  246. package/src/tests/workflow-step-order.test.ts +92 -0
  247. package/src/tests/workflow-sub-workflow.test.ts +30 -0
  248. package/src/tools/get-tasks.ts +8 -0
  249. package/src/tools/kv/kv-read-auth.ts +4 -2
  250. package/src/tools/task-action.ts +1 -1
  251. package/src/tools/workflows/get-workflow-run.ts +14 -4
  252. package/src/types.ts +24 -0
  253. package/src/utils/company-profile.ts +51 -1
  254. package/src/utils/credentials.ts +5 -2
  255. package/src/utils/internal-ai/complete-structured.ts +5 -0
  256. package/src/utils/internal-ai/credentials.ts +4 -3
  257. package/src/utils/provider-key-probes.ts +328 -0
  258. package/src/utils/transcription.ts +4 -1
  259. package/src/workflows/executors/sub-workflow.ts +8 -1
  260. package/templates/ai-toolbox.manifest.json +2 -2
  261. package/templates/skills/researching/content.md +2 -0
  262. package/templates/skills/swarm-scripts/SKILL.md +1 -0
  263. package/templates/skills/swarm-scripts/content.md +1 -0
  264. package/dist/cli-7421jzsw.js +0 -84
  265. package/dist/cli-e45e4m6b.js +0 -413
  266. package/dist/cli-zw88zjba.js +0 -307
  267. package/dist/handlers-wy1dr6ja.js +0 -41
  268. package/dist/index-nmm79qgr.js +0 -43
  269. package/dist/lead-sawnyqxk.js +0 -79
  270. package/dist/render-v2-pjwxvnkw.js +0 -12
  271. package/dist/worker-v5e5rxj3.js +0 -79
@@ -0,0 +1,328 @@
1
+ /** Shared credential probes. This module does not import worker harnesses. */
2
+ import {
3
+ type OpenAICompatibleProvider,
4
+ providerBaseUrl,
5
+ providerForKeyVar,
6
+ } from "@desplega/model-catalog";
7
+ import {
8
+ createScopedFetch,
9
+ deriveDefaultRoute,
10
+ getRouteProvider,
11
+ type ModelRoute,
12
+ } from "@desplega/model-routing";
13
+ import { assertUrlSafe, publicEndpointSsrfOptions } from "../oauth/mcp-wrapper";
14
+ import { getOpenRouterAttributionHeaders, getOpenRouterBaseUrl } from "./openrouter-base-url";
15
+ import { scrubSecrets } from "./secret-scrubber";
16
+
17
+ interface ProbeOptions {
18
+ env?: Record<string, string | undefined>;
19
+ publicEndpoint?: boolean;
20
+ }
21
+
22
+ const LIVE_TEST_TIMEOUT_MS = 5_000;
23
+
24
+ function scrubProbeBody(body: string, init: RequestInit): string {
25
+ const headers = new Headers(init.headers);
26
+ const apiKey =
27
+ headers.get("x-api-key") ?? headers.get("authorization")?.replace(/^Bearer\s+/i, "");
28
+ return scrubSecrets(apiKey ? body.replaceAll(apiKey, "[REDACTED]") : body);
29
+ }
30
+
31
+ export interface LiveKeyResult {
32
+ keyType: string;
33
+ index: number;
34
+ suffix: string;
35
+ ok: boolean;
36
+ error?: string;
37
+ httpStatus?: number;
38
+ latency_ms: number;
39
+ }
40
+
41
+ export interface LiveValidationResult {
42
+ ok: boolean;
43
+ error?: string;
44
+ httpStatus?: number;
45
+ warning?: string;
46
+ keys?: LiveKeyResult[];
47
+ latency_ms: number;
48
+ /** No free live check exists for this credential (cloud routes): report presence only. */
49
+ skipped?: boolean;
50
+ }
51
+
52
+ export async function timedFetch(
53
+ url: string,
54
+ init: RequestInit,
55
+ options: ProbeOptions = {},
56
+ ): Promise<{ ok: boolean; status: number; bodyText: string; latency_ms: number }> {
57
+ const controller = new AbortController();
58
+ const timer = setTimeout(() => controller.abort(), LIVE_TEST_TIMEOUT_MS);
59
+ const start = Date.now();
60
+ try {
61
+ if (options.publicEndpoint) assertUrlSafe(url, publicEndpointSsrfOptions());
62
+ const res = await fetch(url, {
63
+ ...init,
64
+ ...(options.publicEndpoint ? { redirect: "manual" } : {}),
65
+ signal: controller.signal,
66
+ });
67
+ let bodyText = "";
68
+ try {
69
+ bodyText = scrubProbeBody(await res.text(), init);
70
+ } catch {
71
+ // The HTTP status is sufficient when the response body cannot be read.
72
+ }
73
+ return {
74
+ ok: res.ok,
75
+ status: res.status,
76
+ bodyText,
77
+ latency_ms: Date.now() - start,
78
+ };
79
+ } finally {
80
+ clearTimeout(timer);
81
+ }
82
+ }
83
+
84
+ export async function checkAnthropicApiKey(
85
+ apiKey: string,
86
+ options: ProbeOptions = {},
87
+ ): Promise<LiveValidationResult> {
88
+ const r = await timedFetch(
89
+ "https://api.anthropic.com/v1/models",
90
+ {
91
+ method: "GET",
92
+ headers: { "x-api-key": apiKey, "anthropic-version": "2023-06-01" },
93
+ },
94
+ options,
95
+ );
96
+ if (r.ok) return { ok: true, latency_ms: r.latency_ms };
97
+ return {
98
+ ok: false,
99
+ error: scrubSecrets(`HTTP ${r.status}: ${r.bodyText.slice(0, 200)}`),
100
+ httpStatus: r.status,
101
+ latency_ms: r.latency_ms,
102
+ };
103
+ }
104
+
105
+ export async function checkOpenAiApiKey(
106
+ apiKey: string,
107
+ options: ProbeOptions = {},
108
+ ): Promise<LiveValidationResult> {
109
+ const r = await timedFetch(
110
+ "https://api.openai.com/v1/models",
111
+ {
112
+ method: "GET",
113
+ headers: { Authorization: `Bearer ${apiKey}` },
114
+ },
115
+ options,
116
+ );
117
+ if (r.ok) return { ok: true, latency_ms: r.latency_ms };
118
+ return {
119
+ ok: false,
120
+ error: scrubSecrets(`HTTP ${r.status}: ${r.bodyText.slice(0, 200)}`),
121
+ httpStatus: r.status,
122
+ latency_ms: r.latency_ms,
123
+ };
124
+ }
125
+
126
+ /** `GET /v1/models` is the call the Grok CLI makes first; it runs no inference. */
127
+ export async function checkXaiApiKey(
128
+ apiKey: string,
129
+ options: ProbeOptions = {},
130
+ ): Promise<LiveValidationResult> {
131
+ const r = await timedFetch(
132
+ "https://api.x.ai/v1/models",
133
+ {
134
+ method: "GET",
135
+ headers: { Authorization: `Bearer ${apiKey}` },
136
+ },
137
+ options,
138
+ );
139
+ if (r.ok) return { ok: true, latency_ms: r.latency_ms };
140
+ return {
141
+ ok: false,
142
+ error: scrubSecrets(`HTTP ${r.status}: ${r.bodyText.slice(0, 200)}`),
143
+ httpStatus: r.status,
144
+ latency_ms: r.latency_ms,
145
+ };
146
+ }
147
+
148
+ /** Fetch a key-authenticated GET and normalize it into a `LiveValidationResult`. */
149
+ async function checkBearerGet(
150
+ url: string,
151
+ init: RequestInit,
152
+ options: ProbeOptions = {},
153
+ ): Promise<LiveValidationResult> {
154
+ const r = await timedFetch(url, { method: "GET", ...init }, options);
155
+ if (r.ok) return { ok: true, latency_ms: r.latency_ms };
156
+ return {
157
+ ok: false,
158
+ error: scrubSecrets(`HTTP ${r.status}: ${r.bodyText.slice(0, 200)}`),
159
+ httpStatus: r.status,
160
+ latency_ms: r.latency_ms,
161
+ };
162
+ }
163
+
164
+ /**
165
+ * OpenRouter's `/models` is public and answers 200 for any key, so the check
166
+ * reads `GET /key` (the key's own info, 401 for a bad key). A custom
167
+ * `OPENROUTER_BASE_URL` without `/key` (404/405) falls back to `/models`.
168
+ */
169
+ export async function checkOpenRouter(
170
+ apiKey: string,
171
+ options: ProbeOptions = {},
172
+ ): Promise<LiveValidationResult> {
173
+ const base = getOpenRouterBaseUrl(options.env);
174
+ const get = (url: string) =>
175
+ checkBearerGet(
176
+ url,
177
+ {
178
+ headers: {
179
+ ...getOpenRouterAttributionHeaders(url, options.env),
180
+ Authorization: `Bearer ${apiKey}`,
181
+ },
182
+ },
183
+ options,
184
+ );
185
+ const keyResult = await get(`${base}/key`);
186
+ if (keyResult.httpStatus === 404 || keyResult.httpStatus === 405) return get(`${base}/models`);
187
+ return keyResult;
188
+ }
189
+
190
+ /** `GET /models` on the official DeepSeek API: lists models, runs no inference. */
191
+ export function checkDeepSeekApiKey(
192
+ apiKey: string,
193
+ options: ProbeOptions = {},
194
+ ): Promise<LiveValidationResult> {
195
+ return checkBearerGet(
196
+ "https://api.deepseek.com/models",
197
+ {
198
+ headers: { Authorization: `Bearer ${apiKey}` },
199
+ },
200
+ options,
201
+ );
202
+ }
203
+
204
+ /**
205
+ * `GET <baseUrl>/models` with a Bearer key against an OpenAI-compatible
206
+ * registry provider (NaN, Helmcode). Lists models, runs no inference.
207
+ * `baseUrl` defaults to the registry entry; the probe passes the
208
+ * `<ID>_BASE_URL` override, and tests inject a fake server.
209
+ */
210
+ export async function checkOpenAICompatibleProvider(
211
+ provider: OpenAICompatibleProvider,
212
+ apiKey: string,
213
+ baseUrl: string = provider.baseUrl,
214
+ options: ProbeOptions = {},
215
+ ): Promise<LiveValidationResult> {
216
+ const r = await timedFetch(
217
+ `${baseUrl.replace(/\/+$/, "")}/models`,
218
+ {
219
+ method: "GET",
220
+ headers: { Authorization: `Bearer ${apiKey}` },
221
+ },
222
+ options,
223
+ );
224
+ if (r.ok) return { ok: true, latency_ms: r.latency_ms };
225
+ return {
226
+ ok: false,
227
+ error: scrubSecrets(`HTTP ${r.status}: ${r.bodyText.slice(0, 200)}`),
228
+ httpStatus: r.status,
229
+ latency_ms: r.latency_ms,
230
+ };
231
+ }
232
+
233
+ /**
234
+ * Run the route provider's own check. Requests stay on the route's origin and
235
+ * path prefix, go to public hosts only, and never follow a redirect.
236
+ */
237
+ async function checkRoute(
238
+ route: ModelRoute,
239
+ env: Record<string, string | undefined>,
240
+ ): Promise<LiveValidationResult> {
241
+ const startedAt = Date.now();
242
+ const provider = getRouteProvider(route.provider);
243
+ const baseUrl = route.baseUrl ?? provider?.defaultBaseUrl;
244
+ if (!provider?.validate || !baseUrl) return { ok: true, skipped: true, latency_ms: 0 };
245
+ const publicFetch = async (url: string | URL | Request, init?: RequestInit) => {
246
+ assertUrlSafe(String(url), publicEndpointSsrfOptions());
247
+ const res = await fetch(url, init);
248
+ if (res.ok || !res.body) return res;
249
+ return new Response(scrubProbeBody(await res.text(), init ?? {}), {
250
+ status: res.status,
251
+ statusText: res.statusText,
252
+ headers: res.headers,
253
+ });
254
+ };
255
+ const result = await provider.validate({
256
+ route,
257
+ env,
258
+ signal: AbortSignal.timeout(LIVE_TEST_TIMEOUT_MS),
259
+ fetch: createScopedFetch(baseUrl, publicFetch as typeof fetch),
260
+ });
261
+ const latency_ms = Date.now() - startedAt;
262
+ if (result.status === "verified") return { ok: true, latency_ms };
263
+ return {
264
+ ok: result.status === "configured",
265
+ ...(result.status === "configured" ? { skipped: true } : { httpStatus: result.httpStatus }),
266
+ error: scrubSecrets(result.reason),
267
+ latency_ms,
268
+ };
269
+ }
270
+
271
+ /** Check one newly submitted pool member without loading a worker harness. */
272
+ export async function checkProviderPoolKey(
273
+ keyType: string,
274
+ apiKey: string,
275
+ env: Record<string, string | undefined> = process.env,
276
+ ): Promise<LiveValidationResult> {
277
+ const startedAt = Date.now();
278
+ const options: ProbeOptions = { env, publicEndpoint: true };
279
+ try {
280
+ switch (keyType) {
281
+ case "ANTHROPIC_API_KEY": {
282
+ // Behind a gateway (`ANTHROPIC_BASE_URL`), Claude Code sends this key
283
+ // to the gateway, so check it there and not at api.anthropic.com.
284
+ const keyEnv = { ...env, ANTHROPIC_API_KEY: apiKey };
285
+ const route = deriveDefaultRoute("claude", keyEnv);
286
+ if (
287
+ route?.provider === "anthropic-gateway" &&
288
+ "secretKey" in route.auth &&
289
+ route.auth.secretKey === "ANTHROPIC_API_KEY"
290
+ ) {
291
+ return await checkRoute(route, keyEnv);
292
+ }
293
+ return await checkAnthropicApiKey(apiKey, options);
294
+ }
295
+ case "OPENAI_API_KEY":
296
+ return await checkOpenAiApiKey(apiKey, options);
297
+ case "OPENROUTER_API_KEY":
298
+ return await checkOpenRouter(apiKey, options);
299
+ case "DEEPSEEK_API_KEY":
300
+ return await checkDeepSeekApiKey(apiKey, options);
301
+ case "XAI_API_KEY":
302
+ return await checkXaiApiKey(apiKey, options);
303
+ case "CLAUDE_CODE_OAUTH_TOKEN": {
304
+ const tokenEnv = { CLAUDE_CODE_OAUTH_TOKEN: apiKey };
305
+ return await checkRoute(deriveDefaultRoute("claude", tokenEnv)!, tokenEnv);
306
+ }
307
+ default: {
308
+ const provider = providerForKeyVar(keyType);
309
+ if (provider) {
310
+ return await checkOpenAICompatibleProvider(
311
+ provider,
312
+ apiKey,
313
+ providerBaseUrl(provider, env),
314
+ options,
315
+ );
316
+ }
317
+ return { ok: true, skipped: true, latency_ms: 0 };
318
+ }
319
+ }
320
+ } catch (err) {
321
+ const raw = err instanceof Error ? err.message : String(err);
322
+ return {
323
+ ok: false,
324
+ error: scrubSecrets(raw.split(apiKey).join("[REDACTED]")) || "Unknown error",
325
+ latency_ms: Date.now() - startedAt,
326
+ };
327
+ }
328
+ }
@@ -20,6 +20,7 @@
20
20
  * auto-detected key always goes to its provider's own endpoint. Deepgram and AssemblyAI use other request shapes and are not
21
21
  * supported. Read at call time, so a config reload applies without a restart.
22
22
  */
23
+ import { selectCredential } from "./credentials";
23
24
  import { createValueScrubber, scrubSecrets } from "./secret-scrubber";
24
25
 
25
26
  const TRANSCRIPTION_TIMEOUT_MS = 60_000;
@@ -119,7 +120,9 @@ export function resolveTranscriptionConfig(): TranscriptionConfig | null {
119
120
  : AUTO_DETECT_ORDER;
120
121
  for (const name of candidates) {
121
122
  const { keyEnv } = PROVIDERS[name];
122
- const apiKey = envValue(keyEnv);
123
+ const value = envValue(keyEnv);
124
+ // OPENAI_API_KEY may be a comma pool: send one member.
125
+ const apiKey = value ? selectCredential(value, undefined, keyEnv).selected : undefined;
123
126
  if (apiKey) return { provider: name, apiKey, keyEnv, source: "auto" };
124
127
  }
125
128
  return null;
@@ -133,7 +133,14 @@ export class SubWorkflowExecutor extends BaseExecutor<
133
133
  throw err;
134
134
  }
135
135
  const child = await db.getWorkflowRun(childRunId);
136
- if (!child) return { status: "failed", error: `Child workflow run ${childRunId} not found` };
136
+ // The start committed the row, so a missing one was deleted with its workflow.
137
+ if (!child) {
138
+ return {
139
+ status: "failed",
140
+ error: `Child workflow run ${childRunId} not found`,
141
+ retryable: false,
142
+ };
143
+ }
137
144
  return this.settle(child, config.workflowId);
138
145
  }
139
146
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "commit": "5a577022800d9b43ccec5376bfe16c7b24eecd91",
2
+ "commit": "c81f2bb62f49b45603e9e702558e1ca22e98fcdd",
3
3
  "excludedSkills": [
4
4
  "feedback"
5
5
  ],
@@ -44,7 +44,7 @@
44
44
  "templates/skills/questioning/content.md": "66f826a191e96d0041adca00a7c197a458de8e6fe164c1599b8708098d4109ab",
45
45
  "templates/skills/questioning/files/template.md": "a68d72801a0b10380bf773b07e9d2f8d4c5b4b1c609ce954289e38ffc78cc223",
46
46
  "templates/skills/researching/config.json": "921c062cab599097cc7677d2dad4797ec4d5844d2ee73607fbd417ec3fa487d4",
47
- "templates/skills/researching/content.md": "92951142327415696726a2577defdb2379cc611cdafa9cdac92431a5e60f618f",
47
+ "templates/skills/researching/content.md": "87ced42d15f1fd63d1b4e8d5ed8161537202f5b05a85df38dea273e9e3d6ad08",
48
48
  "templates/skills/researching/files/template.md": "411d34b98c610ef715edf0f24d09f828ba2d0f65f688e7879ef33a9453007949",
49
49
  "templates/skills/reviewing/config.json": "09c40bb098e9738c60a848995fa9fe7568d158631a089c76ba02c0dd955445c7",
50
50
  "templates/skills/reviewing/content.md": "1bbc285c9ad8cc3312f1ee2c558b04ffee914ffb6f5c63fb3741fcbb3e300fa5",
@@ -166,3 +166,5 @@ File-review is on by default (unless Autopilot):
166
166
  - Research documents should be self-contained
167
167
  - **CRITICAL**: You are a documentarian, not an evaluator
168
168
  - **REMEMBER**: Document what IS, not what SHOULD BE
169
+ - Single-page-app blogs (Vite/MDX): the HTML and RSS carry only the title and summary. The post text is compiled into the main JS bundle (`/assets/index-<hash>.js`, referenced in the page HTML). Fetch the bundle and extract string literals with a quote-aware tokenizer started at a function boundary; a naive regex desyncs on quotes.
170
+ - YouTube bot-blocks downloads from sandboxed egress. To describe a video, give its URL to a multimodal model. For the bytes of a brand's own product video, grep the brand's site or changelog HTML for `.mp4` URLs first and match the target by duration.
@@ -175,6 +175,7 @@ function rowsToObjects(res: any): any[] {
175
175
  - `kv_get` returns the entry at `data`: read `res.data.value`. The MCP tool shape `entry.value` reads `undefined` in a script.
176
176
  - `script_run` returns an envelope (`result`, `truncated`, `durationMs`, `stdout`, `stderr`, `exitCode`). The callee payload is at `.result`. Take key names from a real response: a guessed key that is absent reads as a false zero.
177
177
  - An `argsSchema` validation error fires before the body runs and repeats on every retry. Fix the args instead of retrying.
178
+ - The skill methods disagree on the id field: `skill_get({ id })`, but `skill_update({ skillId, content })`. `skill_update({ id, content })` returns outer `success: true` with inner `data.success: false` ("skillId is required.") and writes nothing. Gate a write on `res.data.success`, then re-read: the version must increase and the content must match the draft.
178
179
  - Write a dedup or idempotency marker only after the step succeeds. A marker written after a failure blocks the next real attempt.
179
180
  - `kv_set` returns `success: false` instead of throwing when a key has a character outside `[a-zA-Z0-9._:/-]` (for example `#` or `|` from a git ref). Check `success` after every write.
180
181
  - `script_run` without `scope` runs an agent-scoped row of the same name before the global one, so a stale private copy silently wins. Pass `scope: "global"` for a shared script.
@@ -170,6 +170,7 @@ function rowsToObjects(res: any): any[] {
170
170
  - `kv_get` returns the entry at `data`: read `res.data.value`. The MCP tool shape `entry.value` reads `undefined` in a script.
171
171
  - `script_run` returns an envelope (`result`, `truncated`, `durationMs`, `stdout`, `stderr`, `exitCode`). The callee payload is at `.result`. Take key names from a real response: a guessed key that is absent reads as a false zero.
172
172
  - An `argsSchema` validation error fires before the body runs and repeats on every retry. Fix the args instead of retrying.
173
+ - The skill methods disagree on the id field: `skill_get({ id })`, but `skill_update({ skillId, content })`. `skill_update({ id, content })` returns outer `success: true` with inner `data.success: false` ("skillId is required.") and writes nothing. Gate a write on `res.data.success`, then re-read: the version must increase and the content must match the draft.
173
174
  - Write a dedup or idempotency marker only after the step succeeds. A marker written after a failure blocks the next real attempt.
174
175
  - `kv_set` returns `success: false` instead of throwing when a key has a character outside `[a-zA-Z0-9._:/-]` (for example `#` or `|` from a git ref). Check `success` after every write.
175
176
  - `script_run` without `scope` runs an agent-scoped row of the same name before the global one, so a stale private copy silently wins. Pass `scope: "global"` for a shared script.
@@ -1,84 +0,0 @@
1
- import {
2
- __esm
3
- } from "./cli-242037k3.js";
4
-
5
- // src/utils/model-rate-limit-windows.ts
6
- function modelFamilyOf(model) {
7
- if (!model)
8
- return;
9
- const lower = model.toLowerCase();
10
- const families = ["fable", "opus", "sonnet", "haiku"];
11
- for (const family of families) {
12
- if (lower.includes(family))
13
- return family;
14
- }
15
- return;
16
- }
17
- function windowForModelFamily(family) {
18
- switch (family) {
19
- case "fable":
20
- return FABLE_WINDOW;
21
- case "opus":
22
- return OPUS_WINDOW;
23
- case "sonnet":
24
- return SONNET_WINDOW;
25
- default:
26
- return;
27
- }
28
- }
29
- function isModelScopedWindow(type) {
30
- return Object.hasOwn(MODEL_SCOPED_WINDOWS, type) && MODEL_SCOPED_WINDOWS[type] !== undefined;
31
- }
32
- function parseModelLimitMessage(text) {
33
- const match = text.match(/reached your (Fable|Opus|Sonnet) limit/i);
34
- if (!match)
35
- return;
36
- return match[1].toLowerCase();
37
- }
38
- function parseCreditsRequiredMessage(text) {
39
- const match = text.match(/(Fable|Opus|Sonnet)(?:[^.]|\.(?=\d)){0,40}requires usage credits/i);
40
- if (!match)
41
- return;
42
- return match[1].toLowerCase();
43
- }
44
- function activeModelBlock(windows, family, nowMs) {
45
- if (!windows)
46
- return;
47
- const window = windowForModelFamily(family);
48
- if (!window)
49
- return;
50
- const entry = windows[window];
51
- if (!entry)
52
- return;
53
- if (entry.status !== "rejected")
54
- return;
55
- if (typeof entry.resetsAt !== "number" || entry.resetsAt * 1000 <= nowMs)
56
- return;
57
- return { window, resetsAt: entry.resetsAt };
58
- }
59
- function activeModelBlocks(windows, nowMs) {
60
- if (!windows)
61
- return [];
62
- const blocks = [];
63
- for (const [window, model] of Object.entries(MODEL_SCOPED_WINDOWS)) {
64
- const entry = windows[window];
65
- if (!entry)
66
- continue;
67
- if (entry.status !== "rejected")
68
- continue;
69
- if (typeof entry.resetsAt !== "number" || entry.resetsAt * 1000 <= nowMs)
70
- continue;
71
- blocks.push({ model, window, resetsAt: entry.resetsAt });
72
- }
73
- return blocks;
74
- }
75
- var FABLE_WINDOW = "seven_day_overage_included", OPUS_WINDOW = "seven_day_opus", SONNET_WINDOW = "seven_day_sonnet", MODEL_SCOPED_WINDOWS;
76
- var init_model_rate_limit_windows = __esm(() => {
77
- MODEL_SCOPED_WINDOWS = {
78
- [FABLE_WINDOW]: "fable",
79
- [OPUS_WINDOW]: "opus",
80
- [SONNET_WINDOW]: "sonnet"
81
- };
82
- });
83
-
84
- export { MODEL_SCOPED_WINDOWS, modelFamilyOf, windowForModelFamily, isModelScopedWindow, parseModelLimitMessage, parseCreditsRequiredMessage, activeModelBlock, activeModelBlocks, init_model_rate_limit_windows };