@wardby/cli 0.2.0 → 0.3.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 (491) hide show
  1. package/.env.example +34 -4
  2. package/README.md +183 -71
  3. package/dist/claude-coding-worker/driver.d.ts +7 -1
  4. package/dist/claude-coding-worker/driver.js +31 -2
  5. package/dist/claude-coding-worker/main.js +5 -2
  6. package/dist/claude-coding-worker/tool-socket.d.ts +9 -0
  7. package/dist/claude-coding-worker/tool-socket.js +26 -0
  8. package/dist/cli-help.d.ts +1 -1
  9. package/dist/cli-help.js +13 -4
  10. package/dist/cli.d.ts +1 -1
  11. package/dist/cli.js +219 -29
  12. package/dist/coding/collect-exclude.d.ts +25 -0
  13. package/dist/coding/collect-exclude.js +76 -0
  14. package/dist/coding/profile.d.ts +67 -22
  15. package/dist/coding/profile.js +58 -25
  16. package/dist/coding/protected-path-wording.d.ts +24 -0
  17. package/dist/coding/protected-path-wording.js +55 -0
  18. package/dist/coding/protected-paths.d.ts +31 -0
  19. package/dist/coding/protected-paths.js +48 -0
  20. package/dist/coding/protocol.d.ts +99 -3
  21. package/dist/coding/protocol.js +155 -4
  22. package/dist/coding/registry/adapters.d.ts +2 -0
  23. package/dist/coding/registry/adapters.js +9 -0
  24. package/dist/coding/registry/allowlist.d.ts +9 -0
  25. package/dist/coding/registry/allowlist.js +28 -0
  26. package/dist/coding/registry/json-scan.d.ts +55 -0
  27. package/dist/coding/registry/json-scan.js +191 -0
  28. package/dist/coding/registry/lockfiles.d.ts +6 -0
  29. package/dist/coding/registry/lockfiles.js +89 -0
  30. package/dist/coding/registry/npm-lockfile.d.ts +9 -0
  31. package/dist/coding/registry/npm-lockfile.js +73 -0
  32. package/dist/coding/registry/npm-plan.d.ts +29 -0
  33. package/dist/coding/registry/npm-plan.js +289 -0
  34. package/dist/coding/registry/npm.d.ts +7 -0
  35. package/dist/coding/registry/npm.js +252 -0
  36. package/dist/coding/registry/pypi.d.ts +4 -0
  37. package/dist/coding/registry/pypi.js +210 -0
  38. package/dist/coding/registry/report.d.ts +37 -0
  39. package/dist/coding/registry/report.js +31 -0
  40. package/dist/coding/registry/token.d.ts +4 -0
  41. package/dist/coding/registry/token.js +7 -0
  42. package/dist/coding/registry/types.d.ts +286 -0
  43. package/dist/coding/registry/types.js +19 -0
  44. package/dist/coding/registry/worker-config.d.ts +14 -0
  45. package/dist/coding/registry/worker-config.js +31 -0
  46. package/dist/coding/services/builtins.d.ts +22 -0
  47. package/dist/coding/services/builtins.js +85 -0
  48. package/dist/coding/services/catalog.d.ts +443 -0
  49. package/dist/coding/services/catalog.js +159 -0
  50. package/dist/coding/services/declaration.d.ts +11 -0
  51. package/dist/coding/services/declaration.js +114 -0
  52. package/dist/coding/services/note.d.ts +8 -0
  53. package/dist/coding/services/note.js +15 -0
  54. package/dist/coding/services/resolve.d.ts +25 -0
  55. package/dist/coding/services/resolve.js +50 -0
  56. package/dist/coding/services/wording.d.ts +25 -0
  57. package/dist/coding/services/wording.js +70 -0
  58. package/dist/coding-proxy/main.js +24 -4
  59. package/dist/coding-worker/artifact.d.ts +18 -0
  60. package/dist/coding-worker/artifact.js +48 -6
  61. package/dist/coding-worker/debug-trace.d.ts +37 -0
  62. package/dist/coding-worker/debug-trace.js +117 -0
  63. package/dist/coding-worker/driver.d.ts +14 -1
  64. package/dist/coding-worker/driver.js +106 -14
  65. package/dist/coding-worker/errors.d.ts +7 -0
  66. package/dist/coding-worker/errors.js +26 -0
  67. package/dist/coding-worker/main.js +13 -4
  68. package/dist/coding-worker/sdk.d.ts +2 -2
  69. package/dist/coding-worker/sdk.js +7 -2
  70. package/dist/coding-worker/types.d.ts +3 -0
  71. package/dist/config/providers.d.ts +41 -0
  72. package/dist/config/providers.js +66 -0
  73. package/dist/core/budget-groups.d.ts +109 -6
  74. package/dist/core/budget-groups.js +125 -19
  75. package/dist/core/budget-wording.d.ts +22 -0
  76. package/dist/core/budget-wording.js +55 -0
  77. package/dist/core/coding-queue.d.ts +1 -1
  78. package/dist/core/datastores.d.ts +1 -1
  79. package/dist/core/datastores.js +18 -2
  80. package/dist/core/db.d.ts +40 -2
  81. package/dist/core/db.js +75 -2
  82. package/dist/core/dispatch.d.ts +49 -7
  83. package/dist/core/dispatch.js +290 -45
  84. package/dist/core/engine-native.js +27 -10
  85. package/dist/core/grants.d.ts +86 -0
  86. package/dist/core/grants.js +126 -0
  87. package/dist/core/host-events.d.ts +49 -0
  88. package/dist/core/host-events.js +257 -0
  89. package/dist/core/host-identity-links.d.ts +54 -0
  90. package/dist/core/host-identity-links.js +189 -0
  91. package/dist/core/host-status.d.ts +61 -0
  92. package/dist/core/host-status.js +211 -0
  93. package/dist/core/http-runtime.js +3 -4
  94. package/dist/core/in-flight-runs.d.ts +8 -0
  95. package/dist/core/in-flight-runs.js +56 -0
  96. package/dist/core/lease.d.ts +1 -1
  97. package/dist/core/private-directory.d.ts +20 -0
  98. package/dist/core/private-directory.js +36 -0
  99. package/dist/core/provider-wording.d.ts +12 -0
  100. package/dist/core/provider-wording.js +44 -0
  101. package/dist/core/reconciler.d.ts +39 -3
  102. package/dist/core/reconciler.js +86 -2
  103. package/dist/core/repo-access.d.ts +99 -0
  104. package/dist/core/repo-access.js +136 -0
  105. package/dist/core/review-host-checks.d.ts +11 -0
  106. package/dist/core/review-host-checks.js +41 -0
  107. package/dist/core/review-host-tools.d.ts +47 -0
  108. package/dist/core/review-host-tools.js +347 -0
  109. package/dist/core/run-heartbeat.d.ts +27 -0
  110. package/dist/core/run-heartbeat.js +54 -0
  111. package/dist/core/runner.d.ts +31 -9
  112. package/dist/core/runner.js +237 -40
  113. package/dist/core/scheduler.d.ts +1 -1
  114. package/dist/core/secrets.d.ts +12 -3
  115. package/dist/core/secrets.js +25 -6
  116. package/dist/core/subagent-memory-tools.d.ts +2 -2
  117. package/dist/core/subagent-memory-tools.js +16 -3
  118. package/dist/core/tool-admin.d.ts +81 -0
  119. package/dist/core/tool-admin.js +129 -0
  120. package/dist/core/tool-names.d.ts +42 -0
  121. package/dist/core/tool-names.js +64 -0
  122. package/dist/core/untrusted-content.d.ts +32 -0
  123. package/dist/core/untrusted-content.js +72 -0
  124. package/dist/core/webhooks.d.ts +9 -2
  125. package/dist/core/webhooks.js +23 -2
  126. package/dist/generated/prisma/browser.d.ts +328 -0
  127. package/dist/generated/prisma/browser.js +17 -0
  128. package/dist/generated/prisma/client.d.ts +347 -0
  129. package/dist/generated/prisma/client.js +34 -0
  130. package/dist/generated/prisma/commonInputTypes.d.ts +839 -0
  131. package/dist/generated/prisma/commonInputTypes.js +10 -0
  132. package/dist/generated/prisma/enums.d.ts +59 -0
  133. package/dist/generated/prisma/enums.js +59 -0
  134. package/dist/generated/prisma/internal/class.d.ts +605 -0
  135. package/dist/generated/prisma/internal/class.js +49 -0
  136. package/dist/generated/prisma/internal/prismaNamespace.d.ts +4494 -0
  137. package/dist/generated/prisma/internal/prismaNamespace.js +628 -0
  138. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +635 -0
  139. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +599 -0
  140. package/dist/generated/prisma/models/Agent.d.ts +3560 -0
  141. package/dist/generated/prisma/models/Agent.js +1 -0
  142. package/dist/generated/prisma/models/AgentDatastore.d.ts +1210 -0
  143. package/dist/generated/prisma/models/AgentDatastore.js +1 -0
  144. package/dist/generated/prisma/models/AgentMemory.d.ts +986 -0
  145. package/dist/generated/prisma/models/AgentMemory.js +1 -0
  146. package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
  147. package/dist/generated/prisma/models/AgentRepository.js +1 -0
  148. package/dist/generated/prisma/models/AgentSecret.d.ts +1212 -0
  149. package/dist/generated/prisma/models/AgentSecret.js +1 -0
  150. package/dist/generated/prisma/models/AgentSubAgent.d.ts +1217 -0
  151. package/dist/generated/prisma/models/AgentSubAgent.js +1 -0
  152. package/dist/generated/prisma/models/AgentTool.d.ts +1403 -0
  153. package/dist/generated/prisma/models/AgentTool.js +1 -0
  154. package/dist/generated/prisma/models/AuthFormChallenge.d.ts +1298 -0
  155. package/dist/generated/prisma/models/AuthFormChallenge.js +1 -0
  156. package/dist/generated/prisma/models/AuthLoginKey.d.ts +1245 -0
  157. package/dist/generated/prisma/models/AuthLoginKey.js +1 -0
  158. package/dist/generated/prisma/models/AuthRateLimit.d.ts +979 -0
  159. package/dist/generated/prisma/models/AuthRateLimit.js +1 -0
  160. package/dist/generated/prisma/models/AuthSession.d.ts +1376 -0
  161. package/dist/generated/prisma/models/AuthSession.js +1 -0
  162. package/dist/generated/prisma/models/AuthUser.d.ts +1700 -0
  163. package/dist/generated/prisma/models/AuthUser.js +1 -0
  164. package/dist/generated/prisma/models/BudgetGroup.d.ts +1548 -0
  165. package/dist/generated/prisma/models/BudgetGroup.js +1 -0
  166. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +1681 -0
  167. package/dist/generated/prisma/models/CodingAgentProfile.js +1 -0
  168. package/dist/generated/prisma/models/CodingProxyRequest.d.ts +1749 -0
  169. package/dist/generated/prisma/models/CodingProxyRequest.js +1 -0
  170. package/dist/generated/prisma/models/CodingProxySession.d.ts +1626 -0
  171. package/dist/generated/prisma/models/CodingProxySession.js +1 -0
  172. package/dist/generated/prisma/models/CodingRun.d.ts +3583 -0
  173. package/dist/generated/prisma/models/CodingRun.js +1 -0
  174. package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
  175. package/dist/generated/prisma/models/CodingService.js +1 -0
  176. package/dist/generated/prisma/models/Datastore.d.ts +1434 -0
  177. package/dist/generated/prisma/models/Datastore.js +1 -0
  178. package/dist/generated/prisma/models/DatastoreEntry.d.ts +1314 -0
  179. package/dist/generated/prisma/models/DatastoreEntry.js +1 -0
  180. package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
  181. package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
  182. package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
  183. package/dist/generated/prisma/models/HostIdentity.js +1 -0
  184. package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
  185. package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
  186. package/dist/generated/prisma/models/OAuthAuthorizationCode.d.ts +1526 -0
  187. package/dist/generated/prisma/models/OAuthAuthorizationCode.js +1 -0
  188. package/dist/generated/prisma/models/OAuthAuthorizationRequest.d.ts +1350 -0
  189. package/dist/generated/prisma/models/OAuthAuthorizationRequest.js +1 -0
  190. package/dist/generated/prisma/models/OAuthClient.d.ts +1289 -0
  191. package/dist/generated/prisma/models/OAuthClient.js +1 -0
  192. package/dist/generated/prisma/models/OAuthFamily.d.ts +1540 -0
  193. package/dist/generated/prisma/models/OAuthFamily.js +1 -0
  194. package/dist/generated/prisma/models/OAuthGrant.d.ts +1280 -0
  195. package/dist/generated/prisma/models/OAuthGrant.js +1 -0
  196. package/dist/generated/prisma/models/Principal.d.ts +2245 -0
  197. package/dist/generated/prisma/models/Principal.js +1 -0
  198. package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
  199. package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
  200. package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
  201. package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
  202. package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
  203. package/dist/generated/prisma/models/RegistryFetch.js +1 -0
  204. package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
  205. package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
  206. package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
  207. package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
  208. package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
  209. package/dist/generated/prisma/models/ResourceGrant.js +1 -0
  210. package/dist/generated/prisma/models/Run.d.ts +2669 -0
  211. package/dist/generated/prisma/models/Run.js +1 -0
  212. package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
  213. package/dist/generated/prisma/models/RunHostCheck.js +1 -0
  214. package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
  215. package/dist/generated/prisma/models/RunHostStatus.js +1 -0
  216. package/dist/generated/prisma/models/SchedulerLease.d.ts +970 -0
  217. package/dist/generated/prisma/models/SchedulerLease.js +1 -0
  218. package/dist/generated/prisma/models/Secret.d.ts +1399 -0
  219. package/dist/generated/prisma/models/Secret.js +1 -0
  220. package/dist/generated/prisma/models/SecretElicitationOutcome.d.ts +973 -0
  221. package/dist/generated/prisma/models/SecretElicitationOutcome.js +1 -0
  222. package/dist/generated/prisma/models/Task.d.ts +1176 -0
  223. package/dist/generated/prisma/models/Task.js +1 -0
  224. package/dist/generated/prisma/models/Tool.d.ts +1490 -0
  225. package/dist/generated/prisma/models/Tool.js +1 -0
  226. package/dist/generated/prisma/models/Webhook.d.ts +1386 -0
  227. package/dist/generated/prisma/models/Webhook.js +1 -0
  228. package/dist/generated/prisma/models.d.ts +45 -0
  229. package/dist/generated/prisma/models.js +1 -0
  230. package/dist/help/build.d.ts +1 -0
  231. package/dist/help/build.js +9 -0
  232. package/dist/help/catalog.d.ts +24 -0
  233. package/dist/help/catalog.js +160 -0
  234. package/dist/help/cli.d.ts +2 -0
  235. package/dist/help/cli.js +65 -0
  236. package/dist/help/runtime.d.ts +3 -0
  237. package/dist/help/runtime.js +44 -0
  238. package/dist/help/search.d.ts +9 -0
  239. package/dist/help/search.js +104 -0
  240. package/dist/help-index.json +660 -0
  241. package/dist/import/cli-args.js +3 -2
  242. package/dist/import/create.d.ts +6 -1
  243. package/dist/import/create.js +68 -14
  244. package/dist/import/index.d.ts +1 -1
  245. package/dist/import/index.js +19 -9
  246. package/dist/import/neutral-schema.d.ts +11 -11
  247. package/dist/mcp/auth/access.d.ts +70 -0
  248. package/dist/mcp/auth/access.js +58 -0
  249. package/dist/mcp/auth/grants-cli.d.ts +149 -0
  250. package/dist/mcp/auth/grants-cli.js +518 -0
  251. package/dist/mcp/auth/host-account-cli.d.ts +2 -0
  252. package/dist/mcp/auth/host-account-cli.js +47 -0
  253. package/dist/mcp/auth/ownership.d.ts +37 -63
  254. package/dist/mcp/auth/ownership.js +19 -14
  255. package/dist/mcp/auth/principal.d.ts +1 -1
  256. package/dist/mcp/auth/repo-authorization.d.ts +22 -0
  257. package/dist/mcp/auth/repo-authorization.js +51 -0
  258. package/dist/mcp/auth/resource-server.d.ts +32 -3
  259. package/dist/mcp/auth/resource-server.js +84 -3
  260. package/dist/mcp/auth/self-hosted/browser.js +22 -8
  261. package/dist/mcp/auth/self-hosted/cli.d.ts +1 -1
  262. package/dist/mcp/auth/self-hosted/cli.js +47 -13
  263. package/dist/mcp/auth/self-hosted/credentials.d.ts +21 -3
  264. package/dist/mcp/auth/self-hosted/credentials.js +62 -4
  265. package/dist/mcp/auth/self-hosted/rate-limit.d.ts +1 -1
  266. package/dist/mcp/auth/self-hosted/session.d.ts +3 -2
  267. package/dist/mcp/context.d.ts +28 -2
  268. package/dist/mcp/errors.d.ts +25 -6
  269. package/dist/mcp/errors.js +98 -0
  270. package/dist/mcp/host-events/github-ingress.d.ts +36 -0
  271. package/dist/mcp/host-events/github-ingress.js +92 -0
  272. package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
  273. package/dist/mcp/host-events/github-user-callback.js +50 -0
  274. package/dist/mcp/index.d.ts +8 -1
  275. package/dist/mcp/index.js +106 -16
  276. package/dist/mcp/server.d.ts +1 -1
  277. package/dist/mcp/server.js +27 -9
  278. package/dist/mcp/shared/page-style.d.ts +16 -0
  279. package/dist/mcp/shared/page-style.js +129 -0
  280. package/dist/mcp/tasks/manager.d.ts +1 -1
  281. package/dist/mcp/tools/agents.js +447 -49
  282. package/dist/mcp/tools/budget-groups.js +3 -3
  283. package/dist/mcp/tools/datastore.js +23 -16
  284. package/dist/mcp/tools/grants.d.ts +2 -0
  285. package/dist/mcp/tools/grants.js +239 -0
  286. package/dist/mcp/tools/help.d.ts +5 -0
  287. package/dist/mcp/tools/help.js +67 -0
  288. package/dist/mcp/tools/host-accounts.d.ts +2 -0
  289. package/dist/mcp/tools/host-accounts.js +114 -0
  290. package/dist/mcp/tools/memory.d.ts +7 -1
  291. package/dist/mcp/tools/memory.js +5 -5
  292. package/dist/mcp/tools/repositories.d.ts +2 -0
  293. package/dist/mcp/tools/repositories.js +181 -0
  294. package/dist/mcp/tools/runs.d.ts +6 -0
  295. package/dist/mcp/tools/runs.js +60 -6
  296. package/dist/mcp/tools/scheduling.js +4 -7
  297. package/dist/mcp/tools/secret-elicitation-form.d.ts +1 -1
  298. package/dist/mcp/tools/secret-elicitation-form.js +10 -28
  299. package/dist/mcp/tools/secret-elicitation.d.ts +1 -1
  300. package/dist/mcp/tools/secrets.js +18 -6
  301. package/dist/mcp/tools/services.d.ts +2 -0
  302. package/dist/mcp/tools/services.js +222 -0
  303. package/dist/mcp/tools/subagents.js +53 -17
  304. package/dist/mcp/tools/tools.d.ts +41 -0
  305. package/dist/mcp/tools/tools.js +202 -41
  306. package/dist/mcp/tools/trigger.js +36 -6
  307. package/dist/mcp/tools/webhooks.js +15 -4
  308. package/dist/mcp/transport/streamable-http.d.ts +11 -1
  309. package/dist/mcp/transport/streamable-http.js +32 -2
  310. package/dist/mcp/unattended-schedules.d.ts +1 -1
  311. package/dist/mcp/webhooks/ingress.d.ts +1 -1
  312. package/dist/providers/auth/delegating.d.ts +19 -0
  313. package/dist/providers/auth/delegating.js +71 -0
  314. package/dist/providers/auth/index.d.ts +3 -1
  315. package/dist/providers/auth/index.js +11 -0
  316. package/dist/providers/auth/self-hosted.d.ts +4 -3
  317. package/dist/providers/auth/self-hosted.js +16 -4
  318. package/dist/providers/auth/types.d.ts +6 -0
  319. package/dist/providers/coding-proxy/memory-ledger.d.ts +3 -0
  320. package/dist/providers/coding-proxy/memory-ledger.js +21 -2
  321. package/dist/providers/coding-proxy/metering.d.ts +4 -0
  322. package/dist/providers/coding-proxy/metering.js +28 -2
  323. package/dist/providers/coding-proxy/prisma-ledger.d.ts +18 -1
  324. package/dist/providers/coding-proxy/prisma-ledger.js +50 -5
  325. package/dist/providers/coding-proxy/proxy.d.ts +13 -0
  326. package/dist/providers/coding-proxy/proxy.js +479 -18
  327. package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
  328. package/dist/providers/coding-proxy/registry/audit.js +380 -0
  329. package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
  330. package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
  331. package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
  332. package/dist/providers/coding-proxy/registry/plan.js +304 -0
  333. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
  334. package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
  335. package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
  336. package/dist/providers/coding-proxy/registry/service.js +1137 -0
  337. package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
  338. package/dist/providers/coding-proxy/registry/store.js +70 -0
  339. package/dist/providers/coding-proxy/runtime.d.ts +18 -1
  340. package/dist/providers/coding-proxy/runtime.js +63 -0
  341. package/dist/providers/coding-proxy/secure-fetch.js +0 -1
  342. package/dist/providers/coding-proxy/server.d.ts +11 -0
  343. package/dist/providers/coding-proxy/server.js +163 -0
  344. package/dist/providers/coding-proxy/types.d.ts +15 -0
  345. package/dist/providers/datastore/postgres.d.ts +1 -1
  346. package/dist/providers/engine/types.d.ts +10 -0
  347. package/dist/providers/executor/build.d.ts +3 -3
  348. package/dist/providers/executor/composition.d.ts +4 -1
  349. package/dist/providers/executor/composition.js +19 -0
  350. package/dist/providers/executor/container.d.ts +106 -4
  351. package/dist/providers/executor/container.js +305 -22
  352. package/dist/providers/executor/dbos.d.ts +3 -4
  353. package/dist/providers/executor/in-process.d.ts +2 -3
  354. package/dist/providers/executor/routing.d.ts +7 -0
  355. package/dist/providers/executor/routing.js +13 -0
  356. package/dist/providers/executor/types.d.ts +22 -0
  357. package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
  358. package/dist/providers/jobs/claude-tool-setup.js +50 -0
  359. package/dist/providers/jobs/collect-prune.d.ts +7 -0
  360. package/dist/providers/jobs/collect-prune.js +27 -0
  361. package/dist/providers/jobs/docker-isolation.d.ts +48 -3
  362. package/dist/providers/jobs/docker-isolation.js +213 -22
  363. package/dist/providers/jobs/docker-services.d.ts +35 -0
  364. package/dist/providers/jobs/docker-services.js +191 -0
  365. package/dist/providers/jobs/docker.d.ts +60 -2
  366. package/dist/providers/jobs/docker.js +313 -50
  367. package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
  368. package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
  369. package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
  370. package/dist/providers/jobs/kubernetes-isolation.js +238 -57
  371. package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
  372. package/dist/providers/jobs/kubernetes-platform.js +11 -5
  373. package/dist/providers/jobs/kubernetes-preflight.js +3 -0
  374. package/dist/providers/jobs/kubernetes.d.ts +21 -2
  375. package/dist/providers/jobs/kubernetes.js +117 -31
  376. package/dist/providers/jobs/types.d.ts +20 -0
  377. package/dist/providers/llm/anthropic.js +6 -2
  378. package/dist/providers/llm/bedrock.js +2 -1
  379. package/dist/providers/llm/claude-messages.d.ts +5 -1
  380. package/dist/providers/llm/claude-messages.js +1 -0
  381. package/dist/providers/llm/claude-provider.d.ts +3 -1
  382. package/dist/providers/llm/claude-provider.js +5 -1
  383. package/dist/providers/llm/index.d.ts +1 -1
  384. package/dist/providers/llm/index.js +1 -1
  385. package/dist/providers/llm/pricing-anthropic.d.ts +10 -0
  386. package/dist/providers/llm/pricing-anthropic.js +32 -14
  387. package/dist/providers/llm/pricing-bedrock-claude.d.ts +8 -0
  388. package/dist/providers/llm/pricing-bedrock-claude.js +9 -0
  389. package/dist/providers/llm/routing.d.ts +10 -1
  390. package/dist/providers/llm/routing.js +18 -0
  391. package/dist/providers/llm/types.d.ts +12 -0
  392. package/dist/providers/llm/types.js +8 -1
  393. package/dist/providers/memory/postgres.d.ts +1 -1
  394. package/dist/providers/review-host/diff-lines.d.ts +16 -0
  395. package/dist/providers/review-host/diff-lines.js +59 -0
  396. package/dist/providers/review-host/github-events.d.ts +6 -0
  397. package/dist/providers/review-host/github-events.js +180 -0
  398. package/dist/providers/review-host/github-user-auth.d.ts +38 -0
  399. package/dist/providers/review-host/github-user-auth.js +128 -0
  400. package/dist/providers/review-host/github.d.ts +57 -0
  401. package/dist/providers/review-host/github.js +567 -0
  402. package/dist/providers/review-host/index.d.ts +12 -0
  403. package/dist/providers/review-host/index.js +30 -0
  404. package/dist/providers/review-host/review-format.d.ts +19 -0
  405. package/dist/providers/review-host/review-format.js +59 -0
  406. package/dist/providers/review-host/types.d.ts +284 -0
  407. package/dist/providers/review-host/types.js +24 -0
  408. package/dist/providers/vcs/git.d.ts +16 -6
  409. package/dist/providers/vcs/git.js +79 -14
  410. package/dist/providers/vcs/github.d.ts +74 -3
  411. package/dist/providers/vcs/github.js +195 -15
  412. package/dist/providers/vcs/types.d.ts +42 -4
  413. package/dist/quickstart/index.js +34 -32
  414. package/dist/quickstart/migrate.d.ts +23 -0
  415. package/dist/quickstart/migrate.js +125 -0
  416. package/dist/sandbox/fetch-policy.d.ts +20 -2
  417. package/dist/sandbox/fetch-policy.js +64 -3
  418. package/dist/sandbox/host-functions.d.ts +9 -1
  419. package/dist/sandbox/host-functions.js +12 -5
  420. package/dist/serve.js +1 -1
  421. package/dist/wardby-bin.js +6 -0
  422. package/docs/README.md +30 -0
  423. package/docs/architecture-runtime.md +90 -0
  424. package/docs/assets/brand/wardby-icon-512.png +0 -0
  425. package/docs/assets/brand/wardby-icon.svg +16 -0
  426. package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
  427. package/docs/assets/brand/wardby-mascot.png +0 -0
  428. package/docs/assets/brand/wardby-mascot.svg +5 -0
  429. package/docs/assets/wardby-workflow.svg +106 -0
  430. package/docs/code-review-agents.md +481 -0
  431. package/docs/coding-agent-setup.md +172 -0
  432. package/docs/coding-packages.md +455 -0
  433. package/docs/coding-services.md +300 -0
  434. package/docs/coding-worker-byo-images.md +98 -0
  435. package/docs/coding-worker-isolation.md +1061 -0
  436. package/docs/getting-started-gke.md +615 -0
  437. package/docs/getting-started-identity-provider.md +308 -0
  438. package/docs/getting-started.md +133 -0
  439. package/docs/observability.md +53 -0
  440. package/docs/release-verification.md +66 -0
  441. package/docs/security-deployment.md +657 -0
  442. package/help/code-review-agents.md +31 -0
  443. package/help/coding-packages.md +31 -0
  444. package/help/coding-services.md +71 -0
  445. package/help/creating-agents.md +68 -0
  446. package/help/deploy-gke.md +39 -0
  447. package/help/deployment-targets.md +39 -0
  448. package/help/errors/budget-group-exhausted.md +25 -0
  449. package/help/errors/docker-isolation-unsupported.md +26 -0
  450. package/help/errors/protected-path.md +45 -0
  451. package/help/errors/repo-access.md +27 -0
  452. package/help/errors/service-declaration-invalid.md +27 -0
  453. package/help/errors/service-declaration-unavailable.md +25 -0
  454. package/help/errors/service-launcher-unsupported.md +30 -0
  455. package/help/errors/service-not-allowed.md +27 -0
  456. package/help/errors/service-unknown.md +23 -0
  457. package/help/errors/service-unready.md +49 -0
  458. package/help/getting-started.md +31 -0
  459. package/help/github.md +30 -0
  460. package/help/identity-and-access.md +39 -0
  461. package/help/mcp.md +30 -0
  462. package/help/native-capabilities.md +30 -0
  463. package/help/observability.md +41 -0
  464. package/help/operating-agents.md +30 -0
  465. package/help/security.md +27 -0
  466. package/help/troubleshooting/budgets.md +26 -0
  467. package/help/troubleshooting/coding-workers.md +47 -0
  468. package/help/troubleshooting/repository-access.md +27 -0
  469. package/package.json +30 -11
  470. package/prisma/migrate.config.mjs +20 -0
  471. package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
  472. package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
  473. package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
  474. package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
  475. package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
  476. package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
  477. package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
  478. package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
  479. package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
  480. package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
  481. package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
  482. package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
  483. package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
  484. package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
  485. package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
  486. package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
  487. package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
  488. package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
  489. package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
  490. package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
  491. package/prisma/schema.prisma +417 -23
