@pensar/apex 2.6.1 → 2.7.0-canary.21cb83dc

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 (226) hide show
  1. package/build/{agent-yby11zaq.js → agent-68t2wzmc.js} +6 -6
  2. package/build/agent-jhekpern.js +13 -0
  3. package/build/{agent-ayreaj6z.js → agent-qvreg8a4.js} +5 -5
  4. package/build/{agent-runs-g37vkrjr.js → agent-runs-30dvbvmm.js} +18 -18
  5. package/build/agent-vybw255f.js +13 -0
  6. package/build/{apps-xn6km9yn.js → apps-na0y0992.js} +13 -13
  7. package/build/{auth-d105wc64.js → auth-dz2c5thx.js} +13 -13
  8. package/build/{blackboxAgent-s56sgx9b.js → blackboxAgent-2mwvaajb.js} +6 -6
  9. package/build/{blackboxPentest-ps78v3w7.js → blackboxPentest-s2mfze9p.js} +17 -12
  10. package/build/{cli-kb21qhvm.js → cli-1pmfnz1a.js} +4 -3
  11. package/build/{cli-ae8jqxqr.js → cli-563yntrb.js} +2 -2
  12. package/build/{cli-7eea4e2y.js → cli-5ab346kd.js} +6 -6
  13. package/build/{cli-1ve18npr.js → cli-72y76bpg.js} +3 -2
  14. package/build/{cli-fy6vh2a7.js → cli-98wmmphf.js} +1 -1
  15. package/build/{cli-c05y640n.js → cli-9jscnhtg.js} +1 -1
  16. package/build/{cli-3sp6nd8d.js → cli-9kk9tm58.js} +2 -2
  17. package/build/{cli-d37a42dc.js → cli-9pz9wjt2.js} +101 -18
  18. package/build/{cli-dpsw2fe6.js → cli-a6x4jm5a.js} +476 -193
  19. package/build/{cli-az3xq70v.js → cli-cfrdr25z.js} +1 -1
  20. package/build/{cli-ngq9dr8n.js → cli-fdrrbb9p.js} +3 -3
  21. package/build/{cli-k2nwxta0.js → cli-t09zj814.js} +2 -2
  22. package/build/{cli-qgz1q9g5.js → cli-t4q4t4dx.js} +223 -76
  23. package/build/{cli-xtc2t6me.js → cli-vdd1s3x9.js} +103 -39
  24. package/build/{cli-gx71jcke.js → cli-vn4cwac3.js} +1 -1
  25. package/build/{cli-65g6rcat.js → cli-w9nw2a4z.js} +1 -1
  26. package/build/{cli-n2en7j5b.js → cli-z7ysyrr4.js} +1 -1
  27. package/build/{cli-709rvekb.js → cli-zbbtr4sb.js} +4 -2
  28. package/build/cli.js +70 -40
  29. package/build/{config-zpqkd5cz.js → config-nkzm9arw.js} +1 -1
  30. package/build/{fastStrike-mcjdvwez.js → fastStrike-16jw1s61.js} +67 -15
  31. package/build/{fixes-kytx5dmj.js → fixes-6wxk22v5.js} +13 -13
  32. package/build/{index-4vqdgb42.js → index-36p56pcf.js} +1473 -1217
  33. package/build/{index-je6hcsqr.js → index-6nejymsc.js} +1 -1
  34. package/build/{index-fv64eb32.js → index-asm4y7e4.js} +2 -2
  35. package/build/{index-n65z6v7b.js → index-bfmn52za.js} +6 -6
  36. package/build/{index-qv4cc65p.js → index-bt4r13q7.js} +13 -13
  37. package/build/{index-dr3tg1w3.js → index-gfq1rp1q.js} +1 -1
  38. package/build/{index-wqetp6gq.js → index-j9pxgrzd.js} +12 -7
  39. package/build/{index-r9yjkfrv.js → index-n9kapn1x.js} +4 -4
  40. package/build/{index-ns4m4s01.js → index-tr6evhyg.js} +4 -4
  41. package/build/{issues-eb3q9d3a.js → issues-7rmf4ker.js} +13 -13
  42. package/build/{knowledge-fqsvtx0f.js → knowledge-s2sxh8nb.js} +13 -13
  43. package/build/lib/{chunk-1azdancq.js → chunk-0dnx79b1.js} +1 -1
  44. package/build/lib/{chunk-4hwzqthr.js → chunk-161ak2am.js} +3 -2
  45. package/build/lib/{chunk-z137x08b.js → chunk-1rqbhx4b.js} +7 -5
  46. package/build/lib/{chunk-1yct6cra.js → chunk-3hjvdf67.js} +1 -1
  47. package/build/lib/{chunk-r2dkp4tb.js → chunk-5j8pp0xf.js} +1 -1
  48. package/build/lib/{chunk-3c00n0cy.js → chunk-77se1d35.js} +8 -3
  49. package/build/lib/{chunk-yrnrbjes.js → chunk-7tft4b8b.js} +4 -2
  50. package/build/lib/{chunk-w66ce82a.js → chunk-8njjwwcw.js} +4 -2
  51. package/build/lib/{chunk-v3dk9j7k.js → chunk-aq9eyxas.js} +80 -21
  52. package/build/lib/{chunk-9tt2021m.js → chunk-d05cvnv8.js} +9 -9
  53. package/build/lib/{chunk-e8yy2k73.js → chunk-f3sq4896.js} +28 -2
  54. package/build/lib/{chunk-1fy8azee.js → chunk-fasqtjq1.js} +1 -1
  55. package/build/lib/{chunk-1jvwqnem.js → chunk-gm3b9j4y.js} +104 -40
  56. package/build/lib/{chunk-r4j8ybn9.js → chunk-hys7nemr.js} +87 -36
  57. package/build/lib/{chunk-7pwa92rn.js → chunk-j9668h16.js} +1 -1
  58. package/build/lib/{chunk-ba41jh41.js → chunk-kcw4cpr3.js} +418 -133
  59. package/build/lib/{chunk-z96pbjhd.js → chunk-m4swxy5r.js} +13 -6
  60. package/build/lib/{chunk-xhdpgpkm.js → chunk-mbsde9d0.js} +2 -2
  61. package/build/lib/{chunk-9yksqgzr.js → chunk-mpvjfvz0.js} +20 -13
  62. package/build/lib/{chunk-2qsq76xq.js → chunk-qkz5sm0d.js} +128 -34
  63. package/build/lib/{chunk-nhfax4gb.js → chunk-rfyjqybp.js} +1 -1
  64. package/build/lib/{chunk-mg03jdmt.js → chunk-vsnwzne5.js} +1 -1
  65. package/build/lib/{chunk-acgbyf5h.js → chunk-ydxhkgjw.js} +3 -2
  66. package/build/lib/{chunk-bb0ekhkw.js → chunk-z8kej0sp.js} +69 -18
  67. package/build/lib/core/agents/agentRuntime.d.ts +2 -1
  68. package/build/lib/core/agents/defineAgent.d.ts +3 -2
  69. package/build/lib/core/agents/offSecAgent/index.d.ts +3 -1
  70. package/build/lib/core/agents/offSecAgent/index.js +10 -8
  71. package/build/lib/core/agents/offSecAgent/prompt.d.ts +3 -1
  72. package/build/lib/core/agents/offSecAgent/subagentSpawner.d.ts +2 -1
  73. package/build/lib/core/agents/offSecAgent/subagentSpawner.js +5 -5
  74. package/build/lib/core/agents/offSecAgent/tools/index.js +4 -4
  75. package/build/lib/core/agents/offSecAgent/tools/listMemories.d.ts +11 -1
  76. package/build/lib/core/agents/offSecAgent/tools/reportError.d.ts +2 -1
  77. package/build/lib/core/agents/offSecAgent/tools/runPentestWorkflow.d.ts +20 -1
  78. package/build/lib/core/agents/offSecAgent/tools/spawnPentestAgent.d.ts +4 -7
  79. package/build/lib/core/agents/offSecAgent/tools/spawnPentestSwarm.d.ts +7 -1
  80. package/build/lib/core/agents/offSecAgent/tools/types.d.ts +2 -1
  81. package/build/lib/core/agents/offSecAgent/types.d.ts +7 -1
  82. package/build/lib/core/agents/specialized/attackSurface/blackboxAgent.js +9 -9
  83. package/build/lib/core/agents/specialized/attackSurface/schemas.js +9 -9
  84. package/build/lib/core/agents/specialized/authenticationAgent/index.js +10 -10
  85. package/build/lib/core/agents/specialized/codeAgent/agent.js +9 -9
  86. package/build/lib/core/agents/specialized/codeAgent/prompts.d.ts +1 -0
  87. package/build/lib/core/agents/specialized/findingJudge/agent.js +8 -8
  88. package/build/lib/core/agents/specialized/findingJudge/index.js +3 -3
  89. package/build/lib/core/agents/specialized/patching/index.js +10 -10
  90. package/build/lib/core/agents/specialized/pentest/agent.d.ts +18 -4
  91. package/build/lib/core/agents/specialized/pentest/agent.js +11 -9
  92. package/build/lib/core/agents/specialized/whiteboxAttackSurface/index.js +9 -9
  93. package/build/lib/core/ai/ai.d.ts +23 -6
  94. package/build/lib/core/ai/ai.js +2 -2
  95. package/build/lib/core/ai/index.js +2 -2
  96. package/build/lib/core/ai/models/claude-capabilities.d.ts +3 -2
  97. package/build/lib/core/ai/models/concentrate.d.ts +1 -0
  98. package/build/lib/core/ai/models/index.d.ts +1 -1
  99. package/build/lib/core/ai/providers/concentrate.d.ts +1 -0
  100. package/build/lib/core/ai/utils.d.ts +2 -0
  101. package/build/lib/core/ai/utils.js +4 -2
  102. package/build/lib/core/api/index.d.ts +3 -0
  103. package/build/lib/core/api/index.js +22 -19
  104. package/build/lib/core/api/targetedPentest.d.ts +1 -0
  105. package/build/lib/core/api/targetedPentest.js +10 -10
  106. package/build/lib/core/config/config.d.ts +3 -0
  107. package/build/lib/core/config/index.js +1 -1
  108. package/build/lib/core/findings/registry.js +3 -3
  109. package/build/lib/core/session/index.d.ts +12 -0
  110. package/build/lib/core/session/index.js +2 -2
  111. package/build/lib/core/session/persistence.d.ts +7 -0
  112. package/build/lib/core/session/persistence.js +7 -3
  113. package/build/lib/core/workflows/fastStrike.d.ts +3 -1
  114. package/build/lib/core/workflows/pentest.d.ts +8 -1
  115. package/build/lib/core/workflows/pentest.js +15 -15
  116. package/build/lib/core/workflows/seams.js +4 -4
  117. package/build/lib/core/workflows/whiteboxAttackSurface.js +11 -11
  118. package/build/lib/index.js +22 -22
  119. package/build/{logs-x62b7hk1.js → logs-70fyr848.js} +13 -13
  120. package/build/{native-rollout-evidence-c9yhfqcs.js → native-rollout-evidence-ntensvzb.js} +3 -3
  121. package/build/{offesecAgent-5whnpeta.js → offesecAgent-kk62tf60.js} +4 -4
  122. package/build/pentest-4jgznn7h.js +22 -0
  123. package/build/{pentests-e0f32aqx.js → pentests-3e41hj08.js} +13 -13
  124. package/build/recordedRunClient-w34kcch1.js +11 -0
  125. package/build/{targetedPentest-rcbs4nvp.js → targetedPentest-t9snxcj2.js} +18 -11
  126. package/build/{targets-xj9g3q0z.js → targets-1pyf21c7.js} +13 -13
  127. package/build/threatModel-48y5qjze.js +21 -0
  128. package/build/{uninstall-7s784q4a.js → uninstall-fbydd9t6.js} +1 -1
  129. package/build/{upload-4gvhwmvr.js → upload-sbhwe2fq.js} +4 -4
  130. package/build/{utils-cjrz64pc.js → utils-23903gcj.js} +3 -3
  131. package/package.json +1 -1
  132. package/src/cli.ts +48 -14
  133. package/src/core/agents/agentRuntime.test.ts +38 -0
  134. package/src/core/agents/agentRuntime.ts +5 -2
  135. package/src/core/agents/defineAgent.ts +3 -1
  136. package/src/core/agents/offSecAgent/index.ts +3 -0
  137. package/src/core/agents/offSecAgent/offensiveSecurityAgent.test.ts +92 -5
  138. package/src/core/agents/offSecAgent/offensiveSecurityAgent.ts +70 -31
  139. package/src/core/agents/offSecAgent/prompt.test.ts +46 -0
  140. package/src/core/agents/offSecAgent/prompt.ts +52 -8
  141. package/src/core/agents/offSecAgent/selectiveConstruction.publicPath.test.ts +38 -0
  142. package/src/core/agents/offSecAgent/selectiveConstruction.test.ts +64 -0
  143. package/src/core/agents/offSecAgent/subagentSpawner.ts +12 -1
  144. package/src/core/agents/offSecAgent/tools/delegateAuth.ts +1 -0
  145. package/src/core/agents/offSecAgent/tools/listMemories.test.ts +230 -0
  146. package/src/core/agents/offSecAgent/tools/listMemories.ts +73 -6
  147. package/src/core/agents/offSecAgent/tools/reportError.test.ts +6 -0
  148. package/src/core/agents/offSecAgent/tools/reportError.ts +10 -2
  149. package/src/core/agents/offSecAgent/tools/runAttackSurface.ts +2 -0
  150. package/src/core/agents/offSecAgent/tools/runPentestWorkflow.test.ts +57 -0
  151. package/src/core/agents/offSecAgent/tools/runPentestWorkflow.ts +19 -1
  152. package/src/core/agents/offSecAgent/tools/runWhiteboxScan.ts +5 -2
  153. package/src/core/agents/offSecAgent/tools/spawnCodingAgent.ts +1 -0
  154. package/src/core/agents/offSecAgent/tools/spawnPentestAgent.test.ts +72 -2
  155. package/src/core/agents/offSecAgent/tools/spawnPentestAgent.ts +32 -11
  156. package/src/core/agents/offSecAgent/tools/spawnPentestSwarm.ts +25 -5
  157. package/src/core/agents/offSecAgent/tools/threatModelGenerator.ts +1 -0
  158. package/src/core/agents/offSecAgent/tools/types.ts +6 -1
  159. package/src/core/agents/offSecAgent/tools/updateTask.ts +5 -1
  160. package/src/core/agents/offSecAgent/tools/whiteboxCandidates.ts +11 -4
  161. package/src/core/agents/offSecAgent/types.ts +9 -0
  162. package/src/core/agents/specialized/codeAgent/agent.test.ts +7 -0
  163. package/src/core/agents/specialized/codeAgent/agent.ts +9 -2
  164. package/src/core/agents/specialized/codeAgent/prompts.ts +6 -0
  165. package/src/core/agents/specialized/pentest/agent.test.ts +116 -0
  166. package/src/core/agents/specialized/pentest/agent.ts +178 -49
  167. package/src/core/agents/specialized/pentest/results.test.ts +59 -4
  168. package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.ts +1 -0
  169. package/src/core/ai/ai.operator-context.bench.test.ts +1408 -0
  170. package/src/core/ai/ai.recorded-context.test.ts +74 -0
  171. package/src/core/ai/ai.test.ts +54 -0
  172. package/src/core/ai/ai.ts +312 -82
  173. package/src/core/ai/compactionTelemetry.test.ts +2 -2
  174. package/src/core/ai/models/anthropic.ts +6 -0
  175. package/src/core/ai/models/bedrock.ts +18 -0
  176. package/src/core/ai/models/claude-capabilities.ts +29 -8
  177. package/src/core/ai/models/concentrate.ts +52 -0
  178. package/src/core/ai/models/index.ts +25 -4
  179. package/src/core/ai/models/models.test.ts +154 -0
  180. package/src/core/ai/models/openrouter.ts +43 -1
  181. package/src/core/ai/providers/anthropic-models.test.ts +60 -1
  182. package/src/core/ai/providers/bedrock-models.test.ts +81 -0
  183. package/src/core/ai/providers/concentrate.test.ts +31 -0
  184. package/src/core/ai/providers/concentrate.ts +11 -0
  185. package/src/core/ai/providers/gateway-models.test.ts +16 -6
  186. package/src/core/ai/utils.native-rollout-evidence.test.ts +1 -0
  187. package/src/core/ai/utils.ts +189 -143
  188. package/src/core/api/blackboxPentest.ts +9 -1
  189. package/src/core/api/index.ts +9 -0
  190. package/src/core/api/targetedPentest.ts +19 -5
  191. package/src/core/config/config.test.ts +35 -0
  192. package/src/core/config/config.ts +5 -0
  193. package/src/core/observability/telemetry.test.ts +2 -3
  194. package/src/core/session/index.ts +5 -0
  195. package/src/core/session/persistence.test.ts +62 -0
  196. package/src/core/session/persistence.ts +56 -0
  197. package/src/core/workflows/fastStrike.seams.test.ts +149 -0
  198. package/src/core/workflows/fastStrike.test.ts +18 -1
  199. package/src/core/workflows/fastStrike.ts +103 -11
  200. package/src/core/workflows/pentest.test.ts +104 -0
  201. package/src/core/workflows/pentest.ts +110 -7
  202. package/src/core/workflows/seams.test.ts +13 -0
  203. package/src/core/workflows/seams.ts +5 -1
  204. package/src/core/workflows/whiteboxAttackSurface.ts +1 -0
  205. package/src/tui/components/chat/loading-indicator.tsx +20 -4
  206. package/src/tui/components/chat/message-list.tsx +1 -0
  207. package/src/tui/components/commands/__tests__/advanced-settings-lifecycle.tsx +176 -0
  208. package/src/tui/components/commands/advanced-settings.test.ts +28 -0
  209. package/src/tui/components/commands/advanced-settings.tsx +237 -19
  210. package/src/tui/components/operator-dashboard/index.tsx +46 -9
  211. package/src/tui/components/operator-dashboard/logic.test.ts +106 -0
  212. package/src/tui/components/operator-dashboard/logic.ts +42 -2
  213. package/src/tui/components/operator-dashboard/run-events.test.ts +3 -1
  214. package/src/tui/components/shared/__tests__/tool-renderer-lifecycle.tsx +18 -0
  215. package/src/tui/components/shared/action-label.test.ts +9 -0
  216. package/src/tui/components/shared/compacting-icon.tsx +18 -0
  217. package/src/tui/components/shared/index.ts +1 -0
  218. package/src/tui/components/shared/tool-registry.ts +1 -0
  219. package/src/tui/components/shared/tool-renderer.tsx +21 -7
  220. package/src/tui/context/route.tsx +1 -0
  221. package/src/tui/keybindings/registry.ts +2 -0
  222. package/build/agent-6w7egcf1.js +0 -13
  223. package/build/agent-9zx75feh.js +0 -13
  224. package/build/pentest-e61hc38s.js +0 -22
  225. package/build/recordedRunClient-dp032c32.js +0 -11
  226. package/build/threatModel-jkxfa40s.js +0 -21
