@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
@@ -0,0 +1,500 @@
1
+ import type { JSONValue } from "@ai-sdk/provider";
2
+ import {
3
+ type CustomProviders,
4
+ parseCustomModelId,
5
+ resolveCustomModel,
6
+ } from "../config/customProviders";
7
+ import type { OpenAIReasoningEffort, ThinkingEffort } from "./ai";
8
+ import { getClaudeCapabilities, getModelInfo } from "./models";
9
+ import openRouterReasoning from "./models/openrouter-reasoning.json";
10
+ import {
11
+ CONCENTRATE_GLM_5_3_MODEL_ID,
12
+ CONCENTRATE_REASONING_MODEL_IDS,
13
+ } from "./providers/concentrate";
14
+ import type { AIAuthConfig } from "./utils";
15
+
16
+ export const REASONING_SETTINGS = [
17
+ "default",
18
+ "enabled",
19
+ "disabled",
20
+ "none",
21
+ "minimal",
22
+ "low",
23
+ "medium",
24
+ "high",
25
+ "xhigh",
26
+ "max",
27
+ ] as const;
28
+ export type ReasoningSetting = (typeof REASONING_SETTINGS)[number];
29
+ export type ReasoningEffort = Exclude<
30
+ ReasoningSetting,
31
+ "default" | "enabled" | "disabled"
32
+ >;
33
+
34
+ export type ReasoningInput = {
35
+ reasoning?: ReasoningSetting;
36
+ enableThinking?: boolean;
37
+ thinkingEffort?: ThinkingEffort | null;
38
+ openAIReasoningEffort?: OpenAIReasoningEffort | null;
39
+ };
40
+
41
+ export function resolveReasoningSetting(
42
+ model: string,
43
+ input: ReasoningInput & { customProviders?: CustomProviders },
44
+ defaults: "legacy" | "model" = "legacy",
45
+ ): ReasoningSetting | undefined {
46
+ if (input.reasoning !== undefined)
47
+ return validateReasoningSetting(
48
+ model,
49
+ input.reasoning,
50
+ input.customProviders,
51
+ );
52
+ const cap = getReasoningCapabilities(model, input.customProviders);
53
+ if (cap.control === "none") return undefined;
54
+ if (
55
+ (cap.thinking || model.startsWith("anthropic/claude-")) &&
56
+ cap.transport !== "google"
57
+ ) {
58
+ if (
59
+ defaults === "model" &&
60
+ input.enableThinking === undefined &&
61
+ input.thinkingEffort == null
62
+ )
63
+ return "default";
64
+ if (!input.enableThinking && !cap.alwaysOn)
65
+ return cap.efforts.includes("none") ? "none" : "disabled";
66
+ const effort =
67
+ input.thinkingEffort ??
68
+ getClaudeCapabilities(model)?.defaultEffort ??
69
+ cap.defaultEffort;
70
+ return effort && cap.efforts.includes(effort)
71
+ ? effort
72
+ : cap.control === "toggle"
73
+ ? "enabled"
74
+ : "default";
75
+ }
76
+ // Legacy controls did not override Google or custom endpoint defaults.
77
+ if (cap.transport === "google" || model.startsWith("custom:"))
78
+ return undefined;
79
+ const effort = input.openAIReasoningEffort;
80
+ if (defaults === "model" && effort == null) return "default";
81
+ if (model === CONCENTRATE_GLM_5_3_MODEL_ID && effort)
82
+ return effort === "none" || effort === "low"
83
+ ? "low"
84
+ : effort === "medium" || effort === "high"
85
+ ? "high"
86
+ : "max";
87
+ const value = effort === "ultra" ? "max" : effort;
88
+ return value && cap.efforts.includes(value)
89
+ ? value
90
+ : (cap.defaultEffort ?? "default");
91
+ }
92
+
93
+ export function getModelSettings(
94
+ input: ReasoningInput & { model?: string; authConfig?: AIAuthConfig },
95
+ ): { model: string; authConfig?: AIAuthConfig; reasoning?: ReasoningSetting } {
96
+ if (!input.model) throw new Error("Model settings require a model.");
97
+ return {
98
+ model: input.model,
99
+ authConfig: input.authConfig,
100
+ reasoning: resolveReasoningSetting(input.model, {
101
+ ...input,
102
+ customProviders: input.authConfig?.customProviders,
103
+ }),
104
+ };
105
+ }
106
+
107
+ type Transport = "openai" | "openrouter" | "anthropic" | "bedrock" | "google";
108
+ export type ReasoningCapabilities = {
109
+ control: "effort" | "toggle" | "none";
110
+ efforts: readonly ReasoningEffort[];
111
+ defaultEffort?: ReasoningEffort;
112
+ defaultEnabled?: boolean;
113
+ canDisable: boolean;
114
+ transport?: Transport;
115
+ thinking?: "adaptive" | "budget";
116
+ alwaysOn?: boolean;
117
+ };
118
+
119
+ export type ReasoningProviderOptions = Record<
120
+ string,
121
+ Record<string, JSONValue | undefined>
122
+ >;
123
+ const UNKNOWN: ReasoningCapabilities = {
124
+ control: "none",
125
+ efforts: [],
126
+ canDisable: false,
127
+ };
128
+ const BASIC = ["low", "medium", "high"] as const;
129
+ const EXTENDED = [...BASIC, "xhigh", "max"] as const;
130
+
131
+ function effort(
132
+ transport: Transport,
133
+ efforts: readonly ReasoningEffort[],
134
+ defaultEffort: ReasoningEffort = "medium",
135
+ ): ReasoningCapabilities {
136
+ return {
137
+ control: "effort",
138
+ transport,
139
+ efforts,
140
+ defaultEffort,
141
+ canDisable: efforts.includes("none"),
142
+ };
143
+ }
144
+
145
+ // Reviewed against the provider documentation linked in docs/model-reasoning.md.
146
+ export function getReasoningCapabilities(
147
+ modelId: string,
148
+ customProviders?: CustomProviders,
149
+ ): ReasoningCapabilities {
150
+ if (parseCustomModelId(modelId)) {
151
+ let resolved: ReturnType<typeof resolveCustomModel>;
152
+ try {
153
+ resolved = resolveCustomModel(modelId, customProviders);
154
+ } catch {
155
+ // Removed endpoint configuration must not break the model picker.
156
+ return UNKNOWN;
157
+ }
158
+ const { provider, model } = resolved;
159
+ if (
160
+ provider.apiType === "responses" &&
161
+ model.supportedReasoningEfforts?.length
162
+ ) {
163
+ return {
164
+ ...effort("openai", model.supportedReasoningEfforts),
165
+ defaultEffort: model.reasoningEffort,
166
+ };
167
+ }
168
+ return UNKNOWN;
169
+ }
170
+ const { provider } = getModelInfo(modelId);
171
+ if (
172
+ provider === "local" ||
173
+ provider === "hoonify" ||
174
+ provider === "inception"
175
+ )
176
+ return UNKNOWN;
177
+ if (provider === "openrouter") {
178
+ const metadata = (
179
+ openRouterReasoning.models as Record<
180
+ string,
181
+ {
182
+ mandatory?: boolean;
183
+ default_enabled?: boolean;
184
+ supported_efforts?: string[] | null;
185
+ default_effort?: string;
186
+ } | null
187
+ >
188
+ )[modelId];
189
+ if (!metadata) return UNKNOWN;
190
+ const allowed: readonly ReasoningEffort[] = [
191
+ "none",
192
+ "minimal",
193
+ ...EXTENDED,
194
+ ];
195
+ const efforts = allowed.filter(
196
+ (value) =>
197
+ (metadata.supported_efforts === null ||
198
+ metadata.supported_efforts?.includes(value)) &&
199
+ !(metadata.mandatory && value === "none"),
200
+ );
201
+ return {
202
+ control: efforts.length
203
+ ? "effort"
204
+ : metadata.mandatory
205
+ ? "none"
206
+ : "toggle",
207
+ transport: "openrouter",
208
+ efforts,
209
+ canDisable: !metadata.mandatory,
210
+ alwaysOn: metadata.mandatory,
211
+ defaultEnabled: metadata.default_enabled,
212
+ ...(getClaudeCapabilities(modelId)
213
+ ? { thinking: "adaptive" as const }
214
+ : {}),
215
+ defaultEffort: efforts.includes(
216
+ metadata.default_effort as ReasoningEffort,
217
+ )
218
+ ? (metadata.default_effort as ReasoningEffort)
219
+ : undefined,
220
+ };
221
+ }
222
+ const native = modelId
223
+ .replace(/^(?:pensar:|concentrate:|bedrock-mantle:)/, "")
224
+ .replace(/^(?:(?:us|global|eu|au|jp|ap)\.)?anthropic[./]/, "")
225
+ .replace(/^(?:openai[/.]|google\/)/, "")
226
+ .replace(/-\d{4}-\d{2}-\d{2}$/, "")
227
+ .replace(/-\d{8}(?:-v\d+:\d+)?$/, "")
228
+ .replace(/-v\d+(?::\d+)?$/, "");
229
+ const claudeId = native.replace(/(\d)\.(\d)/g, "$1-$2");
230
+ const claude = getClaudeCapabilities(modelId);
231
+ const adaptive =
232
+ claude || /^(?:claude-opus-4-[678]|claude-sonnet-4-6)$/.test(claudeId);
233
+ const budget =
234
+ /^(?:claude-opus-4(?:-1|-5)?|claude-sonnet-4(?:-5)?|claude-haiku-4-5|claude-3-7-sonnet)$/.test(
235
+ claudeId,
236
+ );
237
+ if (adaptive || budget) {
238
+ const transport: Transport =
239
+ provider === "concentrate"
240
+ ? "openai"
241
+ : provider === "bedrock"
242
+ ? "bedrock"
243
+ : "anthropic";
244
+ const efforts = adaptive
245
+ ? /^claude-(?:opus|sonnet)-4-6$/.test(claudeId)
246
+ ? ([...BASIC, "max"] as const)
247
+ : EXTENDED
248
+ : claudeId === "claude-opus-4-5"
249
+ ? BASIC
250
+ : [];
251
+ // Concentrate's Responses route has no manual thinking-budget channel.
252
+ if (budget && transport === "openai") return UNKNOWN;
253
+ return {
254
+ control: efforts.length ? "effort" : "toggle",
255
+ efforts,
256
+ defaultEffort: claude?.defaultEffort ?? "high",
257
+ canDisable: !claude?.alwaysOnThinking,
258
+ alwaysOn: claude?.alwaysOnThinking,
259
+ defaultEnabled: !!claude,
260
+ transport,
261
+ thinking: adaptive ? "adaptive" : "budget",
262
+ };
263
+ }
264
+ if (provider === "google") {
265
+ if (["gemini-3-pro-preview"].includes(native))
266
+ return effort("google", ["low", "high"], "high");
267
+ if (
268
+ [
269
+ "gemini-3.1-pro-preview",
270
+ "gemini-3.1-pro-preview-customtools",
271
+ "gemini-3.8-flash",
272
+ "gemini-4-argon",
273
+ ].includes(native)
274
+ )
275
+ return effort(
276
+ "google",
277
+ BASIC,
278
+ native.startsWith("gemini-3.1") ? "high" : "medium",
279
+ );
280
+ if (
281
+ [
282
+ "gemini-3-flash-preview",
283
+ "gemini-3.1-flash-lite-preview",
284
+ "gemini-3.5-flash-lite",
285
+ "gemini-3.6-flash",
286
+ ].includes(native)
287
+ )
288
+ return effort(
289
+ "google",
290
+ ["minimal", ...BASIC],
291
+ native.includes("lite")
292
+ ? "minimal"
293
+ : native === "gemini-3-flash-preview"
294
+ ? "high"
295
+ : "medium",
296
+ );
297
+ if (["gemini-2.5-flash", "gemini-2.5-flash-lite"].includes(native))
298
+ return {
299
+ control: "toggle",
300
+ efforts: [],
301
+ canDisable: true,
302
+ transport: "google",
303
+ thinking: "budget",
304
+ defaultEnabled: native !== "gemini-2.5-flash-lite",
305
+ };
306
+ // Gemini 2.5 Pro requires thinking and exposes a budget, not an effort level.
307
+ return UNKNOWN;
308
+ }
309
+ const transport = "openai";
310
+ if (modelId === "concentrate:glm-5.3")
311
+ return effort(transport, ["low", "high", "max"], "high");
312
+ if (CONCENTRATE_REASONING_MODEL_IDS.has(modelId))
313
+ return effort(transport, EXTENDED);
314
+ if (!["openai", "pensar", "concentrate", "bedrock-mantle"].includes(provider))
315
+ return UNKNOWN;
316
+ if (
317
+ [
318
+ "gpt-6.1-sol",
319
+ "gpt-6-astra",
320
+ "gpt-5.6-sol",
321
+ "gpt-5.6-terra",
322
+ "gpt-5.6-luna",
323
+ ].includes(native)
324
+ )
325
+ return effort(transport, EXTENDED);
326
+ if (["gpt-6-sol", "gpt-6-luna"].includes(native))
327
+ return effort(transport, ["none", ...EXTENDED]);
328
+ if (["gpt-5.2-pro", "gpt-5.4-pro", "gpt-5.5-pro"].includes(native))
329
+ return effort(transport, ["medium", "high", "xhigh"]);
330
+ if (["gpt-5-pro", "o3-pro"].includes(native))
331
+ return effort(transport, ["high"], "high");
332
+ if (
333
+ ["gpt-5.2", "gpt-5.4", "gpt-5.5", "gpt-5.4-mini", "gpt-5.4-nano"].includes(
334
+ native,
335
+ )
336
+ )
337
+ return effort(transport, [
338
+ "none",
339
+ ...BASIC,
340
+ ...(provider === "concentrate" && /-(mini|nano)$/.test(native)
341
+ ? []
342
+ : ["xhigh" as const]),
343
+ ]);
344
+ if (native === "gpt-5.1") return effort(transport, ["none", ...BASIC]);
345
+ if (["gpt-5", "gpt-5-mini", "gpt-5-nano"].includes(native))
346
+ return effort(transport, ["minimal", ...BASIC]);
347
+ if (
348
+ [
349
+ "o1",
350
+ "o3",
351
+ "o3-mini",
352
+ "o4-mini",
353
+ "gpt-5-codex",
354
+ "gpt-5.1-codex",
355
+ "gpt-5.1-codex-mini",
356
+ ].includes(native)
357
+ )
358
+ return effort(transport, BASIC);
359
+ if (["gpt-5.1-codex-max", "gpt-5.2-codex", "gpt-5.3-codex"].includes(native))
360
+ return effort(transport, [...BASIC, "xhigh"]);
361
+ return UNKNOWN;
362
+ }
363
+
364
+ export function getReasoningChoices(
365
+ capabilities: ReasoningCapabilities,
366
+ ): ReasoningSetting[] {
367
+ if (capabilities.control === "none") return [];
368
+ if (capabilities.control === "toggle")
369
+ return [
370
+ "default",
371
+ "enabled",
372
+ ...(capabilities.canDisable ? ["disabled" as const] : []),
373
+ ];
374
+ return [
375
+ "default",
376
+ ...capabilities.efforts,
377
+ ...(capabilities.canDisable && !capabilities.efforts.includes("none")
378
+ ? ["disabled" as const]
379
+ : []),
380
+ ];
381
+ }
382
+
383
+ export function validateReasoningSetting(
384
+ model: string,
385
+ value: string,
386
+ customProviders?: CustomProviders,
387
+ ): ReasoningSetting {
388
+ const choices = getReasoningChoices(
389
+ getReasoningCapabilities(model, customProviders),
390
+ );
391
+ if (value === "default" || choices.includes(value as ReasoningSetting))
392
+ return value as ReasoningSetting;
393
+ throw new Error(
394
+ `Reasoning setting "${value}" is not supported by ${model}. Supported settings: ${["default", ...choices.filter((choice) => choice !== "default")].join(", ")}.`,
395
+ );
396
+ }
397
+
398
+ export function buildModelReasoningOptions(
399
+ model: string,
400
+ setting: ReasoningSetting,
401
+ options: { customProviders?: CustomProviders; maxOutputTokens?: number } = {},
402
+ ): ReasoningProviderOptions | undefined {
403
+ const cap = getReasoningCapabilities(model, options.customProviders);
404
+ validateReasoningSetting(model, setting, options.customProviders);
405
+ if (cap.control === "none") return undefined;
406
+ if (
407
+ setting === "default" &&
408
+ cap.transport === "openrouter" &&
409
+ cap.defaultEffort === undefined &&
410
+ cap.defaultEnabled === undefined
411
+ )
412
+ return undefined;
413
+ if (setting === "default" && cap.defaultEnabled === false)
414
+ return buildModelReasoningOptions(
415
+ model,
416
+ cap.efforts.includes("none") ? "none" : "disabled",
417
+ options,
418
+ );
419
+ const budgetTokens =
420
+ options.maxOutputTokens === undefined
421
+ ? 10_000
422
+ : Math.min(10_000, Math.floor(options.maxOutputTokens / 2));
423
+ if (
424
+ cap.thinking === "budget" &&
425
+ cap.transport !== "google" &&
426
+ setting !== "disabled" &&
427
+ budgetTokens < 1024
428
+ )
429
+ throw new Error(
430
+ "Extended thinking requires an output limit of at least 2048 tokens.",
431
+ );
432
+ const disabled = setting === "disabled" || setting === "none";
433
+ const selectedEffort =
434
+ setting === "default" || setting === "enabled"
435
+ ? cap.defaultEffort
436
+ : disabled
437
+ ? undefined
438
+ : setting;
439
+ if (cap.transport === "openrouter") {
440
+ return {
441
+ openrouter: {
442
+ reasoning: disabled
443
+ ? {
444
+ enabled: false,
445
+ ...(cap.efforts.includes("none") ? { effort: "none" } : {}),
446
+ }
447
+ : {
448
+ enabled: true,
449
+ ...(selectedEffort ? { effort: selectedEffort } : {}),
450
+ ...(cap.thinking === "budget"
451
+ ? { max_tokens: budgetTokens }
452
+ : {}),
453
+ },
454
+ },
455
+ };
456
+ }
457
+ if (cap.transport === "openai")
458
+ return {
459
+ openai: {
460
+ reasoningEffort: disabled ? "none" : selectedEffort,
461
+ ...(model.startsWith("custom:") ? { forceReasoning: true } : {}),
462
+ },
463
+ };
464
+ if (cap.transport === "google")
465
+ return {
466
+ google: {
467
+ thinkingConfig:
468
+ cap.thinking === "budget"
469
+ ? { thinkingBudget: disabled ? 0 : -1, includeThoughts: !disabled }
470
+ : { thinkingLevel: selectedEffort, includeThoughts: true },
471
+ },
472
+ };
473
+ const thinking = disabled
474
+ ? { type: "disabled" }
475
+ : cap.thinking === "budget"
476
+ ? { type: "enabled", budgetTokens }
477
+ : { type: "adaptive", display: "summarized" };
478
+ const anthropic = {
479
+ thinking,
480
+ ...(!disabled && cap.control === "effort"
481
+ ? { effort: selectedEffort }
482
+ : {}),
483
+ };
484
+ if (cap.transport === "bedrock")
485
+ return {
486
+ anthropic,
487
+ bedrock: {
488
+ reasoningConfig: {
489
+ ...thinking,
490
+ ...(!disabled && cap.control === "effort"
491
+ ? { maxReasoningEffort: selectedEffort }
492
+ : {}),
493
+ },
494
+ ...(disabled
495
+ ? { additionalModelRequestFields: { thinking: { type: "disabled" } } }
496
+ : {}),
497
+ },
498
+ };
499
+ return { anthropic };
500
+ }
@@ -184,6 +184,27 @@ describe("StreamIdleTimeoutError", () => {
184
184
  });