@@ -29,20 +29,26 @@ import { finished } from "node:stream/promises";
29
29
  import { createHash } from "node:crypto";
30
30
  import { isIP } from "node:net";
31
31
  import { logger } from "../../core/logger.js";
32
- import { MAX_CODING_ARTIFACT_BYTES, parseCodingAgentOutputJson } from "../../coding/protocol.js";
32
+ import { MAX_CODING_ARTIFACT_BYTES, MAX_DEBUG_TRACE_LINE_BYTES, parseCodingAgentOutputJson, } from "../../coding/protocol.js";
33
33
  import { CODING_PROXY_DENY_PORT, CODING_PROXY_PORT } from "./docker-isolation.js";
34
- import { SAFE_WORKER_DIAGNOSTIC } from "./docker.js";
34
+ import { parseWorkerDiagnosticLine } from "./docker.js";
35
35
  import { KubernetesAlreadyExistsError, KubernetesConflictError } from "./kubernetes-api.js";
36
- import { ENFORCEMENT_PROBE_DENY_REACHABLE, ENFORCEMENT_PROBE_PROVEN, ENFORCEMENT_PROBE_DENY_REFUSED, ENFORCEMENT_PROBE_PROXY_UNREACHABLE, KEEPER_CONTAINER, KEEPER_SEEDED_MARKER, KUBERNETES_ISOLATION_ERROR, STORAGE_ROOT, WORKER_CONTAINER, assertRunNetworkPolicyMatches, assertRunPodMatches, buildCapabilitySecret, buildRunNetworkPolicy, buildRunPod, enforcementProbeScript, kubernetesRunNames, kubernetesRunNamesForToken, runLabels, validateKubernetesSpec, } from "./kubernetes-isolation.js";
36
+ import { ENFORCEMENT_PROBE_DENY_REACHABLE, ENFORCEMENT_PROBE_PROVEN, ENFORCEMENT_PROBE_DENY_REFUSED, ENFORCEMENT_PROBE_PROXY_UNREACHABLE, KEEPER_CONTAINER, KEEPER_SEEDED_MARKER, KUBERNETES_ISOLATION_ERROR, STORAGE_ROOT, TOOL_RUNNER_CONTAINER, WORKER_CONTAINER, assertRunNetworkPolicyMatches, assertRunPodMatches, buildCapabilitySecret, buildRunNetworkPolicy, buildRunPod, enforcementProbeScript, kubernetesRunNames, kubernetesRunNamesForToken, runLabels, serviceContainerName, validateKubernetesSpec, } from "./kubernetes-isolation.js";
37
+ import { claudeToolSetup } from "./claude-tool-setup.js";
37
38
  import { readProxyWitness } from "./kubernetes-witness.js";