@@ -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)
@@ -288,3 +311,134 @@ describe("OpenAI reasoning effort support", () => {
288
311
  );
289
312
  });
290
313
  });
314
+
315
+ describe("open-weight gateway models", () => {
316
+ it.each([
317
+ {
318
+ id: "xiaomi/mimo-v2.6-pro",
319
+ name: "MiMo-V2.6-Pro",
320
+ provider: "openrouter",
321
+ contextLength: 1_050_000,
322
+ maxOutputTokens: 131_072,
323
+ },
324
+ {
325
+ id: "concentrate:mimo-v2.6-pro",
326
+ name: "MiMo V2.6 Pro",
327
+ provider: "concentrate",
328
+ contextLength: 1_048_576,
329
+ maxOutputTokens: 131_072,
330
+ },
331
+ {
332
+ id: "moonshotai/kimi-k3",
333
+ name: "Kimi K3",
334
+ provider: "openrouter",
335
+ contextLength: 1_048_576,
336
+ maxOutputTokens: 131_072,
337
+ },
338
+ {
339
+ id: "concentrate:kimi-k3",
340
+ name: "Kimi K3",
341
+ provider: "concentrate",
342
+ contextLength: 1_048_576,
343
+ maxOutputTokens: 32_768,
344
+ },
345
+ {
346
+ id: "qwen/qwen3.8-max-0902",
347
+ name: "Qwen3.8 Max (0902)",
348
+ provider: "openrouter",
349
+ contextLength: 1_000_000,
350
+ maxOutputTokens: 131_072,
351
+ },
352
+ {
353
+ id: "qwen/qwen3.8-27b",
354
+ name: "Qwen3.8 27B",
355
+ provider: "openrouter",
356
+ contextLength: 1_000_000,
357
+ maxOutputTokens: 131_072,
358
+ },
359
+ {
360
+ id: "qwen/qwen3.8-flash",
361
+ name: "Qwen3.8 Flash",
362
+ provider: "openrouter",
363
+ contextLength: 1_000_000,
364
+ maxOutputTokens: 131_072,
365
+ },
366
+ {
367
+ id: "concentrate:qwen3.8-max",
368
+ name: "Qwen3.8 Max",
369
+ provider: "concentrate",
370
+ contextLength: 256_000,
371
+ maxOutputTokens: 32_768,
372
+ },
373
+ {
374
+ id: "concentrate:qwen3.8-27b",
375
+ name: "Qwen3.8 27B",
376
+ provider: "concentrate",
377
+ contextLength: 262_144,
378
+ maxOutputTokens: 32_768,
379
+ },
380
+ {
381
+ id: "deepseek/deepseek-v4.1-flash-1017",
382
+ name: "DeepSeek V4.1 Flash 1017",
383
+ provider: "openrouter",
384
+ contextLength: 1_048_576,
385
+ maxOutputTokens: 384_000,
386
+ },
387
+ {
388
+ id: "deepseek/deepseek-v4-pro-0813",
389
+ name: "DeepSeek V4 Pro 0813",
390
+ provider: "openrouter",
391
+ contextLength: 1_048_576,
392
+ maxOutputTokens: 384_000,
393
+ },
394
+ {
395
+ id: "concentrate:deepseek-v4-1-flash",
396
+ name: "DeepSeek V4.1 Flash",
397
+ provider: "concentrate",
398
+ contextLength: 1_000_000,
399
+ maxOutputTokens: 32_768,
400
+ },
401
+ {
402
+ id: "concentrate:deepseek-v4-1-flash-1017",
403
+ name: "DeepSeek V4.1 Flash 1017",
404
+ provider: "concentrate",
405
+ contextLength: 1_048_576,
406
+ maxOutputTokens: 131_072,
407
+ },
408
+ {
409
+ id: "concentrate:deepseek-v4-pro-0813",
410
+ name: "DeepSeek V4 Pro 0813",
411
+ provider: "concentrate",
412
+ contextLength: 1_048_576,
413
+ maxOutputTokens: 384_000,
414
+ },
415
+ ])("registers $id with its gateway limits", ({
416
+ id,
417
+ maxOutputTokens,
418
+ ...info
419
+ }) => {
420
+ expect(getModelInfo(id)).toMatchObject(info);
421
+ expect(getMaxOutputTokens(id)).toBe(maxOutputTokens);
422
+ });
423
+
424
+ it.each([
425
+ "concentrate:mimo-v2.6-pro",
426
+ "concentrate:kimi-k3",
427
+ "concentrate:qwen3.8-max",
428
+ "concentrate:qwen3.8-27b",
429
+ "concentrate:deepseek-v4-1-flash",
430
+ "concentrate:deepseek-v4-1-flash-1017",
431
+ "concentrate:deepseek-v4-pro-0813",
432
+ ])("offers every effort Concentrate can route for %s", (id) => {
433
+ expect(modelSupportsOpenAIReasoning(id)).toBe(true);
434
+ expect(getOpenAIReasoningEfforts(id)).toEqual([
435
+ "low",
436
+ "medium",
437
+ "high",
438
+ "xhigh",
439
+ "max",
440
+ ]);
441
+ expect(normalizeOpenAIReasoningEffort(id)).toBe("medium");
442
+ expect(normalizeOpenAIReasoningEffort(id, "max")).toBe("max");
443
+ });
444
+ });
@@ -3,6 +3,12 @@
3
3
 
