terramend 0.2.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 (406) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +145 -0
  3. package/dist/agents/claude.d.ts +73 -0
  4. package/dist/agents/claudePretoolGate.d.ts +99 -0
  5. package/dist/agents/gateServer.d.ts +7 -0
  6. package/dist/agents/index.d.ts +6 -0
  7. package/dist/agents/nativeFsDenies.d.ts +28 -0
  8. package/dist/agents/opencode.d.ts +231 -0
  9. package/dist/agents/opencodePlugin.d.ts +85 -0
  10. package/dist/agents/opencodeShared.d.ts +40 -0
  11. package/dist/agents/postRun.d.ts +132 -0
  12. package/dist/agents/reviewer.d.ts +38 -0
  13. package/dist/agents/sessionLabeler.d.ts +97 -0
  14. package/dist/agents/shared.d.ts +189 -0
  15. package/dist/agents/subagentModels.d.ts +19 -0
  16. package/dist/agents/subagentToolGates.d.ts +55 -0
  17. package/dist/cli.mjs +197426 -0
  18. package/dist/external.d.ts +227 -0
  19. package/dist/index.d.ts +6 -0
  20. package/dist/index.js +196783 -0
  21. package/dist/internal/index.d.ts +18 -0
  22. package/dist/internal.js +1714 -0
  23. package/dist/lifecycle.d.ts +2 -0
  24. package/dist/main.d.ts +8 -0
  25. package/dist/mcp/arkConfig.d.ts +1 -0
  26. package/dist/mcp/checkSuite.d.ts +25 -0
  27. package/dist/mcp/checkout.d.ts +77 -0
  28. package/dist/mcp/comment.d.ts +119 -0
  29. package/dist/mcp/commitInfo.d.ts +9 -0
  30. package/dist/mcp/crosswalk.d.ts +105 -0
  31. package/dist/mcp/dependencies.d.ts +8 -0
  32. package/dist/mcp/geminiSanitizer.d.ts +28 -0
  33. package/dist/mcp/git.d.ts +46 -0
  34. package/dist/mcp/guardrails.d.ts +104 -0
  35. package/dist/mcp/issue.d.ts +18 -0
  36. package/dist/mcp/issueComments.d.ts +9 -0
  37. package/dist/mcp/issueEvents.d.ts +9 -0
  38. package/dist/mcp/issueInfo.d.ts +9 -0
  39. package/dist/mcp/labels.d.ts +12 -0
  40. package/dist/mcp/localContext.d.ts +19 -0
  41. package/dist/mcp/moduleExtraction.d.ts +71 -0
  42. package/dist/mcp/moduleTests.d.ts +104 -0
  43. package/dist/mcp/modules.d.ts +179 -0
  44. package/dist/mcp/output.d.ts +12 -0
  45. package/dist/mcp/pathSafety.d.ts +14 -0
  46. package/dist/mcp/policy.d.ts +48 -0
  47. package/dist/mcp/pr.d.ts +49 -0
  48. package/dist/mcp/prInfo.d.ts +9 -0
  49. package/dist/mcp/providerSchema.d.ts +50 -0
  50. package/dist/mcp/review.d.ts +199 -0
  51. package/dist/mcp/reviewComments.d.ts +178 -0
  52. package/dist/mcp/roots.d.ts +58 -0
  53. package/dist/mcp/scope.d.ts +15 -0
  54. package/dist/mcp/selectMode.d.ts +18 -0
  55. package/dist/mcp/server.d.ts +48 -0
  56. package/dist/mcp/shared.d.ts +47 -0
  57. package/dist/mcp/shell.d.ts +37 -0
  58. package/dist/mcp/staleFix.d.ts +51 -0
  59. package/dist/mcp/terraform/cost.d.ts +55 -0
  60. package/dist/mcp/terraform/currency.d.ts +94 -0
  61. package/dist/mcp/terraform/decisions.d.ts +178 -0
  62. package/dist/mcp/terraform/findings.d.ts +75 -0
  63. package/dist/mcp/terraform/plan.d.ts +157 -0
  64. package/dist/mcp/terraform/scanners.d.ts +131 -0
  65. package/dist/mcp/terraform/tools.d.ts +63 -0
  66. package/dist/mcp/terraform/types.d.ts +172 -0
  67. package/dist/mcp/terraform.d.ts +22 -0
  68. package/dist/mcp/terratest.d.ts +83 -0
  69. package/dist/mcp/upload.d.ts +6 -0
  70. package/dist/models.d.ts +171 -0
  71. package/dist/modes.d.ts +26 -0
  72. package/dist/prep/index.d.ts +7 -0
  73. package/dist/prep/installNodeDependencies.d.ts +2 -0
  74. package/dist/prep/installPythonDependencies.d.ts +2 -0
  75. package/dist/prep/types.d.ts +31 -0
  76. package/dist/reviewQuality.d.ts +64 -0
  77. package/dist/skills/terraform-best-practices/SKILL.md +369 -0
  78. package/dist/toolState.d.ts +135 -0
  79. package/dist/utils/activity.d.ts +40 -0
  80. package/dist/utils/agent.d.ts +20 -0
  81. package/dist/utils/agentHangReport.d.ts +38 -0
  82. package/dist/utils/apiFetch.d.ts +19 -0
  83. package/dist/utils/apiKeys.d.ts +41 -0
  84. package/dist/utils/apiUrl.d.ts +20 -0
  85. package/dist/utils/assets.d.ts +8 -0
  86. package/dist/utils/billingErrors.d.ts +85 -0
  87. package/dist/utils/body.d.ts +34 -0
  88. package/dist/utils/buildTerramendFooter.d.ts +25 -0
  89. package/dist/utils/byokFallback.d.ts +85 -0
  90. package/dist/utils/claudeSubscription.d.ts +30 -0
  91. package/dist/utils/cli.d.ts +10 -0
  92. package/dist/utils/codexHome.d.ts +29 -0
  93. package/dist/utils/codexOAuth.d.ts +60 -0
  94. package/dist/utils/diffCoverage.d.ts +63 -0
  95. package/dist/utils/errorReport.d.ts +17 -0
  96. package/dist/utils/exitHandler.d.ts +8 -0
  97. package/dist/utils/fixDoubleEscapedString.d.ts +1 -0
  98. package/dist/utils/gitAuth.d.ts +84 -0
  99. package/dist/utils/gitAuthServer.d.ts +24 -0
  100. package/dist/utils/github.d.ts +78 -0
  101. package/dist/utils/globals.d.ts +3 -0
  102. package/dist/utils/install.d.ts +60 -0
  103. package/dist/utils/instructions.d.ts +48 -0
  104. package/dist/utils/leapingComment.d.ts +11 -0
  105. package/dist/utils/learnings.d.ts +62 -0
  106. package/dist/utils/learningsTruncate.d.ts +25 -0
  107. package/dist/utils/lifecycle.d.ts +57 -0
  108. package/dist/utils/log.d.ts +111 -0
  109. package/dist/utils/normalizeEnv.d.ts +30 -0
  110. package/dist/utils/openCodeModels.d.ts +11 -0
  111. package/dist/utils/overrides.d.ts +40 -0
  112. package/dist/utils/packageManager.d.ts +49 -0
  113. package/dist/utils/patchWorkflowRunFields.d.ts +29 -0
  114. package/dist/utils/payload.d.ts +105 -0
  115. package/dist/utils/prSummary.d.ts +61 -0
  116. package/dist/utils/progressComment.d.ts +146 -0
  117. package/dist/utils/providerErrors.d.ts +31 -0
  118. package/dist/utils/rangeDiff.d.ts +51 -0
  119. package/dist/utils/remediationCommand.d.ts +55 -0
  120. package/dist/utils/retry.d.ts +13 -0
  121. package/dist/utils/reviewCleanup.d.ts +14 -0
  122. package/dist/utils/run.d.ts +9 -0
  123. package/dist/utils/runContext.d.ts +60 -0
  124. package/dist/utils/runContextData.d.ts +23 -0
  125. package/dist/utils/runErrorRenderer.d.ts +64 -0
  126. package/dist/utils/runLifecycle.d.ts +86 -0
  127. package/dist/utils/runStartupLog.d.ts +15 -0
  128. package/dist/utils/secrets.d.ts +22 -0
  129. package/dist/utils/setup.d.ts +90 -0
  130. package/dist/utils/shell.d.ts +32 -0
  131. package/dist/utils/skills.d.ts +10 -0
  132. package/dist/utils/subprocess.d.ts +80 -0
  133. package/dist/utils/terraformMcp.d.ts +42 -0
  134. package/dist/utils/time.d.ts +15 -0
  135. package/dist/utils/timer.d.ts +23 -0
  136. package/dist/utils/todoTracking.d.ts +16 -0
  137. package/dist/utils/token.d.ts +39 -0
  138. package/dist/utils/version.d.ts +2 -0
  139. package/dist/utils/versioning.d.ts +7 -0
  140. package/dist/utils/vertex.d.ts +16 -0
  141. package/dist/utils/workflow.d.ts +13 -0
  142. package/package.json +119 -0
  143. package/src/agents/claude.test.ts +1016 -0
  144. package/src/agents/claude.ts +1246 -0
  145. package/src/agents/claudePretoolGate.test.ts +28 -0
  146. package/src/agents/claudePretoolGate.ts +173 -0
  147. package/src/agents/gateServer.test.ts +204 -0
  148. package/src/agents/gateServer.ts +124 -0
  149. package/src/agents/index.ts +10 -0
  150. package/src/agents/nativeFsDenies.ts +82 -0
  151. package/src/agents/opencode.test.ts +1440 -0
  152. package/src/agents/opencode.ts +1312 -0
  153. package/src/agents/opencodePlugin.ts +222 -0
  154. package/src/agents/opencodeShared.test.ts +34 -0
  155. package/src/agents/opencodeShared.ts +121 -0
  156. package/src/agents/postRun.test.ts +549 -0
  157. package/src/agents/postRun.ts +535 -0
  158. package/src/agents/reviewer.ts +104 -0
  159. package/src/agents/sessionLabeler.test.ts +247 -0
  160. package/src/agents/sessionLabeler.ts +178 -0
  161. package/src/agents/shared.test.ts +76 -0
  162. package/src/agents/shared.ts +292 -0
  163. package/src/agents/subagentModels.test.ts +113 -0
  164. package/src/agents/subagentModels.ts +40 -0
  165. package/src/agents/subagentRegistration.test.ts +41 -0
  166. package/src/agents/subagentToolGates.ts +114 -0
  167. package/src/cli.test.ts +129 -0
  168. package/src/cli.ts +105 -0
  169. package/src/commands/gha.test.ts +192 -0
  170. package/src/commands/gha.ts +188 -0
  171. package/src/commands/mcp.ts +122 -0
  172. package/src/config.ts +1 -0
  173. package/src/entry.ts +7 -0
  174. package/src/entryPost.stdlibOnly.test.ts +109 -0
  175. package/src/entryPost.ts +99 -0
  176. package/src/external.test.ts +16 -0
  177. package/src/external.ts +302 -0
  178. package/src/index.ts +11 -0
  179. package/src/internal/index.ts +71 -0
  180. package/src/lifecycle.ts +2 -0
  181. package/src/main.test.ts +873 -0
  182. package/src/main.ts +712 -0
  183. package/src/mcp/__fixtures__/terramend-scratch-pr-49-review-3485940013.json +110 -0
  184. package/src/mcp/__fixtures__/terramend-scratch-pr-64-review-3531000326.json +14 -0
  185. package/src/mcp/__fixtures__/terramend-test-repo-pr-1.diff.json +67 -0
  186. package/src/mcp/__snapshots__/checkout.test.ts.snap +109 -0
  187. package/src/mcp/__snapshots__/reviewComments.test.ts.snap +71 -0
  188. package/src/mcp/arkConfig.ts +7 -0
  189. package/src/mcp/checkSuite.test.ts +245 -0
  190. package/src/mcp/checkSuite.ts +255 -0
  191. package/src/mcp/checkout.test.ts +752 -0
  192. package/src/mcp/checkout.ts +886 -0
  193. package/src/mcp/comment.test.ts +772 -0
  194. package/src/mcp/comment.ts +582 -0
  195. package/src/mcp/commitInfo.test.ts +127 -0
  196. package/src/mcp/commitInfo.ts +61 -0
  197. package/src/mcp/crosswalk.test.ts +106 -0
  198. package/src/mcp/crosswalk.ts +339 -0
  199. package/src/mcp/dependencies.test.ts +309 -0
  200. package/src/mcp/dependencies.ts +189 -0
  201. package/src/mcp/geminiSanitizer.test.ts +287 -0
  202. package/src/mcp/geminiSanitizer.ts +207 -0
  203. package/src/mcp/git.test.ts +1083 -0
  204. package/src/mcp/git.ts +890 -0
  205. package/src/mcp/guardrails.test.ts +705 -0
  206. package/src/mcp/guardrails.ts +465 -0
  207. package/src/mcp/issue.test.ts +113 -0
  208. package/src/mcp/issue.ts +73 -0
  209. package/src/mcp/issueComments.test.ts +69 -0
  210. package/src/mcp/issueComments.ts +48 -0
  211. package/src/mcp/issueEvents.test.ts +134 -0
  212. package/src/mcp/issueEvents.ts +100 -0
  213. package/src/mcp/issueInfo.test.ts +104 -0
  214. package/src/mcp/issueInfo.ts +72 -0
  215. package/src/mcp/labels.test.ts +52 -0
  216. package/src/mcp/labels.ts +34 -0
  217. package/src/mcp/localContext.ts +28 -0
  218. package/src/mcp/localServer.test.ts +75 -0
  219. package/src/mcp/localServer.ts +131 -0
  220. package/src/mcp/moduleExtraction.test.ts +261 -0
  221. package/src/mcp/moduleExtraction.ts +313 -0
  222. package/src/mcp/moduleTests.test.ts +269 -0
  223. package/src/mcp/moduleTests.ts +421 -0
  224. package/src/mcp/modules.test.ts +640 -0
  225. package/src/mcp/modules.ts +696 -0
  226. package/src/mcp/output.test.ts +96 -0
  227. package/src/mcp/output.ts +70 -0
  228. package/src/mcp/pathSafety.test.ts +44 -0
  229. package/src/mcp/pathSafety.ts +28 -0
  230. package/src/mcp/policy.test.ts +282 -0
  231. package/src/mcp/policy.ts +199 -0
  232. package/src/mcp/pr.test.ts +387 -0
  233. package/src/mcp/pr.ts +194 -0
  234. package/src/mcp/prInfo.test.ts +96 -0
  235. package/src/mcp/prInfo.ts +91 -0
  236. package/src/mcp/providerSchema.test.ts +85 -0
  237. package/src/mcp/providerSchema.ts +175 -0
  238. package/src/mcp/review.test.ts +936 -0
  239. package/src/mcp/review.ts +923 -0
  240. package/src/mcp/reviewComments.test.ts +549 -0
  241. package/src/mcp/reviewComments.ts +896 -0
  242. package/src/mcp/roots.test.ts +175 -0
  243. package/src/mcp/roots.ts +217 -0
  244. package/src/mcp/scope.test.ts +59 -0
  245. package/src/mcp/scope.ts +65 -0
  246. package/src/mcp/security.test.ts +720 -0
  247. package/src/mcp/selectMode.test.ts +210 -0
  248. package/src/mcp/selectMode.ts +181 -0
  249. package/src/mcp/server.test.ts +292 -0
  250. package/src/mcp/server.ts +403 -0
  251. package/src/mcp/shared.ts +100 -0
  252. package/src/mcp/shell.test.ts +520 -0
  253. package/src/mcp/shell.ts +505 -0
  254. package/src/mcp/staleFix.test.ts +237 -0
  255. package/src/mcp/staleFix.ts +277 -0
  256. package/src/mcp/terraform/cost.ts +163 -0
  257. package/src/mcp/terraform/currency.test.ts +338 -0
  258. package/src/mcp/terraform/currency.ts +336 -0
  259. package/src/mcp/terraform/decisions.ts +527 -0
  260. package/src/mcp/terraform/findings.ts +333 -0
  261. package/src/mcp/terraform/plan.ts +348 -0
  262. package/src/mcp/terraform/scanners.ts +809 -0
  263. package/src/mcp/terraform/tools.test.ts +1071 -0
  264. package/src/mcp/terraform/tools.ts +908 -0
  265. package/src/mcp/terraform/types.ts +305 -0
  266. package/src/mcp/terraform.test.ts +1957 -0
  267. package/src/mcp/terraform.ts +23 -0
  268. package/src/mcp/terratest.test.ts +105 -0
  269. package/src/mcp/terratest.ts +196 -0
  270. package/src/mcp/toolFiltering.test.ts +85 -0
  271. package/src/mcp/upload.test.ts +180 -0
  272. package/src/mcp/upload.ts +112 -0
  273. package/src/models.test.ts +300 -0
  274. package/src/models.ts +708 -0
  275. package/src/modes.test.ts +107 -0
  276. package/src/modes.ts +880 -0
  277. package/src/prep/index.ts +43 -0
  278. package/src/prep/installNodeDependencies.test.ts +298 -0
  279. package/src/prep/installNodeDependencies.ts +196 -0
  280. package/src/prep/installPythonDependencies.test.ts +268 -0
  281. package/src/prep/installPythonDependencies.ts +199 -0
  282. package/src/prep/types.ts +38 -0
  283. package/src/reviewQuality.test.ts +63 -0
  284. package/src/reviewQuality.ts +134 -0
  285. package/src/runCli.test.ts +214 -0
  286. package/src/runCli.ts +282 -0
  287. package/src/skills/terraform-best-practices/SKILL.md +369 -0
  288. package/src/toolState.test.ts +45 -0
  289. package/src/toolState.ts +252 -0
  290. package/src/utils/activity.test.ts +188 -0
  291. package/src/utils/activity.ts +210 -0
  292. package/src/utils/agent.test.ts +251 -0
  293. package/src/utils/agent.ts +139 -0
  294. package/src/utils/agentHangReport.test.ts +203 -0
  295. package/src/utils/agentHangReport.ts +170 -0
  296. package/src/utils/apiFetch.test.ts +115 -0
  297. package/src/utils/apiFetch.ts +62 -0
  298. package/src/utils/apiKeys.test.ts +344 -0
  299. package/src/utils/apiKeys.ts +206 -0
  300. package/src/utils/apiUrl.test.ts +30 -0
  301. package/src/utils/apiUrl.ts +59 -0
  302. package/src/utils/assets.test.ts +153 -0
  303. package/src/utils/assets.ts +107 -0
  304. package/src/utils/billingErrors.test.ts +121 -0
  305. package/src/utils/billingErrors.ts +189 -0
  306. package/src/utils/body.test.ts +217 -0
  307. package/src/utils/body.ts +168 -0
  308. package/src/utils/buildTerramendFooter.test.ts +38 -0
  309. package/src/utils/buildTerramendFooter.ts +82 -0
  310. package/src/utils/byokFallback.test.ts +205 -0
  311. package/src/utils/byokFallback.ts +128 -0
  312. package/src/utils/claudeSubscription.test.ts +179 -0
  313. package/src/utils/claudeSubscription.ts +93 -0
  314. package/src/utils/cli.ts +31 -0
  315. package/src/utils/codexHome.test.ts +190 -0
  316. package/src/utils/codexHome.ts +191 -0
  317. package/src/utils/codexOAuth.ts +147 -0
  318. package/src/utils/codexRefreshDetect.test.ts +85 -0
  319. package/src/utils/codexRefreshDetect.ts +35 -0
  320. package/src/utils/diffCoverage.test.ts +468 -0
  321. package/src/utils/diffCoverage.ts +404 -0
  322. package/src/utils/errorReport.test.ts +135 -0
  323. package/src/utils/errorReport.ts +83 -0
  324. package/src/utils/exitHandler.ts +35 -0
  325. package/src/utils/fixDoubleEscapedString.ts +9 -0
  326. package/src/utils/ghaCore.ts +13 -0
  327. package/src/utils/gitAuth.test.ts +322 -0
  328. package/src/utils/gitAuth.ts +263 -0
  329. package/src/utils/gitAuthServer.test.ts +260 -0
  330. package/src/utils/gitAuthServer.ts +182 -0
  331. package/src/utils/github.test.ts +615 -0
  332. package/src/utils/github.ts +538 -0
  333. package/src/utils/globals.ts +9 -0
  334. package/src/utils/humanEditCapture.test.ts +100 -0
  335. package/src/utils/humanEditCapture.ts +193 -0
  336. package/src/utils/install.test.ts +768 -0
  337. package/src/utils/install.ts +492 -0
  338. package/src/utils/instructions.test.ts +240 -0
  339. package/src/utils/instructions.ts +543 -0
  340. package/src/utils/leapingComment.test.ts +51 -0
  341. package/src/utils/leapingComment.ts +18 -0
  342. package/src/utils/learnings.test.ts +87 -0
  343. package/src/utils/learnings.ts +138 -0
  344. package/src/utils/learningsTocRender.test.ts +116 -0
  345. package/src/utils/learningsTruncate.test.ts +39 -0
  346. package/src/utils/learningsTruncate.ts +42 -0
  347. package/src/utils/lifecycle.test.ts +195 -0
  348. package/src/utils/lifecycle.ts +198 -0
  349. package/src/utils/log.test.ts +402 -0
  350. package/src/utils/log.ts +432 -0
  351. package/src/utils/normalizeEnv.test.ts +91 -0
  352. package/src/utils/normalizeEnv.ts +106 -0
  353. package/src/utils/openCodeModels.ts +82 -0
  354. package/src/utils/overrides.test.ts +89 -0
  355. package/src/utils/overrides.ts +98 -0
  356. package/src/utils/packageManager.test.ts +321 -0
  357. package/src/utils/packageManager.ts +257 -0
  358. package/src/utils/patchWorkflowRunFields.test.ts +92 -0
  359. package/src/utils/patchWorkflowRunFields.ts +150 -0
  360. package/src/utils/payload.test.ts +497 -0
  361. package/src/utils/payload.ts +371 -0
  362. package/src/utils/postApiFetch.ts +51 -0
  363. package/src/utils/prSummary.test.ts +224 -0
  364. package/src/utils/prSummary.ts +147 -0
  365. package/src/utils/progressComment.ts +261 -0
  366. package/src/utils/providerErrors.test.ts +315 -0
  367. package/src/utils/providerErrors.ts +172 -0
  368. package/src/utils/rangeDiff.test.ts +236 -0
  369. package/src/utils/rangeDiff.ts +182 -0
  370. package/src/utils/remediationCommand.test.ts +163 -0
  371. package/src/utils/remediationCommand.ts +119 -0
  372. package/src/utils/retry.test.ts +153 -0
  373. package/src/utils/retry.ts +58 -0
  374. package/src/utils/reviewCleanup.ts +106 -0
  375. package/src/utils/run.ts +99 -0
  376. package/src/utils/runContext.ts +145 -0
  377. package/src/utils/runContextData.ts +58 -0
  378. package/src/utils/runErrorRenderer.test.ts +95 -0
  379. package/src/utils/runErrorRenderer.ts +259 -0
  380. package/src/utils/runFixture.ts +76 -0
  381. package/src/utils/runLifecycle.ts +237 -0
  382. package/src/utils/runStartupLog.ts +60 -0
  383. package/src/utils/secrets.test.ts +103 -0
  384. package/src/utils/secrets.ts +177 -0
  385. package/src/utils/setup.test.ts +509 -0
  386. package/src/utils/setup.ts +352 -0
  387. package/src/utils/shell.ts +103 -0
  388. package/src/utils/skills.test.ts +46 -0
  389. package/src/utils/skills.ts +67 -0
  390. package/src/utils/subprocess.test.ts +170 -0
  391. package/src/utils/subprocess.ts +438 -0
  392. package/src/utils/terraformMcp.test.ts +63 -0
  393. package/src/utils/terraformMcp.ts +83 -0
  394. package/src/utils/time.test.ts +105 -0
  395. package/src/utils/time.ts +59 -0
  396. package/src/utils/timer.test.ts +91 -0
  397. package/src/utils/timer.ts +72 -0
  398. package/src/utils/todoTracking.test.ts +223 -0
  399. package/src/utils/todoTracking.ts +167 -0
  400. package/src/utils/token.test.ts +239 -0
  401. package/src/utils/token.ts +186 -0
  402. package/src/utils/version.ts +10 -0
  403. package/src/utils/versioning.test.ts +34 -0
  404. package/src/utils/versioning.ts +44 -0
  405. package/src/utils/vertex.ts +85 -0
  406. package/src/utils/workflow.ts +25 -0