38
39
  import { safeExtract } from "./safe-extract.js";
40
+ import { serviceUnreadyError } from "../../coding/services/wording.js";
39
41
  import { replaceDirectoryFromStaging } from "./workspace-swap.js";
42
+ import { collectExclusions, normalizeCollectExclusions, tarExcludeArgs, } from "../../coding/collect-exclude.js";
40
43
  const BACKEND = "kubernetes";
41
44
  const RECORD_SCHEMA_VERSION = 1;
42
45
  const RECORD_KEY = "record.json";
43
46
  const STOP_GRACE_SECONDS = 10;
44
47
  const DIAGNOSTIC_TAIL_LINES = 8;
45
- const DIAGNOSTIC_LIMIT_BYTES = 4096;
48
+ // The API cuts the tail from its FRONT at limitBytes, so this must hold every
49
+ // tail line at full size: a debug-traced run writes lines of up to
50
+ // MAX_DEBUG_TRACE_LINE_BYTES just before its diagnostic line.
51
+ const DIAGNOSTIC_LIMIT_BYTES = DIAGNOSTIC_TAIL_LINES * MAX_DEBUG_TRACE_LINE_BYTES;
46
52
  const MAX_WORKSPACE_ENTRIES = 100_000;
47
53
  const MAX_RECORD_ATTEMPTS = 5;