185
185
 
186
186
  describe("createToolExecutionGate", () => {
187
+ it("keeps streaming tools gated until their final result", () => {
188
+ const gate = createToolExecutionGate();
189
+ gate.observe({
190
+ type: "tool-call",
191
+ toolCallId: "streaming",
192
+ toolName: "probe",
193
+ });
194
+ gate.observe({
195
+ type: "tool-result",
196
+ toolCallId: "streaming",
197
+ preliminary: true,
198
+ });
199
+ expect(gate.shouldEnforceIdleTimeout()).toBe(false);
200
+ gate.observe({
201
+ type: "tool-result",
202
+ toolCallId: "streaming",
203
+ preliminary: false,
204
+ });
205
+ expect(gate.shouldEnforceIdleTimeout()).toBe(true);
206
+ });
207
+
187
208
  it("opens on tool-call and closes on tool-result", () => {
188
209
  const gate = createToolExecutionGate();
189
210
  expect(gate.shouldEnforceIdleTimeout()).toBe(true);
@@ -2,6 +2,7 @@ import type {
2
2
  LanguageModelV3,
3
3
  LanguageModelV3CallOptions,
4
4
  LanguageModelV3GenerateResult,
5
+ LanguageModelV3StreamResult,
5
6
  } from "@ai-sdk/provider";
6
7
  import { beforeEach, describe, expect, it, vi } from "vitest";
7
8
 
@@ -13,13 +14,19 @@ const mocks = vi.hoisted(() => {
13
14
  } = {};
14
15
  return {
15
16
  state,
16
- generateText: vi.fn(async (input: { model: LanguageModelV3 }) => {
17
+ streamText: vi.fn((input: { model: LanguageModelV3 }) => {
17
18
  if (!state.callOptions) throw new Error("missing fixture call options");
18
- await input.model.doGenerate(state.callOptions);
19
+ const called = input.model.doStream(state.callOptions);
19
20
  return {
20
- text: "bounded summary",
21
- usage: { inputTokens: 2, outputTokens: 1, totalTokens: 3 },
22
- providerMetadata: undefined,
21
+ text: called.then(() => "bounded summary"),
22
+ usage: called.then(() => ({
23
+ inputTokens: 2,
24
+ outputTokens: 1,
25
+ totalTokens: 3,
26
+ })),
27
+ finishReason: called.then(() => "stop"),
28
+ providerMetadata: called.then(() => undefined),
29
+ toolCalls: called.then(() => []),
23
30
  };
24
31
  }),
25
32
  streamResponse: vi.fn(() => {
@@ -41,11 +48,13 @@ const mocks = vi.hoisted(() => {
41
48
 
42
49
  vi.mock("ai", async () => {
43
50
  const actual = await vi.importActual<typeof import("ai")>("ai");
44
- return { ...actual, generateText: mocks.generateText };
51
+ return { ...actual, streamText: mocks.streamText };
45
52
  });
46
53
 
47
54
  vi.mock("./ai", () => ({
48
55
  buildOpenRouterProviderOptions: vi.fn(() => undefined),
56
+ buildReasoningProviderOptions: vi.fn(() => undefined),
57
+ mergeProviderOptions: vi.fn(() => undefined),
49
58
  recordContextRestart: vi.fn(async () => {}),
50
59
  streamResponse: mocks.streamResponse,
51
60
  }));
@@ -72,6 +81,10 @@ function generated(): LanguageModelV3GenerateResult {
72
81
  };
73
82
  }
74
83
 
84
+ function streamed(): LanguageModelV3StreamResult {
85
+ return { stream: new ReadableStream({ start: (c) => c.close() }) };
86
+ }
87
+
75
88
  function model(): LanguageModelV3 {
76
89
  return {
77
90
  specificationVersion: "v3",
@@ -79,7 +92,7 @@ function model(): LanguageModelV3 {
79
92
  modelId: "provider-model",
80
93
  supportedUrls: {},
81
94
  doGenerate: vi.fn(async () => generated()),
82
- doStream: vi.fn(),
95
+ doStream: vi.fn(async () => streamed()),
83
96
  };
84
97
  }
85
98
 
@@ -125,7 +138,7 @@ describe("summarization native rollout evidence boundary", () => {
125
138
  });
126
139
  await capture.flush();
127
140
 
128
- expect(mocks.generateText).toHaveBeenCalledOnce();
141
+ expect(mocks.streamText).toHaveBeenCalledOnce();
129
142
  expect(mocks.streamResponse).toHaveBeenCalledOnce();
130
143
  expect(envelopes.map((entry) => entry.operationKind)).toEqual([
131
144
  "context.summarize",