package/src/models.ts ADDED
@@ -0,0 +1,708 @@
1
+ /**
2
+ * model alias registry.
3
+ *
4
+ * slugs use the format `provider/model-id` (e.g. "anthropic/claude-opus").
5
+ * bump `resolve` when a new model generation ships — the alias (slug) stays stable.
6
+ */
7
+
8
+ // ── types ──────────────────────────────────────────────────────────────────────
9
+
10
+ /**
11
+ * routing discriminant for entries whose `resolve` is dynamic — looked up
12
+ * from a separate env var at run time rather than fixed in the catalog.
13
+ *
14
+ * `"bedrock"` means the actual model ID comes from `BEDROCK_MODEL_ID`
15
+ * (an AWS-canonical Bedrock model ID like `eu.anthropic.claude-opus-4-7`
16
+ * or `amazon.nova-pro-v1:0`). `"vertex"` means the actual model ID comes
17
+ * from `VERTEX_MODEL_ID` (a Vertex AI model ID like
18
+ * `claude-opus-4-1@20250805` or `gemini-2.5-pro`). enterprise hosted-model
19
+ * customers self-select for version control — silent alias bumps would break
20
+ * compliance review, model-access enrollment, and provisioned-throughput
21
+ * contracts. so the single `bedrock/byok` and `vertex/byok` entries are
22
+ * routing slugs, not model aliases: the harness reads the backend-specific
23
+ * env var and routes to claude-code for Anthropic IDs or opencode for
24
+ * everything else.
25
+ */
26
+ export type ModelRouting = "bedrock" | "vertex";
27
+
28
+ export interface ModelAlias {
29
+ /** stable alias stored in DB, e.g. "anthropic/claude-opus" */
30
+ slug: string;
31
+ /** provider key (matches providers keys) */
32
+ provider: string;
33
+ /** human-readable name shown in dropdowns */
34
+ displayName: string;
35
+ /** concrete models.dev specifier, e.g. "anthropic/claude-opus-4-6". sentinel for routing entries — never passed to a CLI directly. */
36
+ resolve: string;
37
+ /** full models.dev specifier for the OpenRouter equivalent (undefined for free models and routing entries) */
38
+ openRouterResolve: string | undefined;
39
+ /** top-tier pick for this provider — preferred during auto-select */
40
+ preferred: boolean;
41
+ /** whether this alias is free and requires no API key */
42
+ isFree: boolean;
43
+ /** slug of a replacement model — presence implies this model is deprecated */
44
+ fallback: string | undefined;
45
+ /** dynamic-resolution discriminant — see ModelRouting docs */
46
+ routing: ModelRouting | undefined;
47
+ /** alias key (within same provider) of the cheaper sibling reviewfrog should
48
+ * use as its lens-fanout subagent. e.g. claude-opus → "claude-sonnet". */
49
+ subagentModel: string | undefined;
50
+ /** hide from selectable lists (UI dropdowns, CLI pickers). does NOT affect
51
+ * resolution — for that use `fallback`. used for internal-only tier targets
52
+ * (e.g. gpt-5.4 as a subagent target without exposing it to users). */
53
+ hidden: boolean;
54
+ }
55
+
56
+ interface ModelDef {
57
+ displayName: string;
58
+ /** concrete models.dev specifier, e.g. "anthropic/claude-opus-4-6" */
59
+ resolve: string;
60
+ /** full models.dev specifier for the OpenRouter equivalent, e.g. "openrouter/anthropic/claude-opus-4.6" */
61
+ openRouterResolve?: string;
62
+ preferred?: boolean;
63
+ envVars?: readonly string[];
64
+ isFree?: boolean;
65
+ /** slug of a replacement model — presence implies this model is deprecated */
66
+ fallback?: string;
67
+ /** dynamic-resolution discriminant — see ModelRouting docs */
68
+ routing?: ModelRouting;
69
+ /** alias key (within same provider) of the cheaper sibling reviewfrog should
70
+ * use as its lens-fanout subagent (e.g. claude-opus → "claude-sonnet"). */
71
+ subagentModel?: string;
72
+ /** hide from selectable lists. does NOT affect resolution; for that use `fallback`. */
73
+ hidden?: boolean;
74
+ }
75
+
76
+ export interface ProviderConfig {
77
+ displayName: string;
78
+ envVars: readonly string[];
79
+ /** credentials authored only via `terramend auth <provider>` — never
80
+ * user-facing in `init`, never documented as a manual GHA secret. counted
81
+ * for hasAnyKey / log-redaction purposes but excluded from any prompt /
82
+ * paste flow. CLI-managed magic. see wiki/codex-auth.md. */
83
+ managedCredentials?: readonly string[];
84
+ models: Record<string, ModelDef>;
85
+ }
86
+
87
+ // ── provider + model definitions ────────────────────────────────────────────────
88
+
89
+ function provider(config: ProviderConfig): ProviderConfig {
90
+ return config;
91
+ }
92
+
93
+ export const providers = {
94
+ anthropic: provider({
95
+ displayName: "Anthropic",
96
+ envVars: ["ANTHROPIC_API_KEY", "CLAUDE_CODE_OAUTH_TOKEN"],
97
+ models: {
98
+ // OpenRouter serves claude-fable-5, but models.dev's OpenRouter mirror
99
+ // hasn't indexed it yet (shipped 2026-06-09), so the catalog drift gate
100
+ // can't validate an openRouterResolve. omit it until the mirror catches
101
+ // up; direct BYOK / Claude Code resolves anthropic/claude-fable-5 fine.
102
+ "claude-fable": {
103
+ displayName: "Claude Fable",
104
+ resolve: "anthropic/claude-fable-5",
105
+ preferred: true,
106
+ subagentModel: "claude-sonnet",
107
+ },
108
+ "claude-opus": {
109
+ displayName: "Claude Opus",
110
+ resolve: "anthropic/claude-opus-4-8",
111
+ openRouterResolve: "openrouter/anthropic/claude-opus-4.8",
112
+ subagentModel: "claude-sonnet",
113
+ },
114
+ "claude-sonnet": {
115
+ displayName: "Claude Sonnet",
116
+ resolve: "anthropic/claude-sonnet-4-6",
117
+ openRouterResolve: "openrouter/anthropic/claude-sonnet-4.6",
118
+ },
119
+ "claude-haiku": {
120
+ displayName: "Claude Haiku",
121
+ resolve: "anthropic/claude-haiku-4-5",
122
+ openRouterResolve: "openrouter/anthropic/claude-haiku-4.5",
123
+ },
124
+ },
125
+ }),
126
+ openai: provider({
127
+ displayName: "OpenAI",
128
+ envVars: ["OPENAI_API_KEY"],
129
+ managedCredentials: ["CODEX_AUTH_JSON"],
130
+ models: {
131
+ gpt: {
132
+ displayName: "GPT",
133
+ resolve: "openai/gpt-5.5",
134
+ openRouterResolve: "openrouter/openai/gpt-5.5",
135
+ preferred: true,
136
+ subagentModel: "gpt-5.4",
137
+ },
138
+ "gpt-pro": {
139
+ displayName: "GPT Pro",
140
+ resolve: "openai/gpt-5.5-pro",
141
+ openRouterResolve: "openrouter/openai/gpt-5.5-pro",
142
+ subagentModel: "gpt",
143
+ },
144
+ // hidden subagent target — `gpt` lenses run against this. surfacing
145
+ // it in the picker would just confuse users (it's the prior-flagship,
146
+ // and they already have `gpt` and `gpt-mini` to choose from).
147
+ "gpt-5.4": {
148
+ displayName: "GPT 5.4",
149
+ resolve: "openai/gpt-5.4",
150
+ openRouterResolve: "openrouter/openai/gpt-5.4",
151
+ hidden: true,
152
+ },
153
+ "gpt-mini": {
154
+ displayName: "GPT Mini",
155
+ resolve: "openai/gpt-5.4-mini",
156
+ openRouterResolve: "openrouter/openai/gpt-5.4-mini",
157
+ },
158
+ // legacy aliases — openai unified the codex line into the main GPT family
159
+ // and is shutting down every "-codex" snapshot on 2026-07-23. transparently
160
+ // upgrade existing users via the fallback chain. UI display sites resolve
161
+ // to the terminal alias's label (so dropdown trigger + PR footers show
162
+ // "GPT" / "GPT Mini", not the historical name).
163
+ "gpt-codex": {
164
+ displayName: "GPT Codex",
165
+ resolve: "openai/gpt-5.3-codex",
166
+ openRouterResolve: "openrouter/openai/gpt-5.3-codex",
167
+ fallback: "openai/gpt",
168
+ },
169
+ "gpt-codex-mini": {
170
+ displayName: "GPT Codex Mini",
171
+ resolve: "openai/gpt-5.1-codex-mini",
172
+ openRouterResolve: "openrouter/openai/gpt-5.1-codex-mini",
173
+ fallback: "openai/gpt-mini",
174
+ },
175
+ o3: {
176
+ displayName: "O3",
177
+ resolve: "openai/o3",
178
+ openRouterResolve: "openrouter/openai/o3",
179
+ },
180
+ },
181
+ }),
182
+ google: provider({
183
+ displayName: "Google",
184
+ envVars: ["GEMINI_API_KEY", "GOOGLE_GENERATIVE_AI_API_KEY"],
185
+ models: {
186
+ "gemini-pro": {
187
+ displayName: "Gemini Pro",
188
+ resolve: "google/gemini-3.1-pro-preview",
189
+ openRouterResolve: "openrouter/google/gemini-3.1-pro-preview",
190
+ preferred: true,
191
+ // Inherit (subagents stay on Pro). Google has no in-between tier;
192
+ // dropping to Flash for review work was a meaningful capability cliff
193
+ // (Flash missed the catastrophic camelCase/snake_case mismatch in
194
+ // the v4 e2e test). Pro is cost-effective enough to use for both
195
+ // orchestrator and lenses.
196
+ },
197
+ "gemini-flash": {
198
+ displayName: "Gemini Flash",
199
+ resolve: "google/gemini-3.5-flash",
200
+ openRouterResolve: "openrouter/google/gemini-3.5-flash",
201
+ },
202
+ },
203
+ }),
204
+ xai: provider({
205
+ displayName: "xAI",
206
+ envVars: ["XAI_API_KEY"],
207
+ models: {
208
+ grok: {
209
+ displayName: "Grok",
210
+ resolve: "xai/grok-4.3",
211
+ openRouterResolve: "openrouter/x-ai/grok-4.3",
212
+ preferred: true,
213
+ },
214
+ // legacy aliases — xAI retired the entire fast/code-fast line on
215
+ // 2026-05-15 (https://docs.x.ai/developers/migration/may-15-deprecation)
216
+ // and now redirects every deprecated text-model slug to grok-4.3 at
217
+ // standard pricing. fall back to the live `xai/grok` so the alias
218
+ // chain resolves to grok-4.3 for both direct-key and OpenRouter users.
219
+ "grok-fast": {
220
+ displayName: "Grok Fast",
221
+ resolve: "xai/grok-4-1-fast",
222
+ openRouterResolve: "openrouter/x-ai/grok-4.3",
223
+ fallback: "xai/grok",
224
+ },
225
+ "grok-code-fast": {
226
+ displayName: "Grok Code Fast",
227
+ resolve: "xai/grok-code-fast-1",
228
+ openRouterResolve: "openrouter/x-ai/grok-4.3",
229
+ fallback: "xai/grok",
230
+ },
231
+ },
232
+ }),
233
+ deepseek: provider({
234
+ displayName: "DeepSeek",
235
+ envVars: ["DEEPSEEK_API_KEY"],
236
+ models: {
237
+ "deepseek-pro": {
238
+ displayName: "DeepSeek Pro",
239
+ resolve: "deepseek/deepseek-v4-pro",
240
+ openRouterResolve: "openrouter/deepseek/deepseek-v4-pro",
241
+ preferred: true,
242
+ },
243
+ "deepseek-flash": {
244
+ displayName: "DeepSeek Flash",
245
+ resolve: "deepseek/deepseek-v4-flash",
246
+ openRouterResolve: "openrouter/deepseek/deepseek-v4-flash",
247
+ },
248
+ // legacy aliases — deepseek retires these on 2026-07-24; transparently
249
+ // upgrade existing users to the v4 family via the fallback chain.
250
+ "deepseek-reasoner": {
251
+ displayName: "DeepSeek Reasoner",
252
+ resolve: "deepseek/deepseek-reasoner",
253
+ openRouterResolve: "openrouter/deepseek/deepseek-v3.2",
254
+ fallback: "deepseek/deepseek-pro",
255
+ },
256
+ "deepseek-chat": {
257
+ displayName: "DeepSeek Chat",
258
+ resolve: "deepseek/deepseek-chat",
259
+ openRouterResolve: "openrouter/deepseek/deepseek-v3.2",
260
+ fallback: "deepseek/deepseek-flash",
261
+ },
262
+ },
263
+ }),
264
+ moonshotai: provider({
265
+ displayName: "Moonshot AI",
266
+ envVars: ["MOONSHOT_API_KEY"],
267
+ models: {
268
+ "kimi-k2": {
269
+ displayName: "Kimi K2",
270
+ resolve: "moonshotai/kimi-k2.6",
271
+ openRouterResolve: "openrouter/moonshotai/kimi-k2.6",
272
+ preferred: true,
273
+ },
274
+ },
275
+ }),
276
+ opencode: provider({
277
+ displayName: "OpenCode",
278
+ envVars: ["OPENCODE_API_KEY"],
279
+ models: {
280
+ "big-pickle": {
281
+ displayName: "Big Pickle",
282
+ resolve: "opencode/big-pickle",
283
+ preferred: true,
284
+ envVars: [],
285
+ isFree: true,
286
+ },
287
+ "claude-opus": {
288
+ displayName: "Claude Opus",
289
+ resolve: "opencode/claude-opus-4-8",
290
+ openRouterResolve: "openrouter/anthropic/claude-opus-4.8",
291
+ subagentModel: "claude-sonnet",
292
+ },
293
+ "claude-sonnet": {
294
+ displayName: "Claude Sonnet",
295
+ resolve: "opencode/claude-sonnet-4-6",
296
+ openRouterResolve: "openrouter/anthropic/claude-sonnet-4.6",
297
+ },
298
+ "claude-haiku": {
299
+ displayName: "Claude Haiku",
300
+ resolve: "opencode/claude-haiku-4-5",
301
+ openRouterResolve: "openrouter/anthropic/claude-haiku-4.5",
302
+ },
303
+ gpt: {
304
+ displayName: "GPT",
305
+ resolve: "opencode/gpt-5.5",
306
+ openRouterResolve: "openrouter/openai/gpt-5.5",
307
+ subagentModel: "gpt-5.4",
308
+ },
309
+ "gpt-pro": {
310
+ displayName: "GPT Pro",
311
+ resolve: "opencode/gpt-5.5-pro",
312
+ openRouterResolve: "openrouter/openai/gpt-5.5-pro",
313
+ subagentModel: "gpt",
314
+ },
315
+ // hidden subagent target — see openai provider above for context.
316
+ "gpt-5.4": {
317
+ displayName: "GPT 5.4",
318
+ resolve: "opencode/gpt-5.4",
319
+ openRouterResolve: "openrouter/openai/gpt-5.4",
320
+ hidden: true,
321
+ },
322
+ "gpt-mini": {
323
+ displayName: "GPT Mini",
324
+ resolve: "opencode/gpt-5.4-mini",
325
+ openRouterResolve: "openrouter/openai/gpt-5.4-mini",
326
+ },
327
+ // legacy aliases — see openai provider above for context.
328
+ "gpt-codex": {
329
+ displayName: "GPT Codex",
330
+ resolve: "opencode/gpt-5.3-codex",
331
+ openRouterResolve: "openrouter/openai/gpt-5.3-codex",
332
+ fallback: "opencode/gpt",
333
+ },
334
+ "gpt-codex-mini": {
335
+ displayName: "GPT Codex Mini",
336
+ resolve: "opencode/gpt-5.1-codex-mini",
337
+ openRouterResolve: "openrouter/openai/gpt-5.1-codex-mini",
338
+ fallback: "opencode/gpt-mini",
339
+ },
340
+ "gemini-pro": {
341
+ displayName: "Gemini Pro",
342
+ resolve: "opencode/gemini-3.1-pro",
343
+ openRouterResolve: "openrouter/google/gemini-3.1-pro-preview",
344
+ // Inherit — see google/gemini-pro for rationale.
345
+ },
346
+ "gemini-flash": {
347
+ displayName: "Gemini Flash",
348
+ resolve: "opencode/gemini-3.5-flash",
349
+ openRouterResolve: "openrouter/google/gemini-3.5-flash",
350
+ },
351
+ "kimi-k2": {
352
+ displayName: "Kimi K2",
353
+ resolve: "opencode/kimi-k2.6",
354
+ openRouterResolve: "openrouter/moonshotai/kimi-k2.6",
355
+ },
356
+ "minimax-m2.5": {
357
+ displayName: "MiniMax M2.5",
358
+ resolve: "opencode/minimax-m2.5",
359
+ openRouterResolve: "openrouter/minimax/minimax-m2.5",
360
+ },
361
+ "gpt-5-nano": {
362
+ displayName: "GPT Nano",
363
+ resolve: "opencode/gpt-5-nano",
364
+ openRouterResolve: "openrouter/openai/gpt-5-nano",
365
+ },
366
+ "mimo-v2-pro-free": {
367
+ displayName: "MiMo V2 Pro",
368
+ resolve: "opencode/mimo-v2-pro-free",
369
+ envVars: [],
370
+ isFree: true,
371
+ fallback: "opencode/big-pickle",
372
+ },
373
+ "minimax-m2.5-free": {
374
+ displayName: "MiniMax M2.5",
375
+ resolve: "opencode/minimax-m2.5-free",
376
+ envVars: [],
377
+ isFree: true,
378
+ fallback: "opencode/big-pickle",
379
+ hidden: true,
380
+ },
381
+ },
382
+ }),
383
+ bedrock: provider({
384
+ displayName: "Amazon Bedrock",
385
+ envVars: ["AWS_BEARER_TOKEN_BEDROCK", "AWS_REGION", "BEDROCK_MODEL_ID"],
386
+ models: {
387
+ // single routing entry — the actual Bedrock model ID is read from
388
+ // BEDROCK_MODEL_ID at run time. see ModelRouting docs for why we
389
+ // don't catalog individual Bedrock models.
390
+ byok: {
391
+ displayName: "Amazon Bedrock",
392
+ resolve: "bedrock",
393
+ routing: "bedrock",
394
+ },
395
+ },
396
+ }),
397
+ vertex: provider({
398
+ displayName: "Google Vertex AI",
399
+ envVars: [
400
+ "VERTEX_SERVICE_ACCOUNT_JSON",
401
+ "GOOGLE_CLOUD_PROJECT",
402
+ "VERTEX_LOCATION",
403
+ "VERTEX_MODEL_ID",
404
+ ],
405
+ models: {
406
+ // single routing entry — the actual Vertex AI model ID is read from
407
+ // VERTEX_MODEL_ID at run time. see ModelRouting docs for why we don't
408
+ // catalog individual Vertex models.
409
+ byok: {
410
+ displayName: "Google Vertex AI",
411
+ resolve: "vertex",
412
+ routing: "vertex",
413
+ },
414
+ },
415
+ }),
416
+ openrouter: provider({
417
+ displayName: "OpenRouter",
418
+ envVars: ["OPENROUTER_API_KEY"],
419
+ models: {
420
+ "claude-opus": {
421
+ displayName: "Claude Opus",
422
+ resolve: "openrouter/anthropic/claude-opus-4.8",
423
+ openRouterResolve: "openrouter/anthropic/claude-opus-4.8",
424
+ preferred: true,
425
+ subagentModel: "claude-sonnet",
426
+ },
427
+ "claude-sonnet": {
428
+ displayName: "Claude Sonnet",
429
+ resolve: "openrouter/anthropic/claude-sonnet-4.6",
430
+ openRouterResolve: "openrouter/anthropic/claude-sonnet-4.6",
431
+ },
432
+ "claude-haiku": {
433
+ displayName: "Claude Haiku",
434
+ resolve: "openrouter/anthropic/claude-haiku-4.5",
435
+ openRouterResolve: "openrouter/anthropic/claude-haiku-4.5",
436
+ },
437
+ gpt: {
438
+ displayName: "GPT",
439
+ resolve: "openrouter/openai/gpt-5.5",
440
+ openRouterResolve: "openrouter/openai/gpt-5.5",
441
+ subagentModel: "gpt-5.4",
442
+ },
443
+ "gpt-pro": {
444
+ displayName: "GPT Pro",
445
+ resolve: "openrouter/openai/gpt-5.5-pro",
446
+ openRouterResolve: "openrouter/openai/gpt-5.5-pro",
447
+ subagentModel: "gpt",
448
+ },
449
+ // hidden subagent target — see openai provider above for context.
450
+ "gpt-5.4": {
451
+ displayName: "GPT 5.4",
452
+ resolve: "openrouter/openai/gpt-5.4",
453
+ openRouterResolve: "openrouter/openai/gpt-5.4",
454
+ hidden: true,
455
+ },
456
+ "gpt-mini": {
457
+ displayName: "GPT Mini",
458
+ resolve: "openrouter/openai/gpt-5.4-mini",
459
+ openRouterResolve: "openrouter/openai/gpt-5.4-mini",
460
+ },
461
+ // legacy aliases — see openai provider for context.
462
+ "gpt-codex": {
463
+ displayName: "GPT Codex",
464
+ resolve: "openrouter/openai/gpt-5.3-codex",
465
+ openRouterResolve: "openrouter/openai/gpt-5.3-codex",
466
+ fallback: "openrouter/gpt",
467
+ },
468
+ "gpt-codex-mini": {
469
+ displayName: "GPT Codex Mini",
470
+ resolve: "openrouter/openai/gpt-5.1-codex-mini",
471
+ openRouterResolve: "openrouter/openai/gpt-5.1-codex-mini",
472
+ fallback: "openrouter/gpt-mini",
473
+ },
474
+ "o4-mini": {
475
+ displayName: "O4 Mini",
476
+ resolve: "openrouter/openai/o4-mini",
477
+ openRouterResolve: "openrouter/openai/o4-mini",
478
+ },
479
+ "gemini-pro": {
480
+ displayName: "Gemini Pro",
481
+ resolve: "openrouter/google/gemini-3.1-pro-preview",
482
+ openRouterResolve: "openrouter/google/gemini-3.1-pro-preview",
483
+ // Inherit — see google/gemini-pro for rationale.
484
+ },
485
+ "gemini-flash": {
486
+ displayName: "Gemini Flash",
487
+ resolve: "openrouter/google/gemini-3.5-flash",
488
+ openRouterResolve: "openrouter/google/gemini-3.5-flash",
489
+ },
490
+ grok: {
491
+ displayName: "Grok",
492
+ resolve: "openrouter/x-ai/grok-4.3",
493
+ openRouterResolve: "openrouter/x-ai/grok-4.3",
494
+ },
495
+ "deepseek-pro": {
496
+ displayName: "DeepSeek Pro",
497
+ resolve: "openrouter/deepseek/deepseek-v4-pro",
498
+ openRouterResolve: "openrouter/deepseek/deepseek-v4-pro",
499
+ },
500
+ "deepseek-flash": {
501
+ displayName: "DeepSeek Flash",
502
+ resolve: "openrouter/deepseek/deepseek-v4-flash",
503
+ openRouterResolve: "openrouter/deepseek/deepseek-v4-flash",
504
+ },
505
+ // legacy alias — deepseek retires this on 2026-07-24; transparently
506
+ // upgrade existing users to the v4 family via the fallback chain.
507
+ "deepseek-chat": {
508
+ displayName: "DeepSeek Chat",
509
+ resolve: "openrouter/deepseek/deepseek-v3.2",
510
+ openRouterResolve: "openrouter/deepseek/deepseek-v3.2",
511
+ fallback: "openrouter/deepseek-flash",
512
+ },
513
+ "kimi-k2": {
514
+ displayName: "Kimi K2",
515
+ resolve: "openrouter/moonshotai/kimi-k2.6",
516
+ openRouterResolve: "openrouter/moonshotai/kimi-k2.6",
517
+ },
518
+ "minimax-m2.5": {
519
+ displayName: "MiniMax M2.5",
520
+ resolve: "openrouter/minimax/minimax-m2.5",
521
+ openRouterResolve: "openrouter/minimax/minimax-m2.5",
522
+ },
523
+ },
524
+ }),
525
+ } satisfies Record<string, ProviderConfig>;
526
+
527
+ export type ModelProvider = keyof typeof providers;
528
+
529
+ // ── slug parsing ───────────────────────────────────────────────────────────────
530
+
531
+ export function parseModel(slug: string): { provider: string; model: string } {
532
+ const slashIdx = slug.indexOf("/");
533
+ if (slashIdx === -1) {
534
+ throw new Error(`invalid model slug "${slug}" — expected "provider/model"`);
535
+ }
536
+ return { provider: slug.slice(0, slashIdx), model: slug.slice(slashIdx + 1) };
537
+ }
538
+
539
+ export function getModelProvider(slug: string): string {
540
+ return parseModel(slug).provider;
541
+ }
542
+
543
+ export function getProviderDisplayName(slug: string): string | undefined {
544
+ const parsed = parseModel(slug);
545
+ return (providers as Record<string, ProviderConfig>)[parsed.provider]?.displayName;
546
+ }
547
+
548
+ export function getModelEnvVars(slug: string): string[] {
549
+ const parsed = parseModel(slug);
550
+ const providerConfig = (providers as Record<string, ProviderConfig>)[parsed.provider];
551
+ if (!providerConfig) {
552
+ return [];
553
+ }
554
+
555
+ const modelConfig = providerConfig.models[parsed.model];
556
+ if (modelConfig?.envVars) {
557
+ return modelConfig.envVars.slice();
558
+ }
559
+
560
+ return providerConfig.envVars.slice();
561
+ }
562
+
563
+ /** managed credentials are authored only via `terramend auth <provider>` — they
564
+ * count as "configured" for hasAnyKey-style UI checks but are never offered as
565
+ * a manual-paste option in `init` or the AgentSettings env-var button row.
566
+ * see `provider.managedCredentials` and wiki/codex-auth.md. */
567
+ export function getModelManagedCredentials(slug: string): string[] {
568
+ const parsed = parseModel(slug);
569
+ const providerConfig = (providers as Record<string, ProviderConfig>)[parsed.provider];
570
+ return providerConfig?.managedCredentials?.slice() ?? [];
571
+ }
572
+
573
+ // ── derived flat list ──────────────────────────────────────────────────────────
574
+
575
+ export const modelAliases: ModelAlias[] = Object.entries(providers).flatMap(
576
+ ([providerKey, config]) =>
577
+ Object.entries(config.models).map(([modelId, def]) => ({
578
+ slug: `${providerKey}/${modelId}`,
579
+ provider: providerKey,
580
+ displayName: def.displayName,
581
+ resolve: def.resolve,
582
+ openRouterResolve: def.openRouterResolve,
583
+ preferred: def.preferred ?? false,
584
+ isFree: def.isFree ?? false,
585
+ fallback: def.fallback,
586
+ routing: def.routing,
587
+ // subagentModel is stored as an alias key local to the provider; expand
588
+ // here to a fully-qualified slug so callers can look up the target alias
589
+ // directly without re-deriving the provider.
590
+ subagentModel: def.subagentModel ? `${providerKey}/${def.subagentModel}` : undefined,
591
+ hidden: def.hidden ?? false,
592
+ })),
593
+ );
594
+
595
+ /** OpenRouter target when Router or OSS funding is active and `repo.model` is null. */
596
+ const defaultProxyAlias = modelAliases.find((a) => a.slug === "deepseek/deepseek-pro");
597
+ if (!defaultProxyAlias?.openRouterResolve) {
598
+ throw new Error("DEFAULT_PROXY_MODEL: deepseek/deepseek-pro missing openRouterResolve");
599
+ }
600
+ export const DEFAULT_PROXY_MODEL = defaultProxyAlias.openRouterResolve;
601
+ const defaultProxyDisplayName = defaultProxyAlias.displayName;
602
+
603
+ /** short label for the model auto-select picks today (console hint copy). */
604
+ export function getAutoSelectHintModel(): string {
605
+ return defaultProxyDisplayName;
606
+ }
607
+
608
+ // ── resolution ─────────────────────────────────────────────────────────────────
609
+
610
+ /** resolve a model slug to its concrete models.dev specifier (e.g. "anthropic/claude-opus-4-6") */
611
+ export function resolveModelSlug(slug: string): string | undefined {
612
+ return modelAliases.find((a) => a.slug === slug)?.resolve;
613
+ }
614
+
615
+ const MAX_FALLBACK_DEPTH = 10;
616
+
617
+ /**
618
+ * walk the fallback chain to the terminal (non-deprecated) alias.
619
+ * returns undefined if the chain is broken, exhausted, or cyclic.
620
+ *
621
+ * use this in UI display sites (dropdown trigger labels, PR-comment footers,
622
+ * etc.) so a deprecated stored slug renders as the model the user actually
623
+ * runs against — not the historical name. selectable lists should still hide
624
+ * deprecated and internal-only aliases by filtering on `!a.fallback && !a.hidden`.
625
+ */
626
+ export function resolveDisplayAlias(slug: string): ModelAlias | undefined {
627
+ let current = slug;
628
+ const visited = new Set<string>();
629
+ for (let i = 0; i < MAX_FALLBACK_DEPTH; i++) {
630
+ if (visited.has(current)) return undefined;
631
+ visited.add(current);
632
+ const alias = modelAliases.find((a) => a.slug === current);
633
+ if (!alias) return undefined;
634
+ if (!alias.fallback) return alias;
635
+ current = alias.fallback;
636
+ }
637
+ return undefined;
638
+ }
639
+
640
+ /**
641
+ * resolve a model slug to the CLI-ready model string, following the fallback
642
+ * chain when a model is deprecated. returns the first non-deprecated resolve
643
+ * target, or undefined if the chain is exhausted or broken.
644
+ */
645
+ export function resolveCliModel(slug: string): string | undefined {
646
+ return resolveDisplayAlias(slug)?.resolve;
647
+ }
648
+
649
+ /**
650
+ * resolve a model slug to the OpenRouter-ready model string, following the
651
+ * fallback chain when a model is deprecated. returns undefined if the chain
652
+ * is exhausted/broken or the terminal alias has no openrouter equivalent
653
+ * (e.g. free opencode models).
654
+ */
655
+ export function resolveOpenRouterModel(slug: string): string | undefined {
656
+ return resolveDisplayAlias(slug)?.openRouterResolve;
657
+ }
658
+
659
+ // ── bedrock routing ────────────────────────────────────────────────────────────
660
+
661
+ /** env var that supplies the Bedrock model ID for the `bedrock/byok` slug. */
662
+ export const BEDROCK_MODEL_ID_ENV = "BEDROCK_MODEL_ID";
663
+
664
+ /** env var that supplies the Vertex AI model ID for the `vertex/byok` slug. */
665
+ export const VERTEX_MODEL_ID_ENV = "VERTEX_MODEL_ID";
666
+
667
+ /**
668
+ * the Bedrock model ID passed to claude-code or opencode is whatever the
669
+ * user set in `BEDROCK_MODEL_ID` — Terramend never resolves or upgrades it.
670
+ * we route by checking whether the ID names an Anthropic model: claude-code
671
+ * handles Anthropic-on-Bedrock natively (with `CLAUDE_CODE_USE_BEDROCK=1`),
672
+ * everything else goes through opencode's `amazon-bedrock` provider.
673
+ *
674
+ * AWS Bedrock IDs come in two shapes:
675
+ * - dotted foundation IDs: `eu.anthropic.claude-opus-4-7`,
676
+ * `anthropic.claude-haiku-4-5-20251001-v1:0`, `amazon.nova-pro-v1:0`,
677
+ * `meta.llama4-scout-17b-instruct-v1:0`. AWS-published, lowercase, the
678
+ * foundation provider always appears as a discrete dot-segment.
679
+ * - inference-profile ARNs: `arn:aws:bedrock:eu-west-2:<acct>:application-inference-profile/<user-name>`.
680
+ * `<user-name>` is operator-chosen, so a naive substring check is fragile
681
+ * in both directions (Anthropic profile named without "anthropic" → routes
682
+ * to opencode and misses CLAUDE_CODE_USE_BEDROCK; non-Anthropic profile
683
+ * whose name happens to contain "anthropic" → routes to claude-code).
684
+ *
685
+ * we anchor on a discrete dot-segment match (case-insensitive). this catches
686
+ * every published foundation ID and is conservative for ARN-form IDs: ARN
687
+ * names that don't include "anthropic" as their own dot-segment route to
688
+ * opencode by default. operators using ARN-form IDs whose backing model is
689
+ * Anthropic should set `TERRAMEND_AGENT=claude` to force the right route, or
690
+ * include the foundation segment in the profile name.
691
+ */
692
+ export function isBedrockAnthropicId(bedrockModelId: string): boolean {
693
+ // split on `.`, `/`, and `:` so the check works for both dotted foundation
694
+ // IDs (anthropic.* / eu.anthropic.* / apac.anthropic.* — geo-agnostic, so EU
695
+ // inference profiles route correctly) and ARN-form IDs (where the relevant
696
+ // foundation segment sits between `/` and `.` inside the resource name).
697
+ return bedrockModelId.toLowerCase().split(/[./:]/).includes("anthropic");
698
+ }
699
+
700
+ /**
701
+ * Vertex Anthropic model IDs start with the Claude family name, e.g.
702
+ * `claude-opus-4-1@20250805`. partner-model resource paths can contain the
703
+ * substring "anthropic" elsewhere, so the Bedrock segment check does not
704
+ * transfer — anchor on the model ID prefix instead.
705
+ */
706
+ export function isVertexAnthropicId(vertexModelId: string): boolean {
707
+ return /^claude-/i.test(vertexModelId.trim());
708
+ }