48
54
  const READY_POLL_MS = 250;
@@ -51,8 +57,17 @@ const DEFAULT_ENFORCEMENT_TIMEOUT_MS = 30_000;
51
57
  const ENFORCEMENT_POLL_MS = 500;
52
58
  /** Consecutive proven probes required (8787 reachable, 8788 blocked): one dropped SYN must not open the gate. */
53
59
  const ENFORCEMENT_BLOCKED_STREAK = 3;
54
- /** Bound for one enforcement probe exec (the probe makes two sequential connects, so it gives up after at most 6 s). */
55
- const ENFORCEMENT_EXEC_TIMEOUT_MS = 10_000;
60
+ /**
61
+ * Default bound for one enforcement probe exec (the probe makes two sequential connects, so it
62
+ * gives up after at most 6 s of its own connect timeouts). Overridable per KubernetesJobConfig's
63
+ * `enforcementExecTimeoutMs` (KUBERNETES_ENFORCEMENT_EXEC_TIMEOUT_MS) — a resource-constrained
64
+ * keeper (e.g. a laptop `kind` cluster's default 250m CPU / 128Mi limit) can take noticeably
65
+ * longer than this to run even a healthy probe to completion. DEFAULT_ENFORCEMENT_TIMEOUT_MS
66
+ * above is exactly this default times ENFORCEMENT_BLOCKED_STREAK, which is not a coincidence: see
67
+ * the constructor, which derives the effective overall bound the same way from whichever exec
68
+ * timeout is actually configured, so the two never drift apart.
69
+ */
70
+ const DEFAULT_ENFORCEMENT_EXEC_TIMEOUT_MS = 10_000;
56
71
  /**
57
72
  * What the gate reports at its bound, by the last probe's exit code. Each names a different place
58
73
  * to look, and each carries the reason in its own message rather than in the documentation: an
@@ -224,6 +239,11 @@ function exitedCleanlyInTime(pod, finishedAt, deadlineAt) {
224
239
  const finished = new Date(finishedAt).getTime();
225
240
  return Number.isFinite(finished) && finished <= deadlineAt + FINISHED_AT_SKEW_MS;
226
241
  }
242
+ /** A Claude run's tool runner that restarted or exited: its socket (and any in-flight command) is gone. */
243
+ function toolRunnerFailed(statuses) {
244
+ const tool = statuses?.find((status) => status.name === TOOL_RUNNER_CONTAINER);
245
+ return tool !== undefined && ((tool.restartCount ?? 0) > 0 || tool.state?.terminated !== undefined);
246
+ }
227
247
  /** Maps a pod read-back onto the next record state; `undefined` means "no change". Never called for terminal records. */
