@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
@@ -1,11 +1,12 @@
1
1
  import { tool } from "ai";
2
2
  import { z } from "zod";
3
+ import { getModelSettings } from "../../../ai";
3
4
  import type { AssetRecord } from "../../../findings/attackSurfaceRegistry";
4
5
  import {
5
6
  resolvePathWithinCodebaseRoot,
6
7
  resolveWhiteboxCodebaseRoot,
7
8
  } from "../../../whitebox";
8
- import type { SubagentSpawner } from "../subagentSpawner";
9
+ import { type SpawnOptions, spawnBatch } from "../subagentSpawner";
9
10
  import type { ToolContext } from "./types";
10
11
 
11
12
  /** Default max concurrent coding agents */
@@ -100,49 +101,46 @@ Returns an array of results with the text output from each agent.`,
100
101
 
101
102
  const spawner = ctx.subagentSpawner;
102
103
 
103
- // Bounded fan-out via the spawner so each sub-agent inherits the OTel
104
- // context. Errors are caught per task and returned inline, so results
105
- // never contain null.
106
- const settled = await spawner.spawnMany(
107
- validatedTasks,
108
- async (item, i) => {
109
- try {
110
- const output = await runSingleCodingAgent(
111
- ctx,
112
- spawner,
113
- item.codebasePath,
114
- item.objective,
115
- i + 1,
116
- item.name,
117
- toolCallId,
118
- );
119
- return {
120
- codebasePath: item.codebasePath,
121
- objective: item.objective,
122
- output,
123
- };
124
- } catch (err) {
125
- return {
126
- codebasePath: item.codebasePath,
127
- objective: item.objective,
128
- output: "",
129
- error: err instanceof Error ? err.message : String(err),
130
- };
131
- }
132
- },
104
+ const textOutputs = validatedTasks.map(() => "");
105
+ const settled = await spawnBatch(
106
+ spawner,
107
+ validatedTasks.map((item, index) =>
108
+ codingSpawnOptions(
109
+ ctx,
110
+ item.codebasePath,
111
+ item.objective,
112
+ item.name,
113
+ toolCallId,
114
+ (text) => {
115
+ textOutputs[index] += text;
116
+ },
117
+ ),
118
+ ),
133
119
  { concurrency: DEFAULT_CONCURRENCY },
134
120
  );
135
-
136
- const results = settled.filter(
137
- (
138
- r,
139
- ): r is {
140
- codebasePath: string;
141
- objective: string;
142
- output: string;
143
- error?: string;
144
- } => r !== null,
145
- );
121
+ const results = [];
122
+ for (const [index, item] of validatedTasks.entries()) {
123
+ const result = settled[index]!;
124
+ try {
125
+ if (result.status === "rejected") throw result.reason;
126
+ if (result.value?.surface?.length && ctx.attackSurfaceRegistry) {
127
+ for (const asset of result.value.surface) {
128
+ await ctx.attackSurfaceRegistry.register(asset);
129
+ }
130
+ }
131
+ results.push({
132
+ ...item,
133
+ output: result.value?.text ?? textOutputs[index]!,
134
+ error: undefined,
135
+ });
136
+ } catch (error) {
137
+ results.push({
138
+ ...item,
139
+ output: "",
140
+ error: error instanceof Error ? error.message : String(error),
141
+ });
142
+ }
143
+ }
146
144
 
147
145
  const failedTasks = results.filter((r) => r.error);
148
146
 
@@ -165,7 +163,7 @@ Returns an array of results with the text output from each agent.`,
165
163
  }
166
164
 
167
165
  // ---------------------------------------------------------------------------
168
- // Internal: run a single CodeAgent
166
+ // Child request
169
167
  // ---------------------------------------------------------------------------
170
168
 
171
169
  /** The slice of a durable CodeAgent child's returned value the parent folds. */
@@ -174,33 +172,28 @@ interface CodingChildResult {
174
172
  text?: string;
175
173
  }
176
174
 
