@pensar/apex 2.6.1 → 2.7.0-canary.1572d9a4

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 (328) hide show
  1. package/build/{agent-ayreaj6z.js → agent-3g1b578v.js} +6 -5
  2. package/build/{agent-yby11zaq.js → agent-p8cezgbb.js} +7 -6
  3. package/build/{agent-runs-g37vkrjr.js → agent-runs-f86emmgg.js} +23 -20
  4. package/build/agent-vaq31je6.js +14 -0
  5. package/build/agent-vvdqga09.js +14 -0
  6. package/build/{apps-xn6km9yn.js → apps-3stwnmen.js} +16 -13
  7. package/build/{auth-d105wc64.js → auth-7md9kwda.js} +16 -13
  8. package/build/blackboxAgent-ct8bejef.js +14 -0
  9. package/build/{blackboxPentest-ps78v3w7.js → blackboxPentest-w2nr4md3.js} +21 -15
  10. package/build/{cli-c05y640n.js → cli-1j28cbhy.js} +1 -1
  11. package/build/{cli-d37a42dc.js → cli-2jz2jz06.js} +206 -90
  12. package/build/{cli-az3xq70v.js → cli-4a8dsz2k.js} +4 -4
  13. package/build/{cli-qgz1q9g5.js → cli-58781vcf.js} +1192 -1473
  14. package/build/{cli-gx71jcke.js → cli-7067zzeb.js} +1 -1
  15. package/build/{cli-3sp6nd8d.js → cli-81smmd5w.js} +5 -5
  16. package/build/{cli-k2nwxta0.js → cli-8b25dhky.js} +2 -2
  17. package/build/{cli-fy6vh2a7.js → cli-8sw5zpqz.js} +1 -1
  18. package/build/{cli-709rvekb.js → cli-cg3ypx5n.js} +14 -7
  19. package/build/{cli-dpsw2fe6.js → cli-e9v6zg6h.js} +40941 -40172
  20. package/build/cli-fjxmphg1.js +36 -0
  21. package/build/{cli-7eea4e2y.js → cli-gzb6qrzh.js} +31 -60
  22. package/build/{cli-65g6rcat.js → cli-khnf5xrf.js} +2 -1
  23. package/build/{cli-1ve18npr.js → cli-mdzt2e8j.js} +4 -2
  24. package/build/{cli-n2en7j5b.js → cli-n8mmw4c4.js} +1 -1
  25. package/build/cli-pndb7mh9.js +1193 -0
  26. package/build/cli-px6ehzqx.js +54 -0
  27. package/build/{cli-xtc2t6me.js → cli-qs4msn6x.js} +111 -47
  28. package/build/{cli-kb21qhvm.js → cli-rvybdhgn.js} +4 -3
  29. package/build/{cli-ae8jqxqr.js → cli-v739h7hb.js} +2 -2
  30. package/build/{cli-ngq9dr8n.js → cli-wm8ech0x.js} +3 -3
  31. package/build/cli.js +170 -51
  32. package/build/{config-zpqkd5cz.js → config-e5v1bkh1.js} +1 -1
  33. package/build/{fastStrike-mcjdvwez.js → fastStrike-efp0rjs3.js} +74 -17
  34. package/build/{fixes-kytx5dmj.js → fixes-926cw1ej.js} +16 -13
  35. package/build/{index-n65z6v7b.js → index-3t41p258.js} +7 -6
  36. package/build/{index-qv4cc65p.js → index-bv09af6g.js} +19 -15
  37. package/build/{index-je6hcsqr.js → index-bv6dtwse.js} +1 -1
  38. package/build/{index-4vqdgb42.js → index-jt52ffgn.js} +1657 -1400
  39. package/build/{index-ns4m4s01.js → index-np405b9e.js} +11 -7
  40. package/build/{index-r9yjkfrv.js → index-qys178kp.js} +5 -4
  41. package/build/{index-wqetp6gq.js → index-tpyzq2wq.js} +42 -11
  42. package/build/{index-fv64eb32.js → index-vrcdyebh.js} +2 -2
  43. package/build/{index-dr3tg1w3.js → index-z1g8vbtm.js} +1 -1
  44. package/build/{issues-eb3q9d3a.js → issues-sncyqh14.js} +16 -13
  45. package/build/{knowledge-fqsvtx0f.js → knowledge-esgvy5ws.js} +16 -13
  46. package/build/lance-sacfgs1z.js +232 -0
  47. package/build/lib/{chunk-9yksqgzr.js → chunk-4aevegnc.js} +77 -45
  48. package/build/lib/{chunk-9tt2021m.js → chunk-4kf4a15q.js} +13 -13
  49. package/build/lib/{chunk-fy3bbbqy.js → chunk-5avnedwm.js} +7 -1
  50. package/build/lib/{chunk-bb0ekhkw.js → chunk-5nz5tk3p.js} +76 -22
  51. package/build/lib/{chunk-4hwzqthr.js → chunk-7s8e5wrd.js} +3 -2
  52. package/build/lib/{chunk-yrnrbjes.js → chunk-85kpxwzs.js} +4 -2
  53. package/build/lib/{chunk-mg03jdmt.js → chunk-9jkzk9nc.js} +1 -1
  54. package/build/lib/{chunk-r4j8ybn9.js → chunk-9wwts6r6.js} +824 -77
  55. package/build/lib/{chunk-nhfax4gb.js → chunk-bxznzxdy.js} +5 -1
  56. package/build/lib/{chunk-r2dkp4tb.js → chunk-cqxm95md.js} +1 -1
  57. package/build/lib/{chunk-w66ce82a.js → chunk-e3agqspc.js} +12 -4
  58. package/build/lib/{chunk-ba41jh41.js → chunk-eh5yw385.js} +1137 -333
  59. package/build/lib/{chunk-1azdancq.js → chunk-ewc9r84m.js} +5 -1
  60. package/build/lib/{chunk-2njv7wz3.js → chunk-hbyxyxdc.js} +4 -0
  61. package/build/lib/{chunk-e8yy2k73.js → chunk-jhhwts6w.js} +28 -2
  62. package/build/lib/{chunk-1jvwqnem.js → chunk-kbxbd1kn.js} +108 -44
  63. package/build/lib/{chunk-7pwa92rn.js → chunk-mph5fvk7.js} +1 -1
  64. package/build/lib/chunk-psc23epq.js +107 -0
  65. package/build/lib/{chunk-acgbyf5h.js → chunk-qxad7qjd.js} +4 -2
  66. package/build/lib/{chunk-3c00n0cy.js → chunk-r0v3vvgm.js} +54 -33
  67. package/build/lib/{chunk-1yct6cra.js → chunk-rt13120a.js} +1 -1
  68. package/build/lib/{chunk-v3dk9j7k.js → chunk-sgya016v.js} +93 -22
  69. package/build/lib/{chunk-1fy8azee.js → chunk-txef92hq.js} +1 -1
  70. package/build/lib/{chunk-2qsq76xq.js → chunk-vcd90qrn.js} +189 -90
  71. package/build/lib/{chunk-7gjdygcx.js → chunk-vwr9w3ab.js} +2 -0
  72. package/build/lib/{chunk-z137x08b.js → chunk-wf6ak0m3.js} +92 -70
  73. package/build/lib/{chunk-xhdpgpkm.js → chunk-xft0ms9j.js} +2 -2
  74. package/build/lib/core/agents/agentRuntime.d.ts +3 -1
  75. package/build/lib/core/agents/defineAgent.d.ts +3 -2
  76. package/build/lib/core/agents/lance.d.ts +153 -0
  77. package/build/lib/core/agents/lanceJudge.d.ts +3 -0
  78. package/build/lib/core/agents/offSecAgent/index.d.ts +3 -1
  79. package/build/lib/core/agents/offSecAgent/index.js +16 -14
  80. package/build/lib/core/agents/offSecAgent/offensiveSecurityAgent.d.ts +4 -0
  81. package/build/lib/core/agents/offSecAgent/prompt.d.ts +3 -1
  82. package/build/lib/core/agents/offSecAgent/subagentSpawner.d.ts +18 -2
  83. package/build/lib/core/agents/offSecAgent/subagentSpawner.js +10 -8
  84. package/build/lib/core/agents/offSecAgent/toolExecutionContext.d.ts +3 -0
  85. package/build/lib/core/agents/offSecAgent/toolLifecycle.d.ts +1 -0
  86. package/build/lib/core/agents/offSecAgent/tools/index.js +10 -6
  87. package/build/lib/core/agents/offSecAgent/tools/listMemories.d.ts +11 -1
  88. package/build/lib/core/agents/offSecAgent/tools/reportError.d.ts +2 -1
  89. package/build/lib/core/agents/offSecAgent/tools/runPentestWorkflow.d.ts +20 -1
  90. package/build/lib/core/agents/offSecAgent/tools/spawnPentestAgent.d.ts +4 -7
  91. package/build/lib/core/agents/offSecAgent/tools/spawnPentestSwarm.d.ts +7 -1
  92. package/build/lib/core/agents/offSecAgent/tools/types.d.ts +6 -3
  93. package/build/lib/core/agents/offSecAgent/tools/whiteboxCandidates.d.ts +2 -2
  94. package/build/lib/core/agents/offSecAgent/types.d.ts +19 -3
  95. package/build/lib/core/agents/specialized/attackSurface/blackboxAgent.js +13 -13
  96. package/build/lib/core/agents/specialized/attackSurface/schemas.js +12 -12
  97. package/build/lib/core/agents/specialized/authenticationAgent/index.js +14 -14
  98. package/build/lib/core/agents/specialized/codeAgent/agent.js +13 -13
  99. package/build/lib/core/agents/specialized/codeAgent/prompts.d.ts +1 -0
  100. package/build/lib/core/agents/specialized/cvssScorer/index.d.ts +2 -1
  101. package/build/lib/core/agents/specialized/findingJudge/agent.js +11 -11
  102. package/build/lib/core/agents/specialized/findingJudge/index.d.ts +2 -1
  103. package/build/lib/core/agents/specialized/findingJudge/index.js +4 -4
  104. package/build/lib/core/agents/specialized/patching/index.js +14 -14
  105. package/build/lib/core/agents/specialized/pentest/agent.d.ts +18 -4
  106. package/build/lib/core/agents/specialized/pentest/agent.js +15 -13
  107. package/build/lib/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.d.ts +2 -1
  108. package/build/lib/core/agents/specialized/whiteboxAttackSurface/index.js +13 -13
  109. package/build/lib/core/ai/ai.d.ts +29 -70
  110. package/build/lib/core/ai/ai.js +5 -3
  111. package/build/lib/core/ai/index.d.ts +1 -0
  112. package/build/lib/core/ai/index.js +16 -4
  113. package/build/lib/core/ai/models/claude-capabilities.d.ts +3 -2
  114. package/build/lib/core/ai/models/concentrate.d.ts +1 -0
  115. package/build/lib/core/ai/models/index.d.ts +1 -1
  116. package/build/lib/core/ai/providers/concentrate.d.ts +1 -0
  117. package/build/lib/core/ai/reasoning.d.ts +44 -0
  118. package/build/lib/core/ai/utils.d.ts +2 -0
  119. package/build/lib/core/ai/utils.js +5 -3
  120. package/build/lib/core/api/index.d.ts +6 -0
  121. package/build/lib/core/api/index.js +32 -23
  122. package/build/lib/core/api/lance.d.ts +6 -0
  123. package/build/lib/core/api/offesecAgent.d.ts +1 -0
  124. package/build/lib/core/api/reasoning.d.ts +12 -0
  125. package/build/lib/core/api/targetedPentest.d.ts +1 -0
  126. package/build/lib/core/api/targetedPentest.js +14 -14
  127. package/build/lib/core/cli/reasoning.d.ts +4 -0
  128. package/build/lib/core/config/config.d.ts +10 -2
  129. package/build/lib/core/config/customProviders.d.ts +11 -0
  130. package/build/lib/core/config/index.js +1 -1
  131. package/build/lib/core/eventBus.js +1 -1
  132. package/build/lib/core/findings/registry.d.ts +3 -1
  133. package/build/lib/core/findings/registry.js +4 -4
  134. package/build/lib/core/observability/index.js +1 -1
  135. package/build/lib/core/observability/telemetry.d.ts +4 -0
  136. package/build/lib/core/session/index.d.ts +47 -0
  137. package/build/lib/core/session/index.js +6 -4
  138. package/build/lib/core/session/persistence.d.ts +7 -0
  139. package/build/lib/core/session/persistence.js +8 -4
  140. package/build/lib/core/tools/backends/index.js +1 -1
  141. package/build/lib/core/tools/backends/resolve.d.ts +1 -0
  142. package/build/lib/core/workflows/fastStrike.d.ts +3 -1
  143. package/build/lib/core/workflows/pentest.d.ts +12 -2
  144. package/build/lib/core/workflows/pentest.js +19 -19
  145. package/build/lib/core/workflows/seams.js +5 -5
  146. package/build/lib/core/workflows/threatModel.d.ts +1 -0
  147. package/build/lib/core/workflows/whiteboxAttackSurface.d.ts +6 -4
  148. package/build/lib/core/workflows/whiteboxAttackSurface.js +15 -15
  149. package/build/lib/index.js +25 -25
  150. package/build/{logs-x62b7hk1.js → logs-bcbjpjp6.js} +16 -13
  151. package/build/{native-rollout-evidence-c9yhfqcs.js → native-rollout-evidence-rph1774m.js} +5 -5
  152. package/build/offesecAgent-81h1p6ee.js +32 -0
  153. package/build/pentest-2q20q6n1.js +23 -0
  154. package/build/{pentests-e0f32aqx.js → pentests-jgw031mc.js} +16 -13
  155. package/build/recordedRunClient-6axj6fba.js +11 -0
  156. package/build/subagentSpawner-a861gvt0.js +12 -0
  157. package/build/targetedPentest-6yxznt7s.js +91 -0
  158. package/build/{targets-xj9g3q0z.js → targets-bjah19dp.js} +16 -13
  159. package/build/threatModel-739kpexm.js +24 -0
  160. package/build/{uninstall-7s784q4a.js → uninstall-yxrn9edt.js} +1 -1
  161. package/build/{upload-4gvhwmvr.js → upload-34ewrjp5.js} +4 -4
  162. package/build/{utils-cjrz64pc.js → utils-fzm66efw.js} +5 -5
  163. package/package.json +2 -1
  164. package/src/cli/issues.test.ts +17 -1
  165. package/src/cli/lance-verifier.test.ts +57 -0
  166. package/src/cli/lance-verifier.ts +73 -0
  167. package/src/cli/lance.test.ts +71 -0
  168. package/src/cli/lance.ts +48 -0
  169. package/src/cli.ts +109 -19
  170. package/src/core/agents/agentRuntime.test.ts +42 -0
  171. package/src/core/agents/agentRuntime.ts +7 -2
  172. package/src/core/agents/defineAgent.ts +3 -1
  173. package/src/core/agents/lance.test.ts +421 -0
  174. package/src/core/agents/lance.ts +701 -0
  175. package/src/core/agents/lanceJudge.test.ts +166 -0
  176. package/src/core/agents/lanceJudge.ts +115 -0
  177. package/src/core/agents/offSecAgent/index.ts +3 -0
  178. package/src/core/agents/offSecAgent/interruptedStepFinalization.test.ts +51 -0
  179. package/src/core/agents/offSecAgent/lance.test.ts +188 -0
  180. package/src/core/agents/offSecAgent/offensiveSecurityAgent.test.ts +92 -5
  181. package/src/core/agents/offSecAgent/offensiveSecurityAgent.ts +244 -41
  182. package/src/core/agents/offSecAgent/prompt.test.ts +46 -0
  183. package/src/core/agents/offSecAgent/prompt.ts +52 -8
  184. package/src/core/agents/offSecAgent/selectiveConstruction.publicPath.test.ts +38 -0
  185. package/src/core/agents/offSecAgent/selectiveConstruction.test.ts +64 -0
  186. package/src/core/agents/offSecAgent/subagentSpawner.hooks.test.ts +11 -3
  187. package/src/core/agents/offSecAgent/subagentSpawner.reasoning.test.ts +167 -0
  188. package/src/core/agents/offSecAgent/subagentSpawner.ts +82 -28
  189. package/src/core/agents/offSecAgent/toolExecutionContext.test.ts +230 -0
  190. package/src/core/agents/offSecAgent/toolExecutionContext.ts +56 -0
  191. package/src/core/agents/offSecAgent/toolLifecycle.ts +2 -0
  192. package/src/core/agents/offSecAgent/tools/delegateAuth.ts +4 -2
  193. package/src/core/agents/offSecAgent/tools/documentFinding.test.ts +9 -4
  194. package/src/core/agents/offSecAgent/tools/documentFinding.ts +10 -9
  195. package/src/core/agents/offSecAgent/tools/listMemories.test.ts +230 -0
  196. package/src/core/agents/offSecAgent/tools/listMemories.ts +73 -6
  197. package/src/core/agents/offSecAgent/tools/reportError.test.ts +6 -0
  198. package/src/core/agents/offSecAgent/tools/reportError.ts +10 -2
  199. package/src/core/agents/offSecAgent/tools/runAttackSurface.ts +7 -4
  200. package/src/core/agents/offSecAgent/tools/runPentestWorkflow.test.ts +57 -0
  201. package/src/core/agents/offSecAgent/tools/runPentestWorkflow.ts +21 -1
  202. package/src/core/agents/offSecAgent/tools/runWhiteboxScan.ts +5 -2
  203. package/src/core/agents/offSecAgent/tools/spawnCodingAgent.ts +50 -67
  204. package/src/core/agents/offSecAgent/tools/spawnPentestAgent.test.ts +79 -3
  205. package/src/core/agents/offSecAgent/tools/spawnPentestAgent.ts +36 -16
  206. package/src/core/agents/offSecAgent/tools/spawnPentestSwarm.ts +28 -5
  207. package/src/core/agents/offSecAgent/tools/threatModelGenerator.ts +4 -3
  208. package/src/core/agents/offSecAgent/tools/types.ts +12 -2
  209. package/src/core/agents/offSecAgent/tools/updateTask.ts +5 -1
  210. package/src/core/agents/offSecAgent/tools/whiteboxCandidates.ts +11 -4
  211. package/src/core/agents/offSecAgent/types.ts +22 -1
  212. package/src/core/agents/specialized/codeAgent/agent.test.ts +7 -0
  213. package/src/core/agents/specialized/codeAgent/agent.ts +9 -2
  214. package/src/core/agents/specialized/codeAgent/prompts.ts +6 -0
  215. package/src/core/agents/specialized/cvssScorer/index.test.ts +2 -1
  216. package/src/core/agents/specialized/cvssScorer/index.ts +3 -0
  217. package/src/core/agents/specialized/findingJudge/index.ts +6 -0
  218. package/src/core/agents/specialized/pentest/agent.test.ts +155 -0
  219. package/src/core/agents/specialized/pentest/agent.ts +181 -52
  220. package/src/core/agents/specialized/pentest/results.test.ts +59 -4
  221. package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.concurrency.test.ts +59 -2
  222. package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.hooks.test.ts +127 -0
  223. package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.ts +74 -68
  224. package/src/core/ai/ai.durability-hooks.test.ts +25 -7
  225. package/src/core/ai/ai.lance.test.ts +581 -0
  226. package/src/core/ai/ai.operator-context.bench.test.ts +1390 -0
  227. package/src/core/ai/ai.recorded-context.test.ts +89 -8
  228. package/src/core/ai/ai.test.ts +81 -59
  229. package/src/core/ai/ai.ts +453 -327
  230. package/src/core/ai/compactionTelemetry.test.ts +69 -17
  231. package/src/core/ai/index.ts +11 -0
  232. package/src/core/ai/models/anthropic.ts +6 -0
  233. package/src/core/ai/models/bedrock.ts +18 -0
  234. package/src/core/ai/models/claude-capabilities.ts +29 -8
  235. package/src/core/ai/models/concentrate.ts +52 -0
  236. package/src/core/ai/models/index.ts +25 -4
  237. package/src/core/ai/models/models.test.ts +179 -7
  238. package/src/core/ai/models/openrouter-reasoning.json +219 -0
  239. package/src/core/ai/models/openrouter.ts +43 -1
  240. package/src/core/ai/providers/anthropic-models.test.ts +60 -1
  241. package/src/core/ai/providers/bedrock-models.test.ts +81 -0
  242. package/src/core/ai/providers/concentrate.test.ts +31 -0
  243. package/src/core/ai/providers/concentrate.ts +11 -0
  244. package/src/core/ai/providers/custom.test.ts +74 -0
  245. package/src/core/ai/providers/custom.ts +3 -1
  246. package/src/core/ai/providers/gateway-models.test.ts +16 -6
  247. package/src/core/ai/providers/pensarFormatters.ts +8 -1
  248. package/src/core/ai/reasoning.test.ts +313 -0
  249. package/src/core/ai/reasoning.ts +500 -0
  250. package/src/core/ai/streamIdleTimeout.test.ts +21 -0
  251. package/src/core/ai/utils.native-rollout-evidence.test.ts +21 -8
  252. package/src/core/ai/utils.ts +269 -163
  253. package/src/core/api/attackSurface.ts +1 -0
  254. package/src/core/api/blackboxPentest.ts +9 -1
  255. package/src/core/api/index.ts +17 -0
  256. package/src/core/api/lance.ts +23 -0
  257. package/src/core/api/offesecAgent.test.ts +51 -0
  258. package/src/core/api/offesecAgent.ts +15 -1
  259. package/src/core/api/reasoning.test.ts +213 -0
  260. package/src/core/api/reasoning.ts +44 -0
  261. package/src/core/api/recordedRun.loop.test.ts +5 -1
  262. package/src/core/api/targetedPentest.test.ts +14 -2
  263. package/src/core/api/targetedPentest.ts +52 -5
  264. package/src/core/cli/reasoning.test.ts +149 -0
  265. package/src/core/cli/reasoning.ts +95 -0
  266. package/src/core/config/config.test.ts +35 -0
  267. package/src/core/config/config.ts +11 -0
  268. package/src/core/config/customProviders.ts +21 -2
  269. package/src/core/eventBus.ts +1 -0
  270. package/src/core/findings/registry.test.ts +9 -1
  271. package/src/core/findings/registry.ts +6 -0
  272. package/src/core/observability/telemetry.test.ts +131 -79
  273. package/src/core/observability/telemetry.ts +5 -0
  274. package/src/core/runtime/sqliteRunObservation.test.ts +1 -1
  275. package/src/core/session/index.ts +38 -0
  276. package/src/core/session/persistence.test.ts +62 -0
  277. package/src/core/session/persistence.ts +56 -0
  278. package/src/core/tools/backends/resolve.ts +7 -0
  279. package/src/core/tools/backends/whiteboxJobs.test.ts +9 -8
  280. package/src/core/workflows/fastStrike.seams.test.ts +149 -0
  281. package/src/core/workflows/fastStrike.test.ts +18 -1
  282. package/src/core/workflows/fastStrike.ts +106 -11
  283. package/src/core/workflows/pentest.test.ts +104 -0
  284. package/src/core/workflows/pentest.ts +128 -7
  285. package/src/core/workflows/seams.test.ts +13 -0
  286. package/src/core/workflows/seams.ts +5 -1
  287. package/src/core/workflows/threatModel.ts +2 -0
  288. package/src/core/workflows/whiteboxAttackSurface.test.ts +74 -2
  289. package/src/core/workflows/whiteboxAttackSurface.ts +102 -73
  290. package/src/tui/command-registry.test.ts +12 -0
  291. package/src/tui/command-registry.ts +10 -0
  292. package/src/tui/components/chat/config-view.tsx +6 -10
  293. package/src/tui/components/chat/home-view.tsx +2 -2
  294. package/src/tui/components/chat/input-area.tsx +2 -2
  295. package/src/tui/components/chat/loading-indicator.tsx +20 -4
  296. package/src/tui/components/chat/message-list.tsx +1 -0
  297. package/src/tui/components/commands/__tests__/advanced-settings-lifecycle.tsx +164 -0
  298. package/src/tui/components/commands/advanced-settings.test.ts +34 -0
  299. package/src/tui/components/commands/advanced-settings.tsx +220 -31
  300. package/src/tui/components/commands/models-display.tsx +7 -1
  301. package/src/tui/components/model-picker/ModelPicker.tsx +65 -103
  302. package/src/tui/components/model-picker/ModelPickerDialog.tsx +8 -14
  303. package/src/tui/components/model-picker/__tests__/reasoning-control.tsx +162 -0
  304. package/src/tui/components/model-picker/reasoning-control.test.ts +15 -0
  305. package/src/tui/components/operator-dashboard/index.tsx +166 -50
  306. package/src/tui/components/operator-dashboard/logic.test.ts +144 -2
  307. package/src/tui/components/operator-dashboard/logic.ts +54 -3
  308. package/src/tui/components/operator-dashboard/run-events.test.ts +3 -1
  309. package/src/tui/components/shared/__tests__/tool-renderer-lifecycle.tsx +18 -0
  310. package/src/tui/components/shared/action-label.test.ts +9 -0
  311. package/src/tui/components/shared/compacting-icon.tsx +18 -0
  312. package/src/tui/components/shared/index.ts +1 -0
  313. package/src/tui/components/shared/tool-registry.ts +1 -0
  314. package/src/tui/components/shared/tool-renderer.tsx +21 -7
  315. package/src/tui/context/agent.tsx +32 -79
  316. package/src/tui/context/route.tsx +2 -0
  317. package/src/tui/index.tsx +1 -0
  318. package/src/tui/keybindings/registry.ts +2 -0
  319. package/build/agent-6w7egcf1.js +0 -13
  320. package/build/agent-9zx75feh.js +0 -13
  321. package/build/blackboxAgent-s56sgx9b.js +0 -13
  322. package/build/cli-g06s7edq.js +0 -11
  323. package/build/lib/chunk-z96pbjhd.js +0 -56
  324. package/build/offesecAgent-5whnpeta.js +0 -22
  325. package/build/pentest-e61hc38s.js +0 -22
  326. package/build/recordedRunClient-dp032c32.js +0 -11
  327. package/build/targetedPentest-rcbs4nvp.js +0 -57
  328. package/build/threatModel-jkxfa40s.js +0 -21