228
248
  function observePod(pod, record, now, readyTimeoutMs) {
229
249
  if (!pod) {
@@ -242,6 +262,16 @@ function observePod(pod, record, now, readyTimeoutMs) {
242
262
  if (now >= record.deadlineAt || pod.status?.reason === "DeadlineExceeded") {
243
263
  return { phase: "failed", result: { exitCode: 124, reason: "timed_out" }, deleteGraceSeconds: STOP_GRACE_SECONDS };
244
264
  }
265
+ // Docker's worker_tool_runner_failed, for the same reason: without its tool runner a Claude worker
266
+ // can only stall until the deadline. Checked only while the worker still runs, so a clean finish
267
+ // (checked above) or the pod's own teardown never reads as a tool-runner failure.
268
+ if (!terminated && toolRunnerFailed(pod.status?.initContainerStatuses)) {
269
+ return {
270
+ phase: "failed",
271
+ result: { exitCode: 1, reason: "failed", diagnostic: "worker_tool_runner_failed" },
272
+ deleteGraceSeconds: STOP_GRACE_SECONDS,
273
+ };
274
+ }
245
275
  if (terminated) {
246
276
  // An exit 0 that failed the guard above (pod being deleted, no usable finish time) is not a success.
247
277
  if (terminated.exitCode === 0)
@@ -328,6 +358,7 @@ export class KubernetesJobLauncher {
328
358
  createArchive;
329
359
  warn;
330
360
  enforcementTimeoutMs;
361
+ enforcementExecTimeoutMs;
331
362
  preflightResult;
332
363
  constructor(options) {
333
364
  this.options = options;
@@ -339,10 +370,24 @@ export class KubernetesJobLauncher {
339
370
  this.now = options.now ?? Date.now;
340
371
  this.sleep = options.sleep ?? ((ms) => new Promise((resolvePromise) => setTimeout(resolvePromise, ms)));
341
372
  this.readyTimeoutMs = options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS;
342
- this.enforcementTimeoutMs = options.enforcementTimeoutMs ?? DEFAULT_ENFORCEMENT_TIMEOUT_MS;
373
+ this.enforcementExecTimeoutMs = options.enforcementExecTimeoutMs ?? DEFAULT_ENFORCEMENT_EXEC_TIMEOUT_MS;
374
+ // Never less than a full blocked-streak's worth of worst-case probes: a longer configured exec
375
+ // timeout that left this unchanged could make the wall-clock bound expire before the loop ever
376
+ // has a chance to observe ENFORCEMENT_BLOCKED_STREAK consecutive probes, which would turn "the
377
+ // keeper is just slow" into an indistinguishable "the policy is not enforced" false negative.
378
+ // DEFAULT_ENFORCEMENT_TIMEOUT_MS (30_000) already equals the default exec timeout times the
379
+ // streak (10_000 * 3), so this is a no-op unless a longer exec timeout is actually configured.
380
+ this.enforcementTimeoutMs = Math.max(options.enforcementTimeoutMs ?? DEFAULT_ENFORCEMENT_TIMEOUT_MS, this.enforcementExecTimeoutMs * ENFORCEMENT_BLOCKED_STREAK);
343
381
  this.createArchive = options.createArchive ?? hostTarArchive;
344
382
  this.warn = options.onWarning ?? ((message) => kubernetesLog.warn(message));
345
383
  }
384
+ /**
385
+ * Services run as native sidecars in the run pod (kubernetes-isolation.ts), for either provider:
386
+ * Claude Code's tool runner is a sidecar in the same pod, so it reaches them on 127.0.0.1 too.
387
+ */
388
+ supportsServicesFor(provider) {
389
+ return provider === "codex" || provider === "claude-code";
390
+ }
346
391
  /**
347
392
  * Every per-run object name derives from the run id, so the handle is known before any cluster call.
348
393
  * The executor persists it first; a crash mid-launch then still has a handle to clean up with (the
@@ -359,7 +404,19 @@ export class KubernetesJobLauncher {
359
404
  }
360
405
  }
361
406
  async launch(spec) {
362
- validateKubernetesSpec(spec);
407
+ try {
408
+ validateKubernetesSpec(spec);
409
+ }
410
+ catch (error) {
411
+ // The thrown error already carries the real reason (e.g.
412
+ // "kubernetes_isolation_unsupported:image-not-registry-digest") and is rethrown unchanged;
413
+ // this only makes that reason visible to an operator watching logs before the caller (which
414
+ // may only surface the top-level error code) does anything with it. Nothing here is derived
415
+ // from spec content beyond that fixed reason tag — no image reference, no limit value.
416
+ if (error instanceof Error)
417
+ this.warn(`kubernetes_spec_rejected: ${error.message}`);
418
+ throw error;
419
+ }
363
420
  await this.runPreflight();
364
421
  const names = kubernetesRunNames(spec.runId);
365
422
  const handle = { backend: BACKEND, id: `${this.namespace}/${names.token}` };
@@ -376,6 +433,7 @@ export class KubernetesJobLauncher {
376
433
  createdAt,
377
434
  deadlineAt: createdAt + spec.timeoutSec * 1000,
378
435
  phase: "provisioning",
436
+ collectExclude: spec.collectExclude ?? collectExclusions([]),
379
437
  };
380
438
  try {
381
439
  // The record is the idempotency fence before any other cluster side effect.
@@ -428,7 +486,7 @@ export class KubernetesJobLauncher {
428
486
  if (phase === "failed" && !result.diagnostic) {
429
487
  const diagnostic = await this.readWorkerDiagnostic(names);
430
488
  if (diagnostic)
431
- result = { ...result, diagnostic };
489
+ result = { ...result, ...diagnostic };
432
490
  }
433
491
  if (phase === "succeeded" && !result.resultArtifact) {
434
492
  result = { ...result, resultArtifact: await this.readResultArtifact(names, record.runId) };
@@ -454,7 +512,9 @@ export class KubernetesJobLauncher {
454
512
  throw new Error("kubernetes_workspace_destination_invalid");
455
513
  }
456
514
  const maxBytes = record.diskMb * 1024 * 1024;
457
- await replaceDirectoryFromStaging(expected, maxBytes, "kubernetes_workspace_destination_invalid", (staging) => this.extractWorkspace(names, staging, maxBytes, this.transferBudgetMs(record)));
515
+ await replaceDirectoryFromStaging(expected, maxBytes, "kubernetes_workspace_destination_invalid", (staging) => this.extractWorkspace(names, staging, maxBytes, this.transferBudgetMs(record),
516
+ // Names are always the current built-in list; only the paths come from the record.
517
+ normalizeCollectExclusions(record.collectExclude?.paths)));
458
518
  }
459
519
  async stop(handle, _reason) {
460
520
  const names = this.namesFor(handle);
@@ -532,7 +592,7 @@ export class KubernetesJobLauncher {
532
592
  // `exitCode` is this iteration's probe, and the bound below is only ever reached from here —
533
593
  // so the verdict is always the *last* probe's, never a remembered earlier one.
534
594
  const exitCode = await this.api.exec(this.namespace, names.pod, KEEPER_CONTAINER, command, {
535
- timeoutMs: ENFORCEMENT_EXEC_TIMEOUT_MS,
595
+ timeoutMs: this.enforcementExecTimeoutMs,
536
596
  });
537
597
  blocked = exitCode === 0 ? blocked + 1 : 0;
538
598
  if (blocked >= ENFORCEMENT_BLOCKED_STREAK)
@@ -552,7 +612,7 @@ export class KubernetesJobLauncher {
552
612
  return structuredClone(handle);
553
613
  }
554
614
  async provision(spec, names, record) {
555
- const { runtimeClassName, proxyService, platform } = this.options.config;
615
+ const { runtimeClassName, priorityClassName, proxyService, platform } = this.options.config;
556
616
  if (!runtimeClassName) {
557
617
  this.warn("kubernetes_runtime_class_unset: no runtime class configured; coding pods run without gVisor (development clusters only)");
558
618
  }
@@ -563,12 +623,19 @@ export class KubernetesJobLauncher {
563
623
  // which is only meaningful against a Service that actually exposes it with a ready backend.
564
624
  const witness = await readProxyWitness(this.api, this.namespace, proxyService);
565
625
  const proxyIp = witness.clusterIp;
566
- const pod = buildRunPod(spec, { namespace: this.namespace, proxyIp, runtimeClassName, platform });
626
+ const pod = buildRunPod(spec, {
627
+ namespace: this.namespace,
628
+ proxyIp,
629
+ runtimeClassName,
630
+ priorityClassName,
631
+ platform,
632
+ });
567
633
  const policy = buildRunNetworkPolicy(spec, this.namespace);
568
- await this.createIfMissing(() => this.api.createSecret(this.namespace, buildCapabilitySecret(spec, this.namespace, capability)));
634
+ const toolSetup = spec.provider === "claude-code" ? claudeToolSetup(spec, capability) : undefined;
635
+ await this.createIfMissing(() => this.api.createSecret(this.namespace, buildCapabilitySecret(spec, this.namespace, capability, toolSetup)));
569
636
  await this.createIfMissing(() => this.api.createNetworkPolicy(this.namespace, policy));
570
637
  await this.createIfMissing(() => this.api.createPod(this.namespace, pod));
571
- await this.waitForKeeper(names);
638
+ await this.waitForKeeper(names, (spec.services ?? []).map((service) => service.name), spec.provider === "claude-code");
572
639
  const [actualPod, actualPolicy] = await Promise.all([
573
640
  this.api.readPod(this.namespace, names.pod),
574
641
  this.api.readNetworkPolicy(this.namespace, names.policy),
@@ -612,7 +679,7 @@ export class KubernetesJobLauncher {
612
679
  throw error;
613
680
  }
614
681
  }
615
- async waitForKeeper(names) {
682
+ async waitForKeeper(names, serviceNames, toolRunner = false) {
616
683
  const started = this.now();
617
684
  for (;;) {
618
685
  const pod = await this.api.readPod(this.namespace, names.pod);
@@ -620,6 +687,23 @@ export class KubernetesJobLauncher {
620
687
  throw new Error("kubernetes_pod_start_failed");
621
688
  const statuses = pod.status?.containerStatuses ?? [];
622
689
  const initStatuses = pod.status?.initContainerStatuses ?? [];
690
+ const sidecar = (name) => initStatuses.find((status) => status.name === serviceContainerName(name));
691
+ // A service sidecar that restarted (its startup probe gave up), can't be pulled or created, or
692
+ // exited will never let the keeper start. Name the service rather than a generic start failure.
693
+ const failedService = serviceNames.find((name) => {
694
+ const status = sidecar(name);
695
+ return (status !== undefined &&
696
+ ((status.restartCount ?? 0) > 0 ||
697
+ FATAL_WAITING_REASONS.has(status.state?.waiting?.reason ?? "") ||
698
+ status.state?.terminated !== undefined));
699
+ });
700
+ if (failedService !== undefined)
701
+ throw serviceUnreadyError(failedService);
702
+ const toolStatus = initStatuses.find((status) => status.name === TOOL_RUNNER_CONTAINER);
703
+ if (toolRunner &&
704
+ (toolRunnerFailed(initStatuses) || FATAL_WAITING_REASONS.has(toolStatus?.state?.waiting?.reason ?? ""))) {
705
+ throw new Error("kubernetes_tool_runner_failed");
706
+ }
623
707
  const keeper = statuses.find((status) => status.name === KEEPER_CONTAINER);
624
708
  if ([...initStatuses, ...statuses].some((status) => FATAL_WAITING_REASONS.has(status.state?.waiting?.reason ?? "")) ||
625
709
  // A failed init container (kubectl's Init:Error) or a keeper that already exited can never become ready.
@@ -629,8 +713,17 @@ export class KubernetesJobLauncher {
629
713
  }
630
714
  if (keeper?.ready === true)
631
715
  return;
632
- if (this.now() - started >= this.readyTimeoutMs)
716
+ if (this.now() - started >= this.readyTimeoutMs) {
717
+ if (toolRunner && toolStatus?.started !== true) {
718
+ throw new Error("kubernetes_tool_runner_unready");
719
+ }
720
+ // At the bound, a service that never passed its startup probe (still pulling, still starting)
721
+ // is why the keeper never started.
722
+ const waiting = serviceNames.find((name) => sidecar(name)?.started !== true);
723
+ if (waiting !== undefined)
724
+ throw serviceUnreadyError(waiting);
633
725
  throw new Error("kubernetes_pod_start_timeout");
726
+ }
634
727
  await this.sleep(READY_POLL_MS);
635
728
  }
636
729
  }
@@ -715,14 +808,9 @@ export class KubernetesJobLauncher {
715
808
  return undefined;
716
809
  }
717
810
  for (const line of raw.split("\n").reverse()) {
718
- try {
719
- const value = JSON.parse(line);
720
- if (typeof value?.error === "string" && SAFE_WORKER_DIAGNOSTIC.test(value.error))
721
- return value.error;
722
- }
723
- catch {
724
- // Worker output is untrusted; only one fixed JSON shape is recognized.
725
- }
811
+ const diagnostic = parseWorkerDiagnosticLine(line);
812
+ if (diagnostic)
813
+ return diagnostic;
726
814
  }
727
815
  return undefined;
728
816
  }
@@ -759,9 +847,10 @@ export class KubernetesJobLauncher {
759
847
  * Streams the keeper's workspace through the strict extractor into `staging`.
760
848
  * The transfer is bounded by `timeoutMs` (see transferBudgetMs): on expiry the
761
849
  * extraction is aborted. On any failure the output stream this method created
762
- * is destroyed, since the exec's own timeout does not end it.
850
+ * is destroyed, since the exec's own timeout does not end it. Excluded dependency
851
+ * and cache paths never leave the pod (see src/coding/collect-exclude.ts).
763
852
  */
764
- async extractWorkspace(names, staging, maxBytes, timeoutMs) {
853
+ async extractWorkspace(names, staging, maxBytes, timeoutMs, exclusions) {
765
854
  const output = new PassThrough();
766
855
  // Errors are surfaced through the exec/extraction promises; never let a late destroy() crash the process.
767
856
  output.on("error", () => undefined);
@@ -772,10 +861,7 @@ export class KubernetesJobLauncher {
772
861
  signal: controller.signal,
773
862
  });
774
863
  const execution = this.api
775
- .exec(this.namespace, names.pod, KEEPER_CONTAINER, ["tar", "-C", WORKSPACE_STORAGE, "-cf", "-", "."], {
776
- stdout: output,
777
- timeoutMs,
778
- })
864
+ .exec(this.namespace, names.pod, KEEPER_CONTAINER, ["tar", "-C", WORKSPACE_STORAGE, ...tarExcludeArgs(exclusions), "-cf", "-", "."], { stdout: output, timeoutMs })
779
865
  .then((exitCode) => {
780
866
  if (exitCode !== 0)
781
867
  throw new Error("kubernetes_workspace_archive_failed");
@@ -3,12 +3,15 @@
3
3
  * values are carried in a versioned input artifact, never expanded into an
4
4
  * arbitrary environment map or shell command.
5
5
  */
6
+ import type { CodingProvider } from "../../coding/provider.js";
6
7
  export interface JobResourceLimits {
7
8
  cpus: number;
8
9
  memoryMb: number;
9
10
  pids: number;
10
11
  diskMb: number;
11
12
  }
13
+ import type { CollectExclusions } from "../../coding/collect-exclude.js";
14
+ import type { ResolvedCodingService } from "../../coding/services/catalog.js";
12
15
  export interface JobSpec {
13
16
  kind: "coding-agent";
14
17
  runId: string;
@@ -21,6 +24,16 @@ export interface JobSpec {
21
24
  timeoutSec: number;
22
25
  limits: JobResourceLimits;
23
26
  labels: Record<string, string>;
27
+ /** Workspace paths never collected; absent on legacy specs, which get the built-in names. */
28
+ collectExclude?: CollectExclusions;
29
+ /**
30
+ * Coding-run services (docs/coding-services.md): catalog snapshots resolved
31
+ * at dispatch, started next to the worker. Their images and environments come
32
+ * from the admin-managed catalog, never from repository content. Absent for
33
+ * a run without services (never an empty list), so such a spec, and its hash,
34
+ * is unchanged.
35
+ */
36
+ services?: ResolvedCodingService[];
24
37
  }
25
38
  /** Opaque provider reference. Callers persist both fields exactly as given. */
26
39
  export interface JobHandle {
@@ -45,6 +58,8 @@ export interface JobResult {
45
58
  resultArtifact?: string;
46
59
  /** Fixed worker-owned failure code; never raw container output. */
47
60
  diagnostic?: string;
61
+ /** Output-schema failure locations (`path:code`) that passed SAFE_CODING_OUTPUT_ISSUE; never values. */
62
+ diagnosticIssues?: string[];
48
63
  }
49
64
  /**
50
65
  * Lifecycle invariants implemented by every launcher:
@@ -75,4 +90,9 @@ export interface JobLauncher {
75
90
  /** A launcher that can safely copy a terminal worker workspace back to trusted storage. */
76
91
  export interface WorkspaceJobLauncher extends JobLauncher {
77
92
  materializeWorkspace: (handle: JobHandle, destination: string) => Promise<void>;
93
+ /**
94
+ * Whether this launcher starts JobSpec.services for a run of `provider`: Kubernetes and Docker, for
95
+ * Codex and Claude Code. Absent means no services at all.
96
+ */
97
+ supportsServicesFor?(provider: CodingProvider): boolean;
78
98
  }
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import Anthropic from "@anthropic-ai/sdk";
8
8
  import { ClaudeLlmProvider } from "./claude-provider.js";
9
- import { anthropicPriceUsd, getAnthropicPricing } from "./pricing-anthropic.js";
9
+ import { anthropicPriceUsd, anthropicSupportedEfforts, getAnthropicPricing } from "./pricing-anthropic.js";
10
10
  export { anthropicSupportedModels } from "./pricing-anthropic.js";
11
11
  export function anthropicCredentialsPresent(env = process.env) {
12
12
  return Boolean(env.ANTHROPIC_API_KEY);
@@ -16,6 +16,10 @@ export class AnthropicLlmProvider extends ClaudeLlmProvider {
16
16
  if (!client && !apiKey) {
17
17
  throw new Error("ANTHROPIC_API_KEY is not set — required by the Anthropic LlmProvider adapter.");
18
18
  }
19
- super(client ?? new Anthropic({ apiKey }), { getPricing: getAnthropicPricing, priceUsd: anthropicPriceUsd });
19
+ super(client ?? new Anthropic({ apiKey }), {
20
+ getPricing: getAnthropicPricing,
21
+ priceUsd: anthropicPriceUsd,
22
+ supportedEfforts: anthropicSupportedEfforts,
23
+ });
20
24
  }
21
25
  }
@@ -12,7 +12,7 @@
12
12
  */
13
13
  import { AnthropicBedrock } from "@anthropic-ai/bedrock-sdk";
14
14
  import { ClaudeLlmProvider } from "./claude-provider.js";
15
- import { bedrockClaudePriceUsd, getBedrockClaudePricing } from "./pricing-bedrock-claude.js";
15
+ import { bedrockClaudePriceUsd, bedrockClaudeSupportedEfforts, getBedrockClaudePricing, } from "./pricing-bedrock-claude.js";
16
16
  export { bedrockClaudeSupportedModels } from "./pricing-bedrock-claude.js";
17
17
  export function bedrockCredentialsPresent(env = process.env) {
18
18
  // A region is the one thing Bedrock always needs; AWS resolves actual
@@ -28,6 +28,7 @@ export class BedrockClaudeLlmProvider extends ClaudeLlmProvider {
28
28
  super(client ?? new AnthropicBedrock({ awsRegion: region }), {
29
29
  getPricing: getBedrockClaudePricing,
30
30
  priceUsd: bedrockClaudePriceUsd,
31
+ supportedEfforts: bedrockClaudeSupportedEfforts,
31
32
  });
32
33
  }
33
34
  }
@@ -1,4 +1,4 @@
1
- import type { LlmMessage, LlmRequest, LlmToolDef, LlmStreamEvent, LlmUsage } from "./types.js";
1
+ import type { LlmEffort, LlmMessage, LlmRequest, LlmToolDef, LlmStreamEvent, LlmUsage } from "./types.js";
2
2
  export interface CacheControl {
3
3
  type: "ephemeral";
4
4
  }
@@ -36,6 +36,10 @@ export interface ClaudeRequest {
36
36
  messages: ClaudeMessage[];
37
37
  tools?: ClaudeTool[];
38
38
  max_tokens: number;
39
+ /** Present only when an effort is set; omitted means the API default. */
40
+ output_config?: {
41
+ effort: LlmEffort;
42
+ };
39
43
  }
40
44
  /**
41
45
  * Same shape sent to the API in `toClaudeRequest` — kept as one function so
@@ -77,6 +77,7 @@ export function toClaudeRequest(req, defaultMaxTokens) {
77
77
  messages,
78
78
  ...(tools && tools.length > 0 ? { tools } : {}),
79
79
  max_tokens: req.maxTokens ?? defaultMaxTokens,
80
+ ...(req.effort ? { output_config: { effort: req.effort } } : {}),
80
81
  };
81
82
  }
82
83
  const EPHEMERAL = { type: "ephemeral" };
@@ -5,7 +5,7 @@
5
5
  * stays exactly what each concrete adapter (anthropic.ts, bedrock.ts) has in
6
6
  * common, and nothing more.
7
7
  */
8
- import type { LlmMessage, LlmProvider, LlmRequest, LlmStreamEvent, LlmToolDef } from "./types.js";
8
+ import type { LlmEffort, LlmMessage, LlmProvider, LlmRequest, LlmStreamEvent, LlmToolDef } from "./types.js";
9
9
  import type { ModelPricing, UsageTokens } from "./pricing-core.js";
10
10
  /**
11
11
  * Structural shape every Claude-protocol SDK client satisfies. Loose on
@@ -24,6 +24,8 @@ export interface ClaudePricingModule {
24
24
  /** Must throw on an unknown model — fail-closed, never price at zero. */
25
25
  getPricing(model: string): ModelPricing;
26
26
  priceUsd(model: string, usage: UsageTokens): number;
27
+ /** Effort levels the model accepts; empty = never send an effort. */
28
+ supportedEfforts(model: string): readonly LlmEffort[];
27
29
  }
28
30
  export declare class ClaudeLlmProvider implements LlmProvider {
29
31
  private readonly client;
@@ -17,7 +17,11 @@ export class ClaudeLlmProvider {
17
17
  this.pricing = pricing;
18
18
  }
19
19
  async *stream(req, signal) {
20
- const claudeReq = withCacheBreakpoints(toClaudeRequest(req, DEFAULT_MAX_TOKENS));
20
+ // Defence in depth behind config-time validation: a level the model
21
+ // doesn't accept would fail the whole call, so it is dropped here.
22
+ const { effort, ...rest } = req;
23
+ const accepted = effort && this.pricing.supportedEfforts(req.model).includes(effort);
24
+ const claudeReq = withCacheBreakpoints(toClaudeRequest(accepted ? { ...rest, effort } : rest, DEFAULT_MAX_TOKENS));
21
25
  const raw = this.client.messages.stream({ model: req.model, ...claudeReq }, { signal });
22
26
  yield* mapClaudeStream(raw, (usage) => this.priceUsd(req.model, usage));
23
27
  }
@@ -6,5 +6,5 @@ export { BedrockClaudeLlmProvider, bedrockCredentialsPresent, bedrockClaudeSuppo
6
6
  export { ClaudeLlmProvider, type ClaudeMessagesClient, type ClaudePricingModule } from "./claude-provider.js";
7
7
  export { openaiCredentialsPresent } from "./openai.js";
8
8
  export { supportedModels as openaiSupportedModels } from "./pricing.js";
9
- export { RoutingLlmProvider, type LlmRegistration } from "./routing.js";
9
+ export { RoutingLlmProvider, modelAcceptsEffort, modelSupportedEfforts, type LlmRegistration } from "./routing.js";
10
10
  export { resolveLlmRegistrations, type LlmRegistrationResult } from "./registration.js";
@@ -6,5 +6,5 @@ export { BedrockClaudeLlmProvider, bedrockCredentialsPresent, bedrockClaudeSuppo
6
6
  export { ClaudeLlmProvider } from "./claude-provider.js";
7
7
  export { openaiCredentialsPresent } from "./openai.js";
8
8
  export { supportedModels as openaiSupportedModels } from "./pricing.js";
9
- export { RoutingLlmProvider } from "./routing.js";
9
+ export { RoutingLlmProvider, modelAcceptsEffort, modelSupportedEfforts } from "./routing.js";
10
10
  export { resolveLlmRegistrations } from "./registration.js";
@@ -1,4 +1,14 @@
1
+ /**
2
+ * Anthropic direct-API pricing table. Every entry is a fully literal,
3
+ * hardcoded ModelPricing — every field, including cache read/write, is the
4
+ * provider's own published rate for that exact model, never derived from
5
+ * another field via a ratio (see CLAUDE.md "LLM pricing tables — STRICT": a
6
+ * multiplier that happens to be correct today silently goes stale the
7
+ * moment one model's real cache pricing diverges from the pattern).
8
+ */
1
9
  import { type ModelPricing, type UsageTokens } from "./pricing-core.js";
10
+ import type { LlmEffort } from "./types.js";
11
+ export declare function anthropicSupportedEfforts(model: string): readonly LlmEffort[];
2
12
  export declare function getAnthropicPricing(model: string): ModelPricing;
3
13
  export declare function anthropicSupportedModels(): string[];
4
14
  export declare function anthropicPriceUsd(model: string, usage: UsageTokens): number;
@@ -1,21 +1,39 @@
1
+ /**
2
+ * Anthropic direct-API pricing table. Every entry is a fully literal,
3
+ * hardcoded ModelPricing — every field, including cache read/write, is the
4
+ * provider's own published rate for that exact model, never derived from
5
+ * another field via a ratio (see CLAUDE.md "LLM pricing tables — STRICT": a
6
+ * multiplier that happens to be correct today silently goes stale the
7
+ * moment one model's real cache pricing diverges from the pattern).
8
+ */
1
9
  import { computeCost } from "./pricing-core.js";
2
- const CACHE_READ_MULT = 0.1;
3
- const CACHE_WRITE_MULT = 1.25;
4
- function claude(inputPerMTok, outputPerMTok) {
5
- return {
6
- encoding: "o200k_base",
7
- inputPerMTok,
8
- outputPerMTok,
9
- cachedInputPerMTok: inputPerMTok * CACHE_READ_MULT,
10
- cacheWritePerMTok: inputPerMTok * CACHE_WRITE_MULT,
11
- };
10
+ function claude(inputPerMTok, outputPerMTok, cachedInputPerMTok, cacheWritePerMTok) {
11
+ return { encoding: "o200k_base", inputPerMTok, outputPerMTok, cachedInputPerMTok, cacheWritePerMTok };
12
12
  }
13
+ // Rates are the exact published 5-minute cache-TTL numbers
14
+ // (platform.claude.com/docs/en/about-claude/pricing, "Model pricing" table)
15
+ // — wardby only ever emits the default 5-minute cache_control breakpoint,
16
+ // so the 1-hour-TTL rates are out of scope and not stored here.
13
17
  const PRICING = {
14
- "claude-opus-5": claude(5, 25),
15
- "claude-sonnet-5": claude(2, 10),
16
- "claude-fable-5": claude(10, 50),
17
- "claude-haiku-4-5": claude(1, 5),
18
+ "claude-opus-5": claude(5, 25, 0.5, 6.25),
19
+ "claude-sonnet-5": claude(2, 10, 0.2, 2.5),
20
+ "claude-fable-5": claude(10, 50, 1, 12.5),
21
+ "claude-haiku-4-5": claude(1, 5, 0.1, 1.25),
18
22
  };
23
+ // Reasoning effort levels each model accepts (`output_config.effort`). The
24
+ // single source of truth for direct-API Claude models; a model missing here
25
+ // accepts no effort, so a new roster entry never gets one by accident.
26
+ // Haiku 4.5 rejects the parameter outright.
27
+ const ALL_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
28
+ const EFFORTS = {
29
+ "claude-opus-5": ALL_EFFORTS,
30
+ "claude-sonnet-5": ALL_EFFORTS,
31
+ "claude-fable-5": ALL_EFFORTS,
32
+ "claude-haiku-4-5": [],
33
+ };
34
+ export function anthropicSupportedEfforts(model) {
35
+ return EFFORTS[model] ?? [];
36
+ }
19
37
  export function getAnthropicPricing(model) {
20
38
  const p = PRICING[model];
21
39
  if (!p)
@@ -7,6 +7,14 @@
7
7
  * moment one model's real cache pricing diverges from the pattern).
8
8
  */
9
9
  import { type ModelPricing, type UsageTokens } from "./pricing-core.js";
10
+ import type { LlmEffort } from "./types.js";
10
11
  export declare function getBedrockClaudePricing(model: string): ModelPricing;
11
12
  export declare function bedrockClaudeSupportedModels(): string[];
13
+ /**
14
+ * Reasoning effort is not sent through Bedrock: whether InvokeModel accepts
15
+ * the Messages `output_config.effort` field for these models is unconfirmed,
16
+ * and an unaccepted field fails the whole call. Every Bedrock model therefore
17
+ * accepts no effort until that is verified per model.
18
+ */
19
+ export declare function bedrockClaudeSupportedEfforts(_model: string): readonly LlmEffort[];
12
20
  export declare function bedrockClaudePriceUsd(model: string, usage: UsageTokens): number;
@@ -32,6 +32,15 @@ export function getBedrockClaudePricing(model) {
32
32
  export function bedrockClaudeSupportedModels() {
33
33
  return Object.keys(PRICING);
34
34
  }
35
+ /**
36
+ * Reasoning effort is not sent through Bedrock: whether InvokeModel accepts
37
+ * the Messages `output_config.effort` field for these models is unconfirmed,
38
+ * and an unaccepted field fails the whole call. Every Bedrock model therefore
39
+ * accepts no effort until that is verified per model.
40
+ */
41
+ export function bedrockClaudeSupportedEfforts(_model) {
42
+ return [];
43
+ }
35
44
  export function bedrockClaudePriceUsd(model, usage) {
36
45
  return computeCost(getBedrockClaudePricing(model), usage);
37
46
  }
@@ -4,7 +4,16 @@
4
4
  * selection (Agent.model) selects the provider too — GPT and Claude agents
5
5
  * run in one deployment. Unknown model / duplicate registration fail closed.
6
6
  */
7
- import type { LlmMessage, LlmProvider, LlmRequest, LlmStreamEvent, LlmToolDef } from "./types.js";
7
+ import type { LlmEffort, LlmMessage, LlmProvider, LlmRequest, LlmStreamEvent, LlmToolDef } from "./types.js";
8
+ /**
9
+ * Effort levels `model` accepts, whichever provider owns it — for config-time
10
+ * validation, so it needs no credentials. Each provider's own table is the
11
+ * source of truth. OpenAI models accept none: the Chat Completions
12
+ * `reasoning_effort` parameter is documented for o-series models only, and
13
+ * none is on the roster.
14
+ */
15
+ export declare function modelSupportedEfforts(model: string): readonly LlmEffort[];
16
+ export declare function modelAcceptsEffort(model: string, effort: LlmEffort): boolean;
8
17
  export interface LlmRegistration {
9
18
  provider: LlmProvider;
10
19
  models: string[];
@@ -1,3 +1,21 @@
1
+ import { anthropicSupportedEfforts } from "./pricing-anthropic.js";
2
+ import { bedrockClaudeSupportedEfforts } from "./pricing-bedrock-claude.js";
3
+ /**
4
+ * Effort levels `model` accepts, whichever provider owns it — for config-time
5
+ * validation, so it needs no credentials. Each provider's own table is the
6
+ * source of truth. OpenAI models accept none: the Chat Completions
7
+ * `reasoning_effort` parameter is documented for o-series models only, and
8
+ * none is on the roster.
9
+ */
10
+ export function modelSupportedEfforts(model) {
11
+ const direct = anthropicSupportedEfforts(model);
12
+ if (direct.length > 0)
13
+ return direct;
14
+ return bedrockClaudeSupportedEfforts(model);
15
+ }
16
+ export function modelAcceptsEffort(model, effort) {
17
+ return modelSupportedEfforts(model).includes(effort);
18
+ }
1
19
  export class RoutingLlmProvider {
2
20
  byModel = new Map();
3
21
  constructor(registrations) {
@@ -35,6 +35,13 @@ export interface LlmToolDef {
35
35
  /** JSON Schema for the tool's parameters. */
36
36
  parameters: Record<string, unknown>;
37
37
  }
38
+ /**
39
+ * Reasoning effort levels, lowest to highest. Which levels a model accepts
40
+ * is per model and per provider — ask `modelSupportedEfforts` (routing.ts).
41
+ */
42
+ export declare const LLM_EFFORT_LEVELS: readonly ["low", "medium", "high", "xhigh", "max"];
43
+ export type LlmEffort = (typeof LLM_EFFORT_LEVELS)[number];
44
+ export declare function isLlmEffort(value: unknown): value is LlmEffort;
38
45
  export interface LlmRequest {
39
46
  model: string;
40
47
  messages: LlmMessage[];
@@ -42,6 +49,11 @@ export interface LlmRequest {
42
49
  maxTokens?: number;
43
50
  temperature?: number;
44
51
  stopSequences?: string[];
52
+ /**
53
+ * Reasoning effort. Unset = the provider's own default. An adapter sends
54
+ * it only when the model accepts that level, and drops it otherwise.
55
+ */
56
+ effort?: LlmEffort;
45
57
  }
46
58
  export interface LlmUsage {
47
59
  /** Total input tokens, including any served from cache. */
@@ -9,4 +9,11 @@
9
9
  * Selected per-agent by RoutingLlmProvider (Agent.model -> provider).
10
10
  * Bedrock-hosted Claude is a reserved future adapter (shared claude-messages.ts core).
11
11
  */
12
- export {};
12
+ /**
13
+ * Reasoning effort levels, lowest to highest. Which levels a model accepts
14
+ * is per model and per provider — ask `modelSupportedEfforts` (routing.ts).
15
+ */
16
+ export const LLM_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
17
+ export function isLlmEffort(value) {
18
+ return typeof value === "string" && LLM_EFFORT_LEVELS.includes(value);
19
+ }