177
- async function runSingleCodingAgent(
175
+ function codingSpawnOptions(
178
176
  ctx: ToolContext,
179
- spawner: SubagentSpawner,
180
177
  codebasePath: string,
181
178
  objective: string,
182
- _agentIndex: number,
183
179
  name: string,
184
180
  parentToolCallId: string,
185
- ): Promise<string> {
186
- let textOutput = "";
187
-
188
- const result = await spawner.spawn<CodingChildResult | undefined>({
181
+ onText: (text: string) => void,
182
+ ): SpawnOptions<CodingChildResult | undefined> {
183
+ return {
189
184
  spec: { type: "code", codebasePath, objective },
190
185
  runtime: {
186
+ findingPolicy: ctx.findingPolicy,
191
187
  session: ctx.session,
192
- model: ctx.model!,
193
- authConfig: ctx.authConfig,
188
+ ...getModelSettings(ctx),
194
189
  abortSignal: ctx.abortSignal,
195
190
  sandbox: ctx.sandbox,
196
191
  backends: ctx.backends,
197
192
  environmentVariables: ctx.environmentVariables,
198
193
  secretValues: ctx.secretValues,
199
- enableThinking: ctx.enableThinking,
200
- thinkingEffort: ctx.thinkingEffort,
201
- openAIReasoningEffort: ctx.openAIReasoningEffort,
202
194
  languageModelMiddleware: ctx.languageModelMiddleware,
203
195
  usageRecorder: ctx.usageRecorder,
196
+ toolExecutionContext: ctx.toolExecutionContext,
204
197
  streamIdFactory: ctx.streamIdFactory,
205
198
  },
206
199
  parentBus: ctx.eventBus,
@@ -212,18 +205,8 @@ async function runSingleCodingAgent(
212
205
  stampChildSessionId: true,
213
206
  beforeConsume: (childBus) => {
214
207
  childBus.on("text-delta", (d) => {
215
- textOutput += d.text;
208
+ onText(d.text);
216
209
  });
217
210
  },
218
- });
219
-
220
- // Durable children return their surface by value (in-process children return
221
- // void); fold it into the parent registry so dedup-in-parent closes.
222
- if (result?.surface?.length && ctx.attackSurfaceRegistry) {
223
- for (const asset of result.surface) {
224
- await ctx.attackSurfaceRegistry.register(asset);
225
- }
226
- }
227
-
228
- return result?.text ?? textOutput;
211
+ };
229
212
  }
@@ -23,6 +23,9 @@ const registryCalls: Array<{
23
23
  path: string;
24
24
  options: Record<string, unknown>;
25
25
  }> = [];
26
+ const swarmMock = vi.hoisted(() => ({
27
+ results: [] as Array<Record<string, unknown>>,
28
+ }));
26
29
 
27
30
  vi.mock("../../specialized/pentest/agent", () => ({
28
31
  TargetedPentestAgent: class {
@@ -46,7 +49,7 @@ vi.mock("../../../findings/registry", () => ({
46
49
 
47
50
  vi.mock("../../../workflows/pentest", () => ({
48
51
  DEFAULT_CONCURRENCY: 10,
49
- runPentestSwarm: async () => [],
52
+ runPentestSwarm: async () => swarmMock.results,
50
53
  }));
51
54
 
52
55
  import type { AIModel } from "../../../ai";
@@ -94,7 +97,12 @@ function makeParentSession(): {
94
97
  async function runSpawn(
95
98
  ctx: ToolContext,
96
99
  inputOverrides: Record<string, unknown> = {},
97
- ): Promise<{ success?: boolean; message?: string }> {
100
+ ): Promise<{
101
+ success?: boolean;
102
+ message?: string;
103
+ findingsCount?: number;
104
+ reportedFindings?: string[];
105
+ }> {
98
106
  const tool = spawnPentestAgent(ctx);
99
107
  // biome-ignore lint/suspicious/noExplicitAny: ai-sdk tool execute receives an opaque ExecuteContext we don't need to narrow for this test.
100
108
  return await (tool as any).execute(
@@ -110,6 +118,31 @@ async function runSpawn(
110
118
  );
111
119
  }
112
120
 
121
+ it("passes external grading to a worker and returns its findings without native documentation", async () => {
122
+ const spawn = vi.fn().mockResolvedValue({
123
+ findings: [],
124
+ reportedFindings: ["Cross-tenant read at GET /private"],
125
+ objectiveResults: [],
126
+ });
127
+ const ctx = buildCtx({
128
+ findingPolicy: "external",
129
+ subagentSpawner: { spawn, spawnMany: vi.fn() },
130
+ });
131
+ const result = await runSpawn(ctx);
132
+ expect(spawn).toHaveBeenCalledWith(
133
+ expect.objectContaining({
134
+ runtime: expect.objectContaining({ findingPolicy: "external" }),
135
+ }),
136
+ );
137
+ expect(result).toMatchObject({
138
+ findingsCount: 1,
139
+ reportedFindings: ["Cross-tenant read at GET /private"],
140
+ });
141
+ const description = spawnPentestAgent(ctx).description;
142
+ expect(description).not.toContain("document_vulnerability");
143
+ expect(description).not.toContain("ONE MORE TIME");
144
+ });
145
+
113
146
  function makeEventBus(): {
114
147
  bus: ToolContext["eventBus"];
115
148
  emit: ReturnType<typeof vi.fn>;
@@ -121,6 +154,7 @@ function makeEventBus(): {
121
154
  afterEach(() => {
122
155
  constructorCalls.length = 0;
123
156
  registryCalls.length = 0;
157
+ swarmMock.results = [];
124
158
  vi.restoreAllMocks();
125
159
  });
126
160
 
@@ -154,16 +188,58 @@ describe("spawn_pentest_agent — usage callbacks", () => {
154
188
  it("forwards usage callbacks to the worker", async () => {
155
189
  const onStepFinish = vi.fn();
156
190
  const onCacheMetrics = vi.fn();
157
- const ctx = buildCtx({ onStepFinish, onCacheMetrics });
191
+ const ctx = buildCtx({
192
+ model: "gpt-6.1-sol",
193
+ onStepFinish,
194
+ onCacheMetrics,
195
+ reasoning: "low",
196
+ });
158
197
 
159
198
  await runSpawn(ctx);
160
199
 
161
200
  expect(constructorCalls[0]?.onStepFinish).toBe(onStepFinish);
162
201
  expect(constructorCalls[0]?.onCacheMetrics).toBe(onCacheMetrics);
202
+ expect(constructorCalls[0]?.reasoning).toBe("low");
163
203
  });
164
204
  });
165
205
 
166
206
  describe("spawn tools — findings session attribution", () => {
207
+ it("returns each external swarm objective outcome to the parent", async () => {
208
+ swarmMock.results = [
209
+ {
210
+ findings: [],
211
+ reportedFindings: ["Cross-tenant read"],
212
+ objectiveResults: [
213
+ {
214
+ objective: "Extract the assigned flag",
215
+ completed: true,
216
+ result: "Extracted FLAG{example}",
217
+ },
218
+ ],
219
+ },
220
+ ];
221
+ const tool = spawnPentestSwarm(buildCtx({ findingPolicy: "external" }));
222
+ // biome-ignore lint/suspicious/noExplicitAny: ai-sdk tool execute receives an opaque ExecuteContext we don't need to narrow for this test.
223
+ const output = await (tool as any).execute(
224
+ {
225
+ targets: [{ target: "https://example.com/api", objectives: ["Test"] }],
226
+ toolCallDescription: "spawn swarm",
227
+ },
228
+ { toolCallId: "tc_external", messages: [], abortSignal: undefined },
229
+ );
230
+
231
+ expect(output.results[0]).toMatchObject({
232
+ reportedFindings: ["Cross-tenant read"],
233
+ objectiveResults: [
234
+ {
235
+ objective: "Extract the assigned flag",
236
+ completed: true,
237
+ result: "Extracted FLAG{example}",
238
+ },
239
+ ],
240
+ });
241
+ });
242
+
167
243
  it("attributes the single-agent fallback registry to the root session", async () => {
168
244
  await runSpawn(buildCtx());
169
245
 
@@ -1,6 +1,7 @@
1
1
  import { tool } from "ai";
2
2
  import { z } from "zod";
3
3
  import { parseTargetUrl } from "../../../../util/url";
4
+ import { getModelSettings } from "../../../ai";
4
5
  import { FindingsRegistry } from "../../../findings/registry";
5
6
  // Type-only (erased) — the value is constructed inside the spawner registry.
6
7
  import type { GrpcPentestContext } from "../../specialized/attackSurface/grpcSchema";
@@ -35,6 +36,22 @@ const ORIGIN_AUTH_FAILURE_GUIDANCE = `A 401/403 from one resource does not by it
35
36
  * directly on the parent bus
36
37
  */
37
38
  export function spawnPentestAgent(ctx: ToolContext) {
39
+ const externalFindings = ctx.findingPolicy === "external";
40
+ const findingSteps = externalFindings
41
+ ? "2. Reports concise evidence-backed findings in its response\n3. Leaves acceptance to the caller's external evaluator"
42
+ : "2. Confirms vulnerabilities with POC scripts\n3. Documents findings to the shared findings registry only when the exploit path and material security impact are proven";
43
+ const findingGuidance = externalFindings
44
+ ? "False-positive discipline: report a security weakness only when observations support a material abuse path. Include the affected target, evidence, and impact; do not add a separate in-run finding review."
45
+ : "False-positive discipline: workers should not call document_vulnerability for public/unauthenticated behavior, missing best-practice controls, generic errors, local HTTP deployment context, or other low-signal observations unless they prove a material abuse path such as non-public data access, credentialed cross-origin abuse, state change, denial of service, account takeover, or another concrete exploit chain.";
46
+ const returnGuidance = externalFindings
47
+ ? "Returns the worker's summary, objective results, and reported findings so the parent can investigate chains."
48
+ : "Returns the worker's summary, objective results, and finding count — but NOT the findings themselves (those land in the shared registry).";
49
+ const exploratoryGuidance = externalFindings
50
+ ? "Spawn a follow-up exploratory worker only when the assigned objective requires broader coverage or a credible untested lead remains. For detection-only work, finish once the requested evidence is sufficient."
51
+ : 'After fanning out one worker per objective, call this tool ONE MORE TIME for a final "exploratory" worker. Pass it the URLs of any suspicious behaviors discovered by earlier workers and ask it to chain findings together and probe for additional, unspecified vulnerabilities.';
52
+ const objectiveGuidance = externalFindings
53
+ ? "Pass one focused objective per worker. A follow-up objective may cover a concrete chain or untested lead when the assignment calls for it."
54
+ : "For per-objective fan-out, pass exactly one objective. For the final exploratory pass, pass a single composite objective describing chaining + exploration.";
38
55
  const targetScopeGuidance =
39
56
  ctx.systemScope && ctx.systemScope.memberHosts.length > 0
40
57
  ? `For a cross-service follow-up, you may instead use a full URL on one of these authorized System Scope members: ${ctx.systemScope.memberHosts.join(", ")}.`
@@ -51,18 +68,17 @@ Authentication state is origin-specific. Before spawning a cross-service worker
51
68
 
52
69
  Each spawn produces an independent TargetedPentestAgent that:
53
70
  1. Tests the assigned objective(s) deeply (execute_command, http_request, browser tools)
54
- 2. Confirms vulnerabilities with POC scripts
55
- 3. Documents findings to the shared findings registry only when the exploit path and material security impact are proven
71
+ ${findingSteps}
56
72
 
57
73
  Cloned browser session — the worker gets its OWN isolated Chromium instance, but it is seeded at spawn time with a snapshot of your current cookies and localStorage. If you've already authenticated in your browser (or recon set up useful state), the worker starts already authenticated for the same origins. Critically, the worker's browser is a COPY: its navigations, form fills, \`localStorage.setItem\`, alerts, and any other browser-side mutations stay inside the worker's own browser and do NOT leak back to you or to sibling workers. If you want state to be visible to the next worker, set it up in YOUR browser before spawning.
58
74
 
59
75
  The worker has no further spawn capability — it cannot recursively spawn other pentest agents.
60
76
 
61
- After fanning out one worker per objective, call this tool ONE MORE TIME for a final "exploratory" worker. Pass it the URLs of any suspicious behaviors discovered by earlier workers and ask it to chain findings together and probe for additional, unspecified vulnerabilities.
77
+ ${exploratoryGuidance}
62
78
 
63
- False-positive discipline: workers should not call document_vulnerability for public/unauthenticated behavior, missing best-practice controls, generic errors, local HTTP deployment context, or other low-signal observations unless they prove a material abuse path such as non-public data access, credentialed cross-origin abuse, state change, denial of service, account takeover, or another concrete exploit chain.
79
+ ${findingGuidance}
64
80
 
65
- Returns the worker's summary, objective results, and finding count — but NOT the findings themselves (those land in the shared registry).`,
81
+ ${returnGuidance}`,
66
82
  inputSchema: z.object({
67
83
  name: z
68
84
  .string()
@@ -77,9 +93,7 @@ Returns the worker's summary, objective results, and finding count — but NOT t
77
93
  objectives: z
78
94
  .array(z.string())
79
95
  .min(1)
80
- .describe(
81
- "Testing objectives for this worker. For per-objective fan-out, pass exactly one objective. For the final exploratory pass, pass a single composite objective describing chaining + exploration.",
82
- ),
96
+ .describe(`Testing objectives for this worker. ${objectiveGuidance}`),
83
97
  context: z
84
98
  .string()
85
99
  .optional()
@@ -130,6 +144,7 @@ Returns the worker's summary, objective results, and finding count — but NOT t
130
144
  const findingsRegistry =
131
145
  ctx.findingsRegistry ??
132
146
  FindingsRegistry.fromDirectory(ctx.session.findingsPath, {
147
+ reasoning: ctx.reasoning,
133
148
  model: ctx.model,
134
149
  authConfig: ctx.authConfig,
135
150
  abortSignal: ctx.abortSignal,
@@ -151,7 +166,7 @@ Returns the worker's summary, objective results, and finding count — but NOT t
151
166
  const spawner = ctx.subagentSpawner;
152
167
  let subagentId = "";
153
168
  try {
154
- const { findings, objectiveResults } =
169
+ const { findings, reportedFindings, objectiveResults } =
155
170
  await spawner.spawn<PentestResult>({
156
171
  spec: {
157
172
  type: "pentest",
@@ -170,9 +185,9 @@ Returns the worker's summary, objective results, and finding count — but NOT t
170
185
  browserSession: sharedBrowserSession,
171
186
  },
172
187
  runtime: {
188
+ findingPolicy: ctx.findingPolicy,
173
189
  session: ctx.session,
174
- model: ctx.model,
175
- authConfig: ctx.authConfig,
190
+ ...getModelSettings(ctx),
176
191
  abortSignal: ctx.abortSignal,
177
192
  onStepFinish: ctx.onStepFinish,
178
193
  onCacheMetrics: ctx.onCacheMetrics,
@@ -180,11 +195,9 @@ Returns the worker's summary, objective results, and finding count — but NOT t
180
195
  backends: ctx.backends,
181
196
  display: ctx.display,
182
197
  secretValues: ctx.secretValues,
183
- enableThinking: ctx.enableThinking,
184
- thinkingEffort: ctx.thinkingEffort,
185
- openAIReasoningEffort: ctx.openAIReasoningEffort,
186
198
  languageModelMiddleware: ctx.languageModelMiddleware,
187
199
  usageRecorder: ctx.usageRecorder,
200
+ toolExecutionContext: ctx.toolExecutionContext,
188
201
  streamIdFactory: ctx.streamIdFactory,
189
202
  },
190
203
  parentBus: ctx.eventBus,
@@ -203,9 +216,16 @@ Returns the worker's summary, objective results, and finding count — but NOT t
203
216
  return {
204
217
  success: true,
205
218
  subagentId,
206
- findingsCount: findings.length,
219
+ findingsCount: externalFindings
220
+ ? (reportedFindings?.length ?? 0)
221
+ : findings.length,
222
+ ...(externalFindings
223
+ ? { reportedFindings: reportedFindings ?? [] }
224
+ : {}),
207
225
  objectiveResults,
208
- message: `Worker '${name}' complete. Documented ${findings.length} finding(s).`,
226
+ message: externalFindings
227
+ ? `Worker '${name}' complete. Reported ${reportedFindings?.length ?? 0} finding(s) for external evaluation.`
228
+ : `Worker '${name}' complete. Documented ${findings.length} finding(s).`,
209
229
  };
210
230
  } catch (error) {
211
231
  const message = error instanceof Error ? error.message : String(error);
@@ -21,15 +21,21 @@ const log = scopedLogger(() => createLogger("spawn-pentest-swarm"));
21
21
  * after recon, it calls this tool to fan-out testing to sub-agents.
22
22
  */
23
23
  export function spawnPentestSwarm(ctx: ToolContext) {
24
+ const externalFindings = ctx.findingPolicy === "external";
25
+ const findingSteps = externalFindings
26
+ ? "2. Surface concise evidence-backed findings in its response\n3. Leave finding acceptance to the caller's evaluator"
27
+ : "2. Create POC scripts for confirmed vulnerabilities\n3. Document findings with evidence and remediation";
28
+ const findingDestination = externalFindings
29
+ ? "Reported findings are returned to this caller; no native finding review runs."
30
+ : "All findings are written to the shared session directory.";
24
31
  return tool({
25
32
  description: `Launch a swarm of targeted pentest sub-agents to test multiple targets in parallel.
26
33
 
27
34
  Call this AFTER completing attack surface discovery. Each entry in 'targets' spawns an independent TargetedPentestAgent that will:
28
35
  1. Test the specified target for the given objectives
29
- 2. Create POC scripts for confirmed vulnerabilities
30
- 3. Document findings with evidence and remediation
36
+ ${findingSteps}
31
37
 
32
- All findings are written to the shared session directory.
38
+ ${findingDestination}
33
39
 
34
40
  Pass every target you want tested — the swarm handles concurrency automatically.`,
35
41
  inputSchema: z.object({
@@ -79,6 +85,7 @@ Pass every target you want tested — the swarm handles concurrency automaticall
79
85
  const findingsRegistry =
80
86
  ctx.findingsRegistry ??
81
87
  FindingsRegistry.fromDirectory(ctx.session.findingsPath, {
88
+ reasoning: ctx.reasoning,
82
89
  model: ctx.model!,
83
90
  authConfig: ctx.authConfig,
84
91
  abortSignal: ctx.abortSignal,
@@ -92,6 +99,7 @@ Pass every target you want tested — the swarm handles concurrency automaticall
92
99
  );
93
100
 
94
101
  const swarmResults = await runPentestSwarm({
102
+ findingPolicy: ctx.findingPolicy,
95
103
  targets,
96
104
  model: ctx.model!,
97
105
  session: ctx.session,
@@ -105,12 +113,15 @@ Pass every target you want tested — the swarm handles concurrency automaticall
105
113
  enableThinking: ctx.enableThinking,
106
114
  thinkingEffort: ctx.thinkingEffort,
107
115
  openAIReasoningEffort: ctx.openAIReasoningEffort,
116
+ reasoning: ctx.reasoning,
108
117
  display: ctx.display,
109
118
  hooks: {
119
+ findingPolicy: ctx.findingPolicy,
110
120
  backends: ctx.backends,
111
121
  subagentSpawner: ctx.subagentSpawner,
112
122
  languageModelMiddleware: ctx.languageModelMiddleware,
113
123
  usageRecorder: ctx.usageRecorder,
124
+ toolExecutionContext: ctx.toolExecutionContext,
114
125
  streamIdFactory: ctx.streamIdFactory,
115
126
  smsInbox: ctx.smsInbox,
116
127
  emailAdapterFor: ctx.emailAdapterFor,
@@ -120,7 +131,11 @@ Pass every target you want tested — the swarm handles concurrency automaticall
120
131
 
121
132
  // Aggregate
122
133
  const totalFindings = swarmResults.reduce(
123
- (sum, r) => sum + (r?.findings.length ?? 0),
134
+ (sum, r) =>
135
+ sum +
136
+ (externalFindings
137
+ ? (r?.reportedFindings?.length ?? 0)
138
+ : (r?.findings.length ?? 0)),
124
139
  0,
125
140
  );
126
141
  const failedCount = swarmResults.filter((r) => r == null).length;
@@ -132,7 +147,15 @@ Pass every target you want tested — the swarm handles concurrency automaticall
132
147
  failedTargets: failedCount,
133
148
  results: targets.map((t, i) => ({
134
149
  target: t.target,
135
- findingsCount: swarmResults[i]?.findings.length ?? 0,
150
+ findingsCount: externalFindings
151
+ ? (swarmResults[i]?.reportedFindings?.length ?? 0)
152
+ : (swarmResults[i]?.findings.length ?? 0),
153
+ ...(externalFindings
154
+ ? {
155
+ reportedFindings: swarmResults[i]?.reportedFindings ?? [],
156
+ objectiveResults: swarmResults[i]?.objectiveResults ?? [],
157
+ }
158
+ : {}),
136
159
  error: swarmResults[i] == null ? "Agent failed" : undefined,
137
160
  })),
138
161
  message: `Swarm complete. ${totalFindings} findings across ${total} targets. ${
@@ -1,6 +1,7 @@
1
1
  import { stepCountIs } from "ai";
2
2
  import pLimit from "p-limit";
3
3
  import { z } from "zod";
4
+ import { getModelSettings } from "../../../ai";
4
5
  import { createLogger } from "../../../logger/structured";
5
6
  import { scopedLogger } from "../../../util/lazyLogger";
6
7
  import type {
@@ -314,7 +315,6 @@ export async function generateThreatModelForEndpoint(
314
315
  input: GenerateThreatModelInput,
315
316
  ): Promise<ThreatModelOutput | null> {
316
317
  if (!ctx.model) return null;
317
- const model = ctx.model;
318
318
 
319
319
  return threatModelLimiter(async () => {
320
320
  if (ctx.abortSignal?.aborted) return null;
@@ -373,13 +373,14 @@ export async function generateThreatModelForEndpoint(
373
373
  excludeTools: ["document_endpoint", "document_app"],
374
374
  },
375
375
  runtime: {
376
+ findingPolicy: ctx.findingPolicy,
376
377
  session: ctx.session,
377
- model,
378
- authConfig: ctx.authConfig,
378
+ ...getModelSettings(ctx),
379
379
  abortSignal: childAbort.signal,
380
380
  backends: ctx.backends,
381
381
  languageModelMiddleware: ctx.languageModelMiddleware,
382
382
  usageRecorder: ctx.usageRecorder,
383
+ toolExecutionContext: ctx.toolExecutionContext,
383
384
  streamIdFactory: ctx.streamIdFactory,
384
385
  },
385
386
  parentBus: ctx.eventBus,
@@ -8,6 +8,7 @@ import type {
8
8
  AIModel,
9
9
  CacheMetrics,
10
10
  OpenAIReasoningEffort,
11
+ ReasoningSetting,
11
12
  ThinkingEffort,
12
13
  UsageRecorder,
13
14
  } from "../../../ai";
@@ -22,7 +23,12 @@ import type { ToolBackends } from "../../../tools/backends/types";
22
23
  import type { GrpcPentestContext } from "../../specialized/attackSurface/grpcSchema";
23
24
  import type { SubagentSpawner } from "../subagentSpawner";
24
25
  import type { StepTraceWriter } from "../trace";
25
- import type { StreamIdFactory, SystemPentestScope } from "../types";
26
+ import type {
27
+ AgentHooks,
28
+ FindingPolicy,
29
+ StreamIdFactory,
30
+ SystemPentestScope,
31
+ } from "../types";
26
32
  import type { CallbackListenerRegistry } from "./callbackListener";
27
33
  import type { EmailAdapterResolver } from "./email/adapters";
28
34
  import type { PerCommandShell } from "./perCommandShell";
@@ -38,6 +44,7 @@ import type { SmsInbox } from "./smsInbox";
38
44
  * session or agent internals directly.
39
45
  */
40
46
  export type ToolContext = {
47
+ findingPolicy?: FindingPolicy;
41
48
  attackSurfaceArtifactsPath?: string;
42
49
 
43
50
  /** Session providing paths for findings, POCs, logs, scratchpad, etc. */
@@ -172,6 +179,7 @@ export type ToolContext = {
172
179
 
173
180
  /** OpenAI reasoning effort for GPT/o-series sub-agents. */
174
181
  openAIReasoningEffort?: OpenAIReasoningEffort | null;
182
+ reasoning?: ReasoningSetting;
175
183
 
176
184
  /**
177
185
  * Whitebox attack surface flag. Forwarded into `runPentestWorkflow` so the
@@ -229,7 +237,7 @@ export type ToolContext = {
229
237
  * the agent builds its tools (`OffensiveSecurityAgentInput.subagentSpawner` ??
230
238
  * the in-process spawner), so it is always present here and tools never
231
239
  * re-default it; a durable runtime injects one that spawns child workflows
232
- * instead. The three durable hooks below are inherited by every child the
240
+ * instead. The durable hooks below are inherited by every child the
233
241
  * spawner constructs.
234
242
  */
235
243
  subagentSpawner: SubagentSpawner;
@@ -256,6 +264,8 @@ export type ToolContext = {
256
264
  */
257
265
  emailAdapterFor?: EmailAdapterResolver;
258
266
 
267
+ toolExecutionContext?: AgentHooks["toolExecutionContext"];
268
+
259
269
  /** Provider middleware inherited by spawned children. Unset → raw model. */
260
270
  languageModelMiddleware?: LanguageModelMiddleware | LanguageModelMiddleware[];
261
271
 
@@ -34,12 +34,16 @@ type UpdateTaskResult = {
34
34
  };
35
35
 
36
36
  export function updateTask(ctx: ToolContext) {
37
+ const completedGuidance =
38
+ ctx.findingPolicy === "external"
39
+ ? "vulnerability identified with supporting evidence, OR conclusively determined the endpoint is not vulnerable / materiality is not met for this technique."
40
+ : "vulnerability found and documented via document_vulnerability, OR conclusively determined the endpoint is not vulnerable / materiality is not met for this technique. Documentation is NOT required to complete a non-vulnerable task.";
37
41
  return tool({
38
42
  description: `Update a task's status and record results.
39
43
 
40
44
  Status transitions:
41
45
  - "in_progress" — Set when you START testing a task. Pick one pending task at a time.
42
- - "completed" — Set when testing is done: vulnerability found and documented via document_vulnerability, OR conclusively determined the endpoint is not vulnerable / materiality is not met for this technique. Documentation is NOT required to complete a non-vulnerable task.
46
+ - "completed" — Set when testing is done: ${completedGuidance}
43
47
  - "failed" — Set when the technique didn't work: rate limited, blocked by WAF, dead end, or approach not applicable. Then create a NEW task with an alternative technique.
44
48
 
45
49
  Always include a result or observation explaining the outcome.`,
@@ -47,12 +47,15 @@ const CandidateStateSchema = z.enum([
47
47
  ]);
48
48
 
49
49
  export function createWhiteboxCandidate(ctx: ToolContext) {
50
+ const candidateGuidance =
51
+ ctx.findingPolicy === "external"
52
+ ? "for the caller's final evaluation. Candidates keep hypotheses, source-to-sink traces, scanner artifacts, fuzzing logs, and verification status separate from reported findings."
53
+ : "for document_vulnerability. Candidates keep hypotheses, source-to-sink traces,\nscanner artifacts, fuzzing logs, and verification status out of official findings.";
50
54
  return tool({
51
55
  description: `Create a whitebox vulnerability candidate.
52
56
 
53
57
  Use this for plausible source-code findings that are not yet confirmed enough
54
- for document_vulnerability. Candidates keep hypotheses, source-to-sink traces,
55
- scanner artifacts, fuzzing logs, and verification status out of official findings.`,
58
+ ${candidateGuidance}`,
56
59
  inputSchema: z.object({
57
60
  title: z.string(),
58
61
  vulnerabilityClass: z.string(),
@@ -81,7 +84,9 @@ scanner artifacts, fuzzing logs, and verification status out of official finding
81
84
  artifactPaths: candidate.artifacts.map((artifact) => artifact.path),
82
85
  nextActions: [
83
86
  "Move candidate to investigating after reading source context.",
84
- "Attempt a reproducer or dynamic verification before document_vulnerability.",
87
+ ctx.findingPolicy === "external"
88
+ ? "Gather the evidence required by the caller before reporting this candidate."
89
+ : "Attempt a reproducer or dynamic verification before document_vulnerability.",
85
90
  ],
86
91
  truncated: false,
87
92
  };
@@ -132,7 +137,9 @@ State machine: hypothesis -> investigating -> repro_attempted -> confirmed (requ
132
137
  nextActions:
133
138
  candidate.state === "confirmed"
134
139
  ? [
135
- "Use document_vulnerability only if the proof demonstrates exploitability.",
140
+ ctx.findingPolicy === "external"
141
+ ? "Report only the evidence-backed conclusion to the caller."
142
+ : "Use document_vulnerability only if the proof demonstrates exploitability.",
136
143
  ]
137
144
  : [
138
145
  "Continue tracing or verification based on the candidate state.",