@@ -45,7 +45,7 @@ function model() {
45
45
  stream: simulateReadableStream({
46
46
  chunks: [
47
47
  { type: "text-start", id: "t" },
48
- { type: "text-delta", id: "t", delta: "Done" },
48
+ { type: "text-delta", id: "t", delta: "A compact summary" },
49
49
  { type: "text-end", id: "t" },
50
50
  {
51
51
  type: "finish",
@@ -58,6 +58,26 @@ function model() {
58
58
  });
59
59
  }
60
60
 
61
+ const SUMMARIZE_FN = "apex.context.summarize";
62
+
63
+ // Summarization and the resumed agent turn both reach the model through doStream,
64
+ // so a mock that must fail only the summary matches on its instruction.
65
+ function isSummaryCall(options: { prompt: unknown }): boolean {
66
+ return JSON.stringify(options.prompt).includes("Compact this conversation");
67
+ }
68
+
69
+ // Located by telemetry functionId rather than span name, so it survives the
70
+ // summarizer switching between streaming and one-shot generation.
71
+ function summarySpan<
72
+ T extends { name: string; attributes: Record<string, unknown> },
73
+ >(spans: readonly T[]): T | undefined {
74
+ return spans.find(
75
+ (span) =>
76
+ span.name.endsWith(".doStream") &&
77
+ span.attributes["ai.telemetry.functionId"] === SUMMARIZE_FN,
78
+ );
79
+ }
80
+
61
81
  async function drain(stream: { fullStream: AsyncIterable<unknown> }) {
62
82
  for await (const _part of stream.fullStream) {
63
83
  /* consume the SDK lifecycle */
@@ -193,10 +213,13 @@ describe("compaction observability", () => {
193
213
  await drain(stream);
194
214
  const spans = otel.getFinishedSpans();
195
215
  const compaction = requireSpan(spans, "apex.context.compact");
196
- const summary = spans.find(
197
- (span) => span.name === "ai.generateText.doGenerate",
216
+ const summary = summarySpan(spans);
217
+ const resumed = spans.find(
218
+ (span) =>
219
+ span.name === "ai.streamText" &&
220
+ span.attributes["ai.telemetry.functionId"] !== SUMMARIZE_FN,
198
221
  );
199
- const resumed = requireSpan(spans, "ai.streamText");
222
+ if (!resumed) throw new Error("no resumed agent-turn span");
200
223
  expect(compaction?.attributes).toMatchObject({
201
224
  "apex.compaction.outcome": "completed",
202
225
  "pensar.session.id": "ses_child",
@@ -387,7 +410,7 @@ describe("compaction observability", () => {
387
410
  ).toBe(true);
388
411
  });
389
412
 
390
- it("preserves tool evidence omitted by the summarizer and links its actual request and response", async () => {
413
+ it("passes tool evidence to the summarizer and links its actual request and response", async () => {
391
414
  const summaryModel = model();
392
415
  state.model = summaryModel;
393
416
  const messages: ModelMessage[] = [
@@ -418,11 +441,12 @@ describe("compaction observability", () => {
418
441
  prompt: "original task",
419
442
  system: "research system",
420
443
  });
421
- const summary = requireSpan(spans, "ai.generateText.doGenerate");
444
+ const summary = summarySpan(spans);
445
+ if (!summary) throw new Error("no summarizer model span");
422
446
  expect(summary.attributes["ai.prompt.messages"]).toContain(
423
447
  "continue researching",
424
448
  );
425
- expect(summary.attributes["ai.prompt.messages"]).not.toContain(
449
+ expect(summary.attributes["ai.prompt.messages"]).toContain(
426
450
  "evidence evidence",
427
451
  );
428
452
  expect(summary.attributes["ai.response.text"]).toBe("A compact summary");
@@ -433,7 +457,8 @@ describe("compaction observability", () => {
433
457
  String(compact.attributes["apex.compaction.evidence.after.json"]),
434
458
  );
435
459
  expect(after.messages[0].content).toContain("A compact summary");
436
- expect(summaryModel.doStreamCalls[0]?.prompt).toMatchObject([
460
+ // [0] is the summarization call; the resumed agent turn follows it.
461
+ expect(summaryModel.doStreamCalls[1]?.prompt).toMatchObject([
437
462
  { role: "system", content: after.system },
438
463
  {
439
464
  role: "user",
@@ -538,11 +563,30 @@ describe("compaction observability", () => {
538
563
  }
539
564
  });
540
565
 
566
+ it("summarizes over a stream, so a slow summary is never a silent connection", async () => {
567
+ // A one-shot summarization returns no response headers until the whole summary
568
+ // exists, which anything watching time-to-headers reads as a dead connection.
569
+ // Compaction became unreachable that way, so the call must stream.
570
+ const summaryModel = model();
571
+ state.model = summaryModel;
572
+ await drain(
573
+ createSummarizationStream(
574
+ [{ role: "user", content: "history" }],
575
+ { prompt: "continue", model: MODEL, silent: true },
576
+ summaryModel,
577
+ ),
578
+ );
579
+ expect(summaryModel.doGenerateCalls).toHaveLength(0);
580
+ expect(JSON.stringify(summaryModel.doStreamCalls[0]?.prompt)).toContain(
581
+ "Compact this conversation",
582
+ );
583
+ });
584
+
541
585
  it("records summary cancellation without hiding the original error", async () => {
542
586
  const cancelled = new Error("cancelled");
543
587
  cancelled.name = "AbortError";
544
588
  state.model = new MockLanguageModelV3({
545
- doGenerate: async () => {
589
+ doStream: async () => {
546
590
  throw cancelled;
547
591
  },
548
592
  });
@@ -569,10 +613,10 @@ describe("compaction observability", () => {
569
613
  const normal = model();
570
614
  state.model = new MockLanguageModelV3({
571
615
  modelId: MODEL,
572
- doGenerate: async () => {
573
- throw new Error("context length exceeded");
574
- },
575
- doStream: (options) => normal.doStream(options),
616
+ doStream: (options) =>
617
+ isSummaryCall(options)
618
+ ? Promise.reject(new Error("context length exceeded"))
619
+ : normal.doStream(options),
576
620
  });
577
621
  await runWithAiPayloadCapture(true, () =>
578
622
  drain(
@@ -612,10 +656,18 @@ describe("compaction observability", () => {
612
656
  String(reset?.attributes["apex.compaction.evidence.after.json"]),
613
657
  )?.messages,
614
658
  ).toEqual([{ role: "user", content: "original task" }]);
659
+ // A resumed agent turn must carry the reset's span id. Summarizer attempts are
660
+ // `ai.streamText` spans too now, so they are excluded rather than assumed last.
615
661
  expect(
616
- spans.find((span) => span.name === "ai.streamText")?.attributes[
617
- "ai.telemetry.metadata.compactionSpanId"
618
- ],
619
- ).toBe(reset?.spanContext().spanId);
662
+ spans
663
+ .filter(
664
+ (span) =>
665
+ span.name === "ai.streamText" &&
666
+ span.attributes["ai.telemetry.functionId"] !== SUMMARIZE_FN,
667
+ )
668
+ .map(
669
+ (span) => span.attributes["ai.telemetry.metadata.compactionSpanId"],
670
+ ),
671
+ ).toContain(reset?.spanContext().spanId);
620
672
  });
621
673
  });
@@ -69,5 +69,16 @@ export {
69
69
  stringifyCanonicalJson,
70
70
  toJsonValue,
71
71
  } from "./native-rollout-evidence";
72
+ export {
73
+ getModelSettings,
74
+ getReasoningCapabilities,
75
+ getReasoningChoices,
76
+ REASONING_SETTINGS,
77
+ type ReasoningCapabilities,
78
+ type ReasoningEffort,
79
+ type ReasoningSetting,
80
+ resolveReasoningSetting,
81
+ validateReasoningSetting,
82
+ } from "./reasoning";
72
83
  export type { AIAuthConfig } from "./utils";
73
84
  export { buildAuthConfig } from "./utils";
@@ -112,6 +112,12 @@ export const ANTHROPIC_MODELS: ModelInfo[] = [
112
112
  provider: "anthropic",
113
113
  contextLength: 1000000,
114
114
  },
115
+ {
116
+ id: "claude-haiku-5-5",
117
+ name: "Claude Haiku 5.5",
118
+ provider: "anthropic",
119
+ contextLength: 1000000,
120
+ },
115
121
  {
116
122
  id: "claude-sonnet-5-5",
117
123
  name: "Claude Sonnet 5.5",
@@ -465,6 +465,18 @@ export const BEDROCK_MODELS: ModelInfo[] = [
465
465
  provider: "bedrock",
466
466
  contextLength: 262000,
467
467
  },
468
+ {
469
+ id: "anthropic.claude-haiku-5-5",
470
+ name: "Claude Haiku 5.5 (Bedrock)",
471
+ provider: "bedrock",
472
+ contextLength: 1000000,
473
+ },
474
+ {
475
+ id: "us.anthropic.claude-haiku-5-5",
476
+ name: "Claude Haiku 5.5 (US)",
477
+ provider: "bedrock",
478
+ contextLength: 1000000,
479
+ },
468
480
  {
469
481
  id: "anthropic.claude-opus-4-8",
470
482
  name: "Claude Opus 4.8 (Bedrock)",
@@ -597,4 +609,10 @@ export const BEDROCK_MODELS: ModelInfo[] = [
597
609
  provider: "bedrock",
598
610
  contextLength: 1000000,
599
611
  },
612
+ {
613
+ id: "global.anthropic.claude-haiku-5-5",
614
+ name: "Claude Haiku 5.5 (Global)",
615
+ provider: "bedrock",
616
+ contextLength: 1000000,
617
+ },
600
618
  ];
@@ -1,22 +1,43 @@
1
- type ClaudeCapabilities = {
1
+ export type ClaudeThinkingEffort = "low" | "medium" | "high" | "xhigh" | "max";
2
+
3
+ export type ClaudeCapabilities = {
2
4
  alwaysOnThinking: boolean;
3
5
  bindsThinking?: boolean;
6
+ defaultEffort: ClaudeThinkingEffort;
4
7
  };
5
8
 
6
9
  const CLAUDE_CAPABILITIES: Record<string, ClaudeCapabilities> = {
7
- "claude-sonnet-5-5": { alwaysOnThinking: true, bindsThinking: true },
8
- "claude-opus-5-5": { alwaysOnThinking: true, bindsThinking: true },
9
- "claude-fable-5-1": { alwaysOnThinking: true, bindsThinking: true },
10
- "claude-opus-5": { alwaysOnThinking: false },
11
- "claude-sonnet-5": { alwaysOnThinking: false },
12
- "claude-fable-5": { alwaysOnThinking: true },
10
+ "claude-haiku-5-5": {
11
+ alwaysOnThinking: false,
12
+ bindsThinking: true,
13
+ defaultEffort: "medium",
14
+ },
15
+ "claude-sonnet-5-5": {
16
+ alwaysOnThinking: true,
17
+ bindsThinking: true,
18
+ defaultEffort: "high",
19
+ },
20
+ "claude-opus-5-5": {
21
+ alwaysOnThinking: true,
22
+ bindsThinking: true,
23
+ defaultEffort: "medium",
24
+ },
25
+ "claude-fable-5-1": {
26
+ alwaysOnThinking: true,
27
+ bindsThinking: true,
28
+ defaultEffort: "high",
29
+ },
30
+ "claude-opus-5": { alwaysOnThinking: false, defaultEffort: "high" },
31
+ "claude-sonnet-5": { alwaysOnThinking: false, defaultEffort: "high" },
32
+ "claude-fable-5": { alwaysOnThinking: true, defaultEffort: "high" },
13
33
  };
14
34
 
15
35
  export function getClaudeCapabilities(
16
36
  modelId: string,
17
37
  ): ClaudeCapabilities | undefined {
18
38
  const nativeId = modelId
19
- .replace(/^(concentrate:|anthropic\/)/, "")
39
+ .replace(/^(concentrate:|pensar:)/, "")
40
+ .replace(/^(?:(?:us|global|eu|au|jp)\.)?anthropic[./]/, "")
20
41
  .replace(/(\d)\.(\d)/g, "$1-$2");
21
42
  return CLAUDE_CAPABILITIES[nativeId];
22
43
  }
@@ -1,5 +1,15 @@
1
1
  import type { ModelInfo } from "../ai";
2
2
 
3
+ // Concentrate does not route on max_output_tokens, so these use the lowest
4
+ // ceiling among each slug's upstream providers.
5
+ export const CONCENTRATE_MAX_OUTPUT_TOKENS: Readonly<Record<string, number>> = {
6
+ "concentrate:kimi-k3": 32_768,
7
+ "concentrate:qwen3.8-max": 32_768,
8
+ "concentrate:qwen3.8-27b": 32_768,
9
+ "concentrate:deepseek-v4-1-flash": 32_768,
10
+ "concentrate:deepseek-v4-1-flash-1017": 131_072,
11
+ };
12
+
3
13
  // Curated from Concentrate's public model catalog. The local prefix keeps
4
14
  // gateway selections distinct from direct-provider models and is not sent.
5
15
  export const CONCENTRATE_MODELS: ModelInfo[] = [
@@ -153,6 +163,36 @@ export const CONCENTRATE_MODELS: ModelInfo[] = [
153
163
  provider: "concentrate",
154
164
  contextLength: 1000000,
155
165
  },
166
+ {
167
+ id: "concentrate:qwen3.8-max",
168
+ name: "Qwen3.8 Max",
169
+ provider: "concentrate",
170
+ contextLength: 256000,
171
+ },
172
+ {
173
+ id: "concentrate:qwen3.8-27b",
174
+ name: "Qwen3.8 27B",
175
+ provider: "concentrate",
176
+ contextLength: 262144,
177
+ },
178
+ {
179
+ id: "concentrate:deepseek-v4-1-flash-1017",
180
+ name: "DeepSeek V4.1 Flash 1017",
181
+ provider: "concentrate",
182
+ contextLength: 1048576,
183
+ },
184
+ {
185
+ id: "concentrate:deepseek-v4-1-flash",
186
+ name: "DeepSeek V4.1 Flash",
187
+ provider: "concentrate",
188
+ contextLength: 1000000,
189
+ },
190
+ {
191
+ id: "concentrate:deepseek-v4-pro-0813",
192
+ name: "DeepSeek V4 Pro 0813",
193
+ provider: "concentrate",
194
+ contextLength: 1048576,
195
+ },
156
196
  {
157
197
  id: "concentrate:deepseek-v3-2",
158
198
  name: "DeepSeek V3.2",
@@ -165,6 +205,12 @@ export const CONCENTRATE_MODELS: ModelInfo[] = [
165
205
  provider: "concentrate",
166
206
  contextLength: 256000,
167
207
  },
208
+ {
209
+ id: "concentrate:kimi-k3",
210
+ name: "Kimi K3",
211
+ provider: "concentrate",
212
+ contextLength: 1048576,
213
+ },
168
214
  {
169
215
  id: "concentrate:kimi-k2-thinking",
170
216
  name: "Kimi K2 Thinking",
@@ -177,6 +223,12 @@ export const CONCENTRATE_MODELS: ModelInfo[] = [
177
223
  provider: "concentrate",
178
224
  contextLength: 204800,
179
225
  },
226
+ {
227
+ id: "concentrate:mimo-v2.6-pro",
228
+ name: "MiMo V2.6 Pro",
229
+ provider: "concentrate",
230
+ contextLength: 1048576,
231
+ },
180
232
  {
181
233
  id: "concentrate:glm-5.3",
182
234
  name: "GLM-5.3",
@@ -12,9 +12,15 @@ import { ANTHROPIC_MODELS } from "./anthropic";
12
12
  import { BEDROCK_MODELS } from "./bedrock";
13
13
  import { getClaudeCapabilities } from "./claude-capabilities";
14
14
 
15
- export { getClaudeCapabilities } from "./claude-capabilities";
15
+ export {
16
+ type ClaudeThinkingEffort,
17
+ getClaudeCapabilities,
18
+ } from "./claude-capabilities";
16
19
 
17
- import { CONCENTRATE_MODELS } from "./concentrate";
20
+ import {
21
+ CONCENTRATE_MAX_OUTPUT_TOKENS,
22
+ CONCENTRATE_MODELS,
23
+ } from "./concentrate";
18
24
  import { GOOGLE_MODELS } from "./google";
19
25
  import { INCEPTION_MODELS } from "./inception";
20
26
  // Bedrock Mantle (OpenAI Responses API) — curated manually.
@@ -100,6 +106,8 @@ export function getMaxOutputTokens(
100
106
  function lookupOutputBudgetByPattern(modelId: string): number {
101
107
  if (modelId === "chat-latest") return 128_000;
102
108
  if (getClaudeCapabilities(modelId)) return 128_000;
109
+ const concentrateBudget = CONCENTRATE_MAX_OUTPUT_TOKENS[modelId];
110
+ if (concentrateBudget) return concentrateBudget;
103
111
  // OpenRouter uses dots in Claude version numbers (anthropic/claude-opus-4.6)
104
112
  // while native Anthropic uses dashes (claude-opus-4-6-20250929). Normalize
105
113
  // digit.digit sequences to dashes so all Claude patterns match both forms.
@@ -219,14 +227,22 @@ function lookupOutputBudgetByPattern(modelId: string): number {
219
227
  return 131_072;
220
228
  }
221
229
 
230
+ if (modelId.includes("mimo-v2.6-pro")) {
231
+ return 131_072;
232
+ }
233
+
234
+ if (modelId.includes("kimi-k3")) {
235
+ return 131_072;
236
+ }
237
+
222
238
  // GLM 5 models ship a ~131K (128Ki) max-output window. Matching the family
223
239
  // keeps Bedrock and OpenRouter model IDs on the same budget.
224
240
  if (modelId.includes("glm-5")) {
225
241
  return 131_072;
226
242
  }
227
243
 
228
- // V4.1 Flash's larger output window must precede the legacy DeepSeek fallback.
229
- if (modelId.includes("deepseek-v4.1-flash")) {
244
+ // DeepSeek V4's larger output window must precede the legacy DeepSeek fallback.
245
+ if (/deepseek-v4[.-]/.test(modelId)) {
230
246
  return 384_000;
231
247
  }
232
248
 
@@ -235,6 +251,11 @@ function lookupOutputBudgetByPattern(modelId: string): number {
235
251
  return 8_192;
236
252
  }
237
253
 
254
+ // Qwen3.8's first-party 131K output window must precede the Qwen fallback.
255
+ if (modelId.includes("qwen3.8")) {
256
+ return 131_072;
257
+ }
258
+
238
259
  // Qwen3 Coder ships a 16K max-output window over its 128K+ context. Match the
239
260
  // whole `qwen` family so future Bedrock/OpenRouter qwen ids don't silently
240
261
  // inherit the 4,096 default.
@@ -103,6 +103,11 @@ describe("getMaxOutputTokens", () => {
103
103
  });
104
104
 
105
105
  it("recognizes Claude tier-specific budgets", () => {
106
+ expect(getMaxOutputTokens("claude-haiku-5-5")).toBe(128_000);
107
+ expect(getMaxOutputTokens("global.anthropic.claude-haiku-5-5")).toBe(
108
+ 128_000,
109
+ );
110
+ expect(getMaxOutputTokens("anthropic/claude-haiku-5.5")).toBe(128_000);
106
111
  expect(getMaxOutputTokens("claude-sonnet-4-5-20250929")).toBe(64_000);
107
112
  expect(getMaxOutputTokens("claude-opus-4-5-20250101")).toBe(64_000);
108
113
  expect(getMaxOutputTokens("claude-haiku-4-5-20251001")).toBe(64_000);
@@ -111,6 +116,24 @@ describe("getMaxOutputTokens", () => {
111
116
  expect(getMaxOutputTokens("claude-3-5-haiku-20241022")).toBe(8_192);
112
117
  });
113
118
 
119
+ it("registers Claude Haiku 5.5 across Anthropic, Bedrock, and OpenRouter", () => {
120
+ expect(getModelInfo("claude-haiku-5-5")).toMatchObject({
121
+ name: "Claude Haiku 5.5",
122
+ provider: "anthropic",
123
+ contextLength: 1_000_000,
124
+ });
125
+ expect(getModelInfo("global.anthropic.claude-haiku-5-5")).toMatchObject({
126
+ name: "Claude Haiku 5.5 (Global)",
127
+ provider: "bedrock",
128
+ contextLength: 1_000_000,
129
+ });
130
+ expect(getModelInfo("anthropic/claude-haiku-5.5")).toMatchObject({
131
+ name: "Claude Haiku 5.5",
132
+ provider: "openrouter",
133
+ contextLength: 1_000_000,
134
+ });
135
+ });
136
+
114
137
  it("pins latest-tier Claude (4.6 + 4.7 + 4.8) to 128K output", () => {
115
138
  // These ship the 128K window; they MUST not fall through to the
116
139
  // generic `claude-opus-4-` / `claude-sonnet-4-` catch-alls (32K/64K)
@@ -261,7 +284,6 @@ describe("OpenAI reasoning effort support", () => {
261
284
  "high",
262
285
  "xhigh",
263
286
  "max",
264
- "ultra",
265
287
  ]);
266
288
  expect(modelSupportsOpenAIReasoning("gpt-5.6-terra")).toBe(true);
267
289
  expect(modelSupportsOpenAIReasoning("gpt-5.6-luna")).toBe(true);
@@ -274,12 +296,31 @@ describe("OpenAI reasoning effort support", () => {
274
296
  expect(modelSupportsOpenAIReasoning("o3-mini")).toBe(true);
275
297
  });
276
298
 
277
- it("does not apply reasoning effort to unsupported GPT-5 variants", () => {
278
- expect(modelSupportsOpenAIReasoning("gpt-5-nano")).toBe(false);
279
- expect(modelSupportsOpenAIReasoning("gpt-5.1-codex-mini")).toBe(false);
280
- expect(modelSupportsOpenAIReasoning("gpt-5.1-codex-max")).toBe(false);
281
- expect(modelSupportsOpenAIReasoning("gpt-5.3-codex")).toBe(false);
282
- expect(normalizeOpenAIReasoningEffort("gpt-5-nano")).toBeUndefined();
299
+ it("uses verified effort support for smaller and Codex models", () => {
300
+ expect(getOpenAIReasoningEfforts("gpt-5-nano")).toEqual([
301
+ "minimal",
302
+ "low",
303
+ "medium",
304
+ "high",
305
+ ]);
306
+ expect(getOpenAIReasoningEfforts("gpt-5.1-codex-mini")).toEqual([
307
+ "low",
308
+ "medium",
309
+ "high",
310
+ ]);
311
+ expect(getOpenAIReasoningEfforts("gpt-5.1-codex-max")).toEqual([
312
+ "low",
313
+ "medium",
314
+ "high",
315
+ "xhigh",
316
+ ]);
317
+ expect(getOpenAIReasoningEfforts("gpt-5.3-codex")).toEqual([
318
+ "low",
319
+ "medium",
320
+ "high",
321
+ "xhigh",
322
+ ]);
323
+ expect(modelSupportsOpenAIReasoning("gpt-future-99")).toBe(false);
283
324
  });
284
325
 
285
326
  it("defaults only supported reasoning models to medium", () => {
@@ -288,3 +329,134 @@ describe("OpenAI reasoning effort support", () => {
288
329
  );
289
330
  });
290
331
  });
332
+
333
+ describe("open-weight gateway models", () => {
334
+ it.each([
335
+ {
336
+ id: "xiaomi/mimo-v2.6-pro",
337
+ name: "MiMo-V2.6-Pro",
338
+ provider: "openrouter",
339
+ contextLength: 1_050_000,
340
+ maxOutputTokens: 131_072,
341
+ },
342
+ {
343
+ id: "concentrate:mimo-v2.6-pro",
344
+ name: "MiMo V2.6 Pro",
345
+ provider: "concentrate",
346
+ contextLength: 1_048_576,
347
+ maxOutputTokens: 131_072,
348
+ },
349
+ {
350
+ id: "moonshotai/kimi-k3",
351
+ name: "Kimi K3",
352
+ provider: "openrouter",
353
+ contextLength: 1_048_576,
354
+ maxOutputTokens: 131_072,
355
+ },
356
+ {
357
+ id: "concentrate:kimi-k3",
358
+ name: "Kimi K3",
359
+ provider: "concentrate",
360
+ contextLength: 1_048_576,
361
+ maxOutputTokens: 32_768,
362
+ },
363
+ {
364
+ id: "qwen/qwen3.8-max-0902",
365
+ name: "Qwen3.8 Max (0902)",
366
+ provider: "openrouter",
367
+ contextLength: 1_000_000,
368
+ maxOutputTokens: 131_072,
369
+ },
370
+ {
371
+ id: "qwen/qwen3.8-27b",
372
+ name: "Qwen3.8 27B",
373
+ provider: "openrouter",
374
+ contextLength: 1_000_000,
375
+ maxOutputTokens: 131_072,
376
+ },
377
+ {
378
+ id: "qwen/qwen3.8-flash",
379
+ name: "Qwen3.8 Flash",
380
+ provider: "openrouter",
381
+ contextLength: 1_000_000,
382
+ maxOutputTokens: 131_072,
383
+ },
384
+ {
385
+ id: "concentrate:qwen3.8-max",
386
+ name: "Qwen3.8 Max",
387
+ provider: "concentrate",
388
+ contextLength: 256_000,
389
+ maxOutputTokens: 32_768,
390
+ },
391
+ {
392
+ id: "concentrate:qwen3.8-27b",
393
+ name: "Qwen3.8 27B",
394
+ provider: "concentrate",
395
+ contextLength: 262_144,
396
+ maxOutputTokens: 32_768,
397
+ },
398
+ {
399
+ id: "deepseek/deepseek-v4.1-flash-1017",
400
+ name: "DeepSeek V4.1 Flash 1017",
401
+ provider: "openrouter",
402
+ contextLength: 1_048_576,
403
+ maxOutputTokens: 384_000,
404
+ },
405
+ {
406
+ id: "deepseek/deepseek-v4-pro-0813",
407
+ name: "DeepSeek V4 Pro 0813",
408
+ provider: "openrouter",
409
+ contextLength: 1_048_576,
410
+ maxOutputTokens: 384_000,
411
+ },
412
+ {
413
+ id: "concentrate:deepseek-v4-1-flash",
414
+ name: "DeepSeek V4.1 Flash",
415
+ provider: "concentrate",
416
+ contextLength: 1_000_000,
417
+ maxOutputTokens: 32_768,
418
+ },
419
+ {
420
+ id: "concentrate:deepseek-v4-1-flash-1017",
421
+ name: "DeepSeek V4.1 Flash 1017",
422
+ provider: "concentrate",
423
+ contextLength: 1_048_576,
424
+ maxOutputTokens: 131_072,
425
+ },
426
+ {
427
+ id: "concentrate:deepseek-v4-pro-0813",
428
+ name: "DeepSeek V4 Pro 0813",
429
+ provider: "concentrate",
430
+ contextLength: 1_048_576,
431
+ maxOutputTokens: 384_000,
432
+ },
433
+ ])("registers $id with its gateway limits", ({
434
+ id,
435
+ maxOutputTokens,
436
+ ...info
437
+ }) => {
438
+ expect(getModelInfo(id)).toMatchObject(info);
439
+ expect(getMaxOutputTokens(id)).toBe(maxOutputTokens);
440
+ });
441
+
442
+ it.each([
443
+ "concentrate:mimo-v2.6-pro",
444
+ "concentrate:kimi-k3",
445
+ "concentrate:qwen3.8-max",
446
+ "concentrate:qwen3.8-27b",
447
+ "concentrate:deepseek-v4-1-flash",
448
+ "concentrate:deepseek-v4-1-flash-1017",
449
+ "concentrate:deepseek-v4-pro-0813",
450
+ ])("offers every effort Concentrate can route for %s", (id) => {
451
+ expect(modelSupportsOpenAIReasoning(id)).toBe(true);
452
+ expect(getOpenAIReasoningEfforts(id)).toEqual([
453
+ "low",
454
+ "medium",
455
+ "high",
456
+ "xhigh",
457
+ "max",
458
+ ]);
459
+ expect(normalizeOpenAIReasoningEffort(id)).toBe("medium");
460
+ expect(normalizeOpenAIReasoningEffort(id, "max")).toBe("max");
461
+ });
462
+ });