4
4
  import type { ModelInfo } from "../ai";
5
5
  export const OPENROUTER_MODELS: ModelInfo[] = [
6
+ {
7
+ id: "anthropic/claude-haiku-5.5",
8
+ name: "Claude Haiku 5.5",
9
+ provider: "openrouter",
10
+ contextLength: 1000000,
11
+ },
6
12
  {
7
13
  id: "anthropic/claude-sonnet-5.5",
8
14
  name: "Claude Sonnet 5.5",
@@ -177,12 +183,24 @@ export const OPENROUTER_MODELS: ModelInfo[] = [
177
183
  provider: "openrouter",
178
184
  contextLength: 128000,
179
185
  },
186
+ {
187
+ id: "deepseek/deepseek-v4.1-flash-1017",
188
+ name: "DeepSeek V4.1 Flash 1017",
189
+ provider: "openrouter",
190
+ contextLength: 1048576,
191
+ },
180
192
  {
181
193
  id: "deepseek/deepseek-v4.1-flash",
182
194
  name: "DeepSeek V4.1 Flash",
183
195
  provider: "openrouter",
184
196
  contextLength: 1048576,
185
197
  },
198
+ {
199
+ id: "deepseek/deepseek-v4-pro-0813",
200
+ name: "DeepSeek V4 Pro 0813",
201
+ provider: "openrouter",
202
+ contextLength: 1048576,
203
+ },
186
204
  {
187
205
  id: "deepseek/deepseek-chat",
188
206
  name: "DeepSeek Chat",
@@ -217,7 +235,7 @@ export const OPENROUTER_MODELS: ModelInfo[] = [
217
235
  id: "moonshotai/kimi-k3",
218
236
  name: "Kimi K3",
219
237
  provider: "openrouter",
220
- contextLength: 1000000,
238
+ contextLength: 1048576,
221
239
  },
222
240
  {
223
241
  id: "moonshot/kimi-k2-turbo",
@@ -249,6 +267,24 @@ export const OPENROUTER_MODELS: ModelInfo[] = [
249
267
  provider: "openrouter",
250
268
  contextLength: 1048576,
251
269
  },
270
+ {
271
+ id: "qwen/qwen3.8-max-0902",
272
+ name: "Qwen3.8 Max (0902)",
273
+ provider: "openrouter",
274
+ contextLength: 1000000,
275
+ },
276
+ {
277
+ id: "qwen/qwen3.8-27b",
278
+ name: "Qwen3.8 27B",
279
+ provider: "openrouter",
280
+ contextLength: 1000000,
281
+ },
282
+ {
283
+ id: "qwen/qwen3.8-flash",
284
+ name: "Qwen3.8 Flash",
285
+ provider: "openrouter",
286
+ contextLength: 1000000,
287
+ },
252
288
  {
253
289
  id: "qwen/qwen3-8b",
254
290
  name: "Qwen 3 8B Thinking",
@@ -315,6 +351,12 @@ export const OPENROUTER_MODELS: ModelInfo[] = [
315
351
  provider: "openrouter",
316
352
  contextLength: 256000,
317
353
  },
354
+ {
355
+ id: "xiaomi/mimo-v2.6-pro",
356
+ name: "MiMo-V2.6-Pro",
357
+ provider: "openrouter",
358
+ contextLength: 1050000,
359
+ },
318
360
  {
319
361
  id: "z-ai/glm-4.7-flash",
320
362
  name: "GLM 4.7 Flash",
@@ -12,12 +12,14 @@ import { getMaxOutputTokens } from "../models";
12
12
  import { getProviderModel } from "../utils";
13
13
 
14
14
  const boundModels = new Set([
15
+ "claude-haiku-5-5",
15
16
  "claude-sonnet-5-5",
16
17
  "claude-opus-5-5",
17
18
  "claude-fable-5-1",
18
19
  ]);
19
20
 
20
21
  const models = [
22
+ { id: "claude-haiku-5-5", required: false },
21
23
  { id: "claude-sonnet-5-5", required: true },
22
24
  { id: "claude-opus-5-5", required: true },
23
25
  { id: "claude-fable-5-1", required: true },
@@ -105,7 +107,10 @@ describe.each(models)("Anthropic $id", ({ id, required }) => {
105
107
  expect(body).toMatchObject({
106
108
  model: id,
107
109
  max_tokens: 128_000,
108
- output_config: { effort: "medium", format: { type: "json_schema" } },
110
+ output_config: {
111
+ ...(enableThinking || required ? { effort: "medium" } : {}),
112
+ format: { type: "json_schema" },
113
+ },
109
114
  thinking: { type: enableThinking || required ? "adaptive" : "disabled" },
110
115
  });
111
116
  expect(body.temperature).toBeUndefined();
@@ -287,3 +292,57 @@ describe.each(models)("Anthropic $id", ({ id, required }) => {
287
292
  );
288
293
  });
289
294
  });
295
+
296
+ describe("Anthropic Claude Haiku 5.5 effort", () => {
297
+ it.each([
298
+ "xhigh",
299
+ "max",
300
+ ] as const)("sends the %s effort with adaptive thinking", async (effort) => {
301
+ const fetchMock = vi.fn(
302
+ async (_input: RequestInfo | URL, _init?: RequestInit) =>
303
+ completion("claude-haiku-5-5"),
304
+ );
305
+ vi.stubGlobal("fetch", fetchMock);
306
+
307
+ await getProviderModel("claude-haiku-5-5", {
308
+ anthropicAPIKey: "test-key",
309
+ }).doGenerate({
310
+ prompt: [{ role: "user", content: [{ type: "text", text: "Check" }] }],
311
+ providerOptions: buildReasoningProviderOptions("claude-haiku-5-5", {
312
+ enableThinking: true,
313
+ thinkingEffort: effort,
314
+ }),
315
+ });
316
+
317
+ const call = fetchMock.mock.calls[0];
318
+ if (!call) throw new Error("Expected a Claude request");
319
+ expect(JSON.parse(String(call[1]?.body))).toMatchObject({
320
+ thinking: { type: "adaptive" },
321
+ output_config: { effort },
322
+ });
323
+ });
324
+
325
+ it("uses the documented medium effort by default", async () => {
326
+ const fetchMock = vi.fn(
327
+ async (_input: RequestInfo | URL, _init?: RequestInit) =>
328
+ completion("claude-haiku-5-5"),
329
+ );
330
+ vi.stubGlobal("fetch", fetchMock);
331
+
332
+ await getProviderModel("claude-haiku-5-5", {
333
+ anthropicAPIKey: "test-key",
334
+ }).doGenerate({
335
+ prompt: [{ role: "user", content: [{ type: "text", text: "Check" }] }],
336
+ providerOptions: buildReasoningProviderOptions("claude-haiku-5-5", {
337
+ enableThinking: true,
338
+ }),
339
+ });
340
+
341
+ const call = fetchMock.mock.calls[0];
342
+ if (!call) throw new Error("Expected a Claude request");
343
+ expect(JSON.parse(String(call[1]?.body))).toMatchObject({
344
+ thinking: { type: "adaptive" },
345
+ output_config: { effort: "medium" },
346
+ });
347
+ });
348
+ });
@@ -0,0 +1,81 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { buildReasoningProviderOptions } from "../ai";
3
+ import { getProviderModel } from "../utils";
4
+
5
+ function completion() {
6
+ return Response.json({
7
+ output: {
8
+ message: {
9
+ role: "assistant",
10
+ content: [{ text: "ok" }],
11
+ },
12
+ },
13
+ stopReason: "end_turn",
14
+ usage: {
15
+ inputTokens: 12,
16
+ outputTokens: 2,
17
+ totalTokens: 14,
18
+ },
19
+ });
20
+ }
21
+
22
+ afterEach(() => vi.unstubAllGlobals());
23
+
24
+ describe("Bedrock Claude Haiku 5.5", () => {
25
+ it("sends adaptive max effort to the global inference profile", async () => {
26
+ const fetchMock = vi.fn(
27
+ async (_input: RequestInfo | URL, _init?: RequestInit) => completion(),
28
+ );
29
+ vi.stubGlobal("fetch", fetchMock);
30
+
31
+ await getProviderModel("global.anthropic.claude-haiku-5-5", {
32
+ bedrock: { apiKey: "test-key", region: "us-east-1" },
33
+ }).doGenerate({
34
+ prompt: [{ role: "user", content: [{ type: "text", text: "Check" }] }],
35
+ providerOptions: buildReasoningProviderOptions(
36
+ "global.anthropic.claude-haiku-5-5",
37
+ { enableThinking: true, thinkingEffort: "max" },
38
+ ),
39
+ });
40
+
41
+ const call = fetchMock.mock.calls[0];
42
+ if (!call) throw new Error("Expected a Bedrock request");
43
+ expect(String(call[0])).toContain(
44
+ "/model/global.anthropic.claude-haiku-5-5/converse",
45
+ );
46
+ expect(new Headers(call[1]?.headers).get("authorization")).toBe(
47
+ "Bearer test-key",
48
+ );
49
+ expect(JSON.parse(String(call[1]?.body))).toMatchObject({
50
+ additionalModelRequestFields: {
51
+ thinking: { type: "adaptive", display: "summarized" },
52
+ output_config: { effort: "max" },
53
+ },
54
+ });
55
+ });
56
+
57
+ it("forwards thinking disabled explicitly", async () => {
58
+ const fetchMock = vi.fn(
59
+ async (_input: RequestInfo | URL, _init?: RequestInit) => completion(),
60
+ );
61
+ vi.stubGlobal("fetch", fetchMock);
62
+
63
+ await getProviderModel("global.anthropic.claude-haiku-5-5", {
64
+ bedrock: { apiKey: "test-key", region: "us-east-1" },
65
+ }).doGenerate({
66
+ prompt: [{ role: "user", content: [{ type: "text", text: "Check" }] }],
67
+ providerOptions: buildReasoningProviderOptions(
68
+ "global.anthropic.claude-haiku-5-5",
69
+ { enableThinking: false },
70
+ ),
71
+ });
72
+
73
+ const call = fetchMock.mock.calls[0];
74
+ if (!call) throw new Error("Expected a Bedrock request");
75
+ expect(JSON.parse(String(call[1]?.body))).toMatchObject({
76
+ additionalModelRequestFields: {
77
+ thinking: { type: "disabled" },
78
+ },
79
+ });
80
+ });
81
+ });
@@ -79,6 +79,37 @@ describe("createConcentrateModel", () => {
79
79
  });
80
80
  });
81
81
 
82
+ it.each([
83
+ "mimo-v2.6-pro",
84
+ "kimi-k3",
85
+ "qwen3.8-max",
86
+ "qwen3.8-27b",
87
+ "deepseek-v4-1-flash",
88
+ "deepseek-v4-1-flash-1017",
89
+ "deepseek-v4-pro-0813",
90
+ ])("forces reasoning on the %s Responses request", async (slug) => {
91
+ const fetchMock = vi.fn(
92
+ async (_input: RequestInfo | URL, _init?: RequestInit) =>
93
+ completedResponse(),
94
+ );
95
+ const model = createConcentrateModel(`concentrate:${slug}`, {
96
+ apiKey: "sk-cn-test",
97
+ fetch: fetchMock,
98
+ });
99
+
100
+ await model.doGenerate({
101
+ prompt: [{ role: "user", content: [{ type: "text", text: "Say ok" }] }],
102
+ providerOptions: { openai: { reasoningEffort: "max" } },
103
+ } satisfies LanguageModelV3CallOptions);
104
+
105
+ const body = JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body));
106
+ expect(body).toMatchObject({
107
+ model: slug,
108
+ store: false,
109
+ reasoning: { effort: "max", summary: "auto" },
110
+ });
111
+ });
112
+
82
113
  it("fails before constructing a request when the key is missing", () => {
83
114
  expect(() =>
84
115
  createConcentrateModel("concentrate:glm-5.3", { apiKey: " " }),
@@ -9,6 +9,16 @@ import { getClaudeCapabilities } from "../models";
9
9
 
10
10
  export const CONCENTRATE_BASE_URL = "https://api.concentrate.ai/v1";
11
11
  export const CONCENTRATE_GLM_5_3_MODEL_ID = "concentrate:glm-5.3";
12
+ // Concentrate routes each reasoning effort only to upstreams that accept it.
13
+ export const CONCENTRATE_REASONING_MODEL_IDS: ReadonlySet<string> = new Set([
14
+ "concentrate:mimo-v2.6-pro",
15
+ "concentrate:kimi-k3",
16
+ "concentrate:qwen3.8-max",
17
+ "concentrate:qwen3.8-27b",
18
+ "concentrate:deepseek-v4-1-flash",
19
+ "concentrate:deepseek-v4-1-flash-1017",
20
+ "concentrate:deepseek-v4-pro-0813",
21
+ ]);
12
22
 
13
23
  const RETRYABLE_STATUSES = new Set([424, 429, 500, 503, 504]);
14
24
 
@@ -139,6 +149,7 @@ export function createConcentrateModel(
139
149
  return withConcentrateDefaults(
140
150
  concentrate.responses(upstreamModelId),
141
151
  modelId === CONCENTRATE_GLM_5_3_MODEL_ID ||
152
+ CONCENTRATE_REASONING_MODEL_IDS.has(modelId) ||
142
153
  /^gpt-6[.-]/.test(upstreamModelId) ||
143
154
  !!getClaudeCapabilities(modelId),
144
155
  );