@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,6 +1,7 @@
1
1
  import { writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { z } from "zod";
4
+ import type { FindingPolicy } from "../agents/offSecAgent";
4
5
  import { OffensiveSecurityAgent } from "../agents/offSecAgent";
5
6
  import { FindingsRegistry } from "../findings/registry";
6
7
  import {
@@ -32,9 +33,44 @@ const FastStrikeResult = z.object({
32
33
  ),
33
34
  });
34
35
 
35
- type FastStrikeOutcome = z.infer<typeof FastStrikeResult>;
36
+ const FastStrikeExternalResult = z.object({
37
+ solved: z.boolean().describe("True if the supplied objective was met."),
38
+ summary: z
39
+ .string()
40
+ .describe(
41
+ "Concise account of security findings and the evidence observed, including any flag or sensitive data extracted for an exfiltration objective.",
42
+ ),
43
+ reportedFindings: z
44
+ .array(z.string())
45
+ .describe(
46
+ "One concise evidence-backed description per discovered vulnerability.",
47
+ ),
48
+ submissionJson: z
49
+ .string()
50
+ .describe(
51
+ "The exact task-defined JSON submission as a string, or an empty string when no JSON submission was requested. Do not invent fields.",
52
+ ),
53
+ });
54
+
55
+ type FastStrikeOutcome =
56
+ | z.infer<typeof FastStrikeResult>
57
+ | z.infer<typeof FastStrikeExternalResult>;
36
58
 
37
- export const FAST_STRIKE_SYSTEM_PROMPT = `You are an elite offensive security operator running a fast strike against a single target. You work ALONE and finish the job yourself — there is no team to hand off to, no swarm to spawn, no plan to file. Your edge is discipline and observation, not volume.
59
+ export function buildFastStrikeSystemPrompt(
60
+ findingPolicy: FindingPolicy = "native",
61
+ ): string {
62
+ const external = findingPolicy === "external";
63
+ const objectiveStep = external
64
+ ? "5. FOLLOW THE OBJECTIVE — Determine whether the observed behavior establishes a security weakness. For a detection-only objective, stop once the requested evidence is sufficient; develop an exploit or pivot only when the objective requires it."
65
+ : "5. EXPLOIT — When a vulnerability is confirmed, exploit it: capture the flag, extract the sensitive data, and pivot through it if the objective requires more. Preserve viable primitives and build on them. Prefer extending a working script into an end-to-end exploit over rebuilding the chain with repeated one-off tool calls or stopping at the next network or protocol boundary.";
66
+ const finishStep = external
67
+ ? "6. REPORT & FINISH — Include each credible finding and its supporting observations in the caller's requested output. If the caller requested a JSON submission, include the exact JSON text in the response tool's submissionJson field as well; otherwise use an empty string. The caller evaluates findings after the run. Once the objective is met or credible leads are exhausted, call the response tool with the correct solved value and a concise summary."
68
+ : "6. DOCUMENT & FINISH — Record each confirmed vulnerability with the document_vulnerability tool as you go. Before concluding that every credible lead is exhausted, account for each viable primitive and pursue any concrete path that could still close the objective. After this check, whether the objective was met or every credible lead was genuinely exhausted, call the response tool with the correct solved value and a concise summary.";
69
+ const evidenceRule = external
70
+ ? "- Bias toward the shortest path that satisfies the assigned objective. Distinguish observed evidence from hypotheses, and avoid separate in-run finding review."
71
+ : "- Bias toward the shortest path that proves impact. Speed comes from picking the right lead, not from skipping verification — always confirm a finding before you claim it.";
72
+
73
+ return `You are an elite offensive security operator running a fast strike against a single target. You work ALONE and finish the job yourself — there is no team to hand off to, no swarm to spawn, no plan to file. Your edge is discipline and observation, not volume.
38
74
 
39
75
  Run this loop, tightly:
40
76
 
@@ -42,15 +78,18 @@ Run this loop, tightly:
42
78
  2. HYPOTHESIZE — From that signal and your deep knowledge of web/app vulnerability classes, name the single most likely vulnerability and why the evidence points to it. Rank leads by signal strength and go for the strongest first. A disabled form field, an ID in a URL or JWT, advertised demo creds, an exposed schema — these are tells; read them.
43
79
  3. ACT — Test that one hypothesis with the strongest available technique. Use direct tools for quick probes, but treat the shell as a working environment rather than merely a way to issue one-off commands. When the work becomes stateful, repetitive, concurrent, protocol-heavy, or multi-stage, externalize it into an editable script in the session scratchpad directory identified in the workspace instructions. Never create a scratchpad/ directory inside the target repository. Run the script, inspect the results, modify it, and rerun it as the attack develops. For blind/out-of-band testing (SSRF, blind RCE, XSS callbacks), start_callback_listener gives you a nonce-correlated listener that survives intervening commands: embed the returned callback URL (only an address you know the target can reach — never a guessed one) in the payload, then poll_callback_listener for hits. Listener readiness and the tool's own self-test never count as target evidence; stop_callback_listener when done.
44
80
  4. PRUNE — If the evidence disproves a lead, drop it and move to the next-strongest. Do NOT re-run a disproven approach or grind a dead end. Do NOT enumerate, brute-force, or run wordlists unless a specific signal demands it — choose techniques from evidence, not from a checklist.
45
- 5. EXPLOIT — When a vulnerability is confirmed, exploit it: capture the flag, extract the sensitive data, and pivot through it if the objective requires more. Preserve viable primitives and build on them. Prefer extending a working script into an end-to-end exploit over rebuilding the chain with repeated one-off tool calls or stopping at the next network or protocol boundary.
46
- 6. DOCUMENT & FINISH — Record each confirmed vulnerability with the document_vulnerability tool as you go. Before concluding that every credible lead is exhausted, account for each viable primitive and pursue any concrete path that could still close the objective. After this check, whether the objective was met or every credible lead was genuinely exhausted, call the response tool with the correct solved value and a concise summary.
81
+ ${objectiveStep}
82
+ ${finishStep}
47
83
 
48
84
  Rules:
49
85
  - You are one operator. Plan in your head and in short text notes — never via planning tools, task lists, or sub-agents.
50
86
  - Recon and exploitation are the same loop. Do just enough recon to form the first hypothesis, then start testing. Do not map the entire surface before acting.
51
87
  - Spend your reasoning on choosing techniques and interpreting evidence; use scripts for deterministic mechanics, iteration, and chain execution.
52
- - Bias toward the shortest path that proves impact. Speed comes from picking the right lead, not from skipping verification — always confirm a finding before you claim it.
88
+ ${evidenceRule}
53
89
  - Stay in scope. Only touch the target you were given and hosts it legitimately depends on.`;
90
+ }
91
+
92
+ export const FAST_STRIKE_SYSTEM_PROMPT = buildFastStrikeSystemPrompt();
54
93
 
55
94
  /** Single-operator pentest: skips attack-surface/swarm, returns {@link PentestWorkflowResult}. */
56
95
  export async function runFastStrike(
@@ -67,7 +106,9 @@ export async function runFastStrike(
67
106
  onStepFinish,
68
107
  onCacheMetrics,
69
108
  enableThinking,
109
+ thinkingEffort,
70
110
  openAIReasoningEffort,
111
+ reasoning,
71
112
  prompt,
72
113
  threatModel,
73
114
  seams: inputSeams,
@@ -82,6 +123,7 @@ export async function runFastStrike(
82
123
  findings: () =>
83
124
  FindingsRegistry.fromDirectory(session.findingsPath, {
84
125
  model,
126
+ reasoning,
85
127
  authConfig,
86
128
  abortSignal,
87
129
  sessionId: session.id,
@@ -116,6 +158,7 @@ export async function runFastStrike(
116
158
  }
117
159
 
118
160
  const operatorGuidance = session.config?.prompt;
161
+ const findingPolicy = input.hooks?.findingPolicy ?? input.findingPolicy;
119
162
 
120
163
  const mode: "blackbox" | "whitebox" = cwd ? "whitebox" : "blackbox";
121
164
 
@@ -136,8 +179,15 @@ export async function runFastStrike(
136
179
  const promptParts: string[] = [`Target: ${target}`];
137
180
  if (operatorGuidance) promptParts.push(operatorGuidance);
138
181
  promptParts.push(
139
- "Run the fast strike: find and exploit the vulnerability, complete the objective, and report what worked.",
182
+ findingPolicy === "external"
183
+ ? "Run the fast strike: investigate the target, identify security weaknesses, and submit the findings in the requested output. Stop when the supplied objective is met."
184
+ : "Run the fast strike: find and exploit the vulnerability, complete the objective, and report what worked.",
140
185
  );
186
+ if (findingPolicy === "external" && session.config?.exfilMode) {
187
+ promptParts.push(
188
+ "Exfiltration goal: locate and extract FLAG{...} or the assigned sensitive data through a confirmed weakness. Do not stop at discovery; include the extraction result in the final summary.",
189
+ );
190
+ }
141
191
 
142
192
  const agent = new OffensiveSecurityAgent<FastStrikeOutcome>({
143
193
  subagentSpawner: input.subagentSpawner,
@@ -145,14 +195,18 @@ export async function runFastStrike(
145
195
  usageRecorder: input.usageRecorder,
146
196
  streamIdFactory: input.streamIdFactory,
147
197
  ...input.hooks,
148
- system: FAST_STRIKE_SYSTEM_PROMPT,
198
+ system: buildFastStrikeSystemPrompt(findingPolicy),
149
199
  prompt: promptParts.join("\n\n"),
150
200
  model,
151
201
  session,
152
202
  target,
153
203
  mode: "fast-strike",
204
+ findingPolicy,
154
205
  activeTools: [],
155
- responseSchema: FastStrikeResult,
206
+ responseSchema:
207
+ findingPolicy === "external"
208
+ ? FastStrikeExternalResult
209
+ : FastStrikeResult,
156
210
  findingsRegistry,
157
211
  subagentId: "fast-strike",
158
212
  authConfig,
@@ -164,7 +218,9 @@ export async function runFastStrike(
164
218
  onStepFinish,
165
219
  onCacheMetrics,
166
220
  enableThinking,
221
+ thinkingEffort,
167
222
  openAIReasoningEffort,
223
+ reasoning,
168
224
  });
169
225
 
170
226
  eventBus?.emit("subagent-spawn", {
@@ -179,16 +235,32 @@ export async function runFastStrike(
179
235
  target,
180
236
  });
181
237
 
182
- let strikeResult: FastStrikeOutcome;
238
+ let completed:
239
+ | { policy: "native"; outcome: FastStrikeOutcome }
240
+ | {
241
+ policy: "external";
242
+ outcome: z.infer<typeof FastStrikeExternalResult>;
243
+ };
183
244
  try {
184
- strikeResult = await agent.consume();
245
+ const strikeResult = await agent.consume();
246
+ if (findingPolicy === "external") {
247
+ const parsed = FastStrikeExternalResult.safeParse(strikeResult);
248
+ if (!parsed.success) {
249
+ throw new Error(
250
+ "Externally graded Fast Strike ended without a structured findings response",
251
+ );
252
+ }
253
+ completed = { policy: "external", outcome: parsed.data };
254
+ } else {
255
+ completed = { policy: "native", outcome: strikeResult };
256
+ }
185
257
  eventBus?.emit("subagent-complete", {
186
258
  subagentId: "fast-strike",
187
259
  status: "completed",
188
260
  });
189
261
  await seams.hooks.onEndpointDone?.(
190
262
  { subagentId: "fast-strike", subagentName: "Fast Strike", target },
191
- strikeResult,
263
+ completed.outcome,
192
264
  );
193
265
  } catch (e) {
194
266
  eventBus?.emit("subagent-complete", {
@@ -206,8 +278,31 @@ export async function runFastStrike(
206
278
  throw new DOMException("Pentest aborted by user", "AbortError");
207
279
  }
208
280
 
281
+ if (completed.policy === "external") {
282
+ const strikeResult = completed.outcome;
283
+ eventBus?.emit("workflow-phase-complete", {
284
+ phase: "pentesting",
285
+ summary: {
286
+ findingsCount: strikeResult.reportedFindings.length,
287
+ solved: strikeResult.solved,
288
+ operatorSummary: strikeResult.summary,
289
+ },
290
+ });
291
+ return {
292
+ findings: [],
293
+ findingsPath: session.findingsPath,
294
+ pocsPath: session.pocsPath,
295
+ reportPath: null,
296
+ reportedFindings: strikeResult.reportedFindings,
297
+ submissionJson: strikeResult.submissionJson,
298
+ solved: strikeResult.solved,
299
+ summary: strikeResult.summary,
300
+ };
301
+ }
302
+
209
303
  await findingsRegistry.groupByRootCause();
210
304
  const findings = [...findingsRegistry.getFindings()];
305
+ const strikeResult = completed.outcome;
211
306
 
212
307
  const report = buildPentestReport(findings, {
213
308
  target,
@@ -573,3 +573,107 @@ describe("runPentestWorkflow — threads seams into the whitebox discovery phase
573
573
  expect(mocks.whiteboxCalls[0]?.seams).toBe(seams);
574
574
  });
575
575
  });
576
+
577
+ describe("external pentest workflow", () => {
578
+ it("returns worker findings without native report generation and retains them on resume", async () => {
579
+ const emit = vi.fn();
580
+ mocks.consumeImpl = async () => ({
581
+ findings: [],
582
+ reportedFindings: ["GET /api reveals another tenant's data"],
583
+ objectiveResults: [
584
+ {
585
+ objective: "Test the target",
586
+ completed: true,
587
+ result: "Extracted FLAG{example} through the confirmed weakness",
588
+ },
589
+ ],
590
+ findingsPath: session.findingsPath,
591
+ pocsPath: session.pocsPath,
592
+ });
593
+ const input: PentestWorkflowInput = {
594
+ target: "https://example.com",
595
+ model: {} as PentestWorkflowInput["model"],
596
+ session,
597
+ findingPolicy: "external",
598
+ eventBus: { emit } as unknown as NonNullable<
599
+ PentestWorkflowInput["eventBus"]
600
+ >,
601
+ hooks: { findingPolicy: undefined },
602
+ };
603
+
604
+ const first = await runPentestWorkflow(input);
605
+ expect(first).toMatchObject({
606
+ findings: [],
607
+ reportedFindings: ["GET /api reveals another tenant's data"],
608
+ objectiveResults: [
609
+ {
610
+ objective: "Test the target",
611
+ completed: true,
612
+ result: "Extracted FLAG{example} through the confirmed weakness",
613
+ },
614
+ ],
615
+ reportPath: null,
616
+ });
617
+ expect(mocks.targetedInputs).toHaveLength(1);
618
+ expect(mocks.targetedInputs[0]?.findingPolicy).toBe("external");
619
+ expect(emit).toHaveBeenCalledWith("workflow-phase-complete", {
620
+ phase: "pentesting",
621
+ summary: { findingsCount: 1 },
622
+ });
623
+
624
+ mocks.consumeImpl = async () => {
625
+ throw new Error("completed worker was rerun");
626
+ };
627
+ const resumed = await runPentestWorkflow(input);
628
+ expect(resumed.reportedFindings).toEqual(first.reportedFindings);
629
+ expect(resumed.objectiveResults).toEqual(first.objectiveResults);
630
+ expect(mocks.targetedInputs).toHaveLength(1);
631
+ });
632
+
633
+ it("does not generate a native report when discovery has no targets", async () => {
634
+ mocks.loadAttackSurfaceResults.mockReturnValue({ targets: [] });
635
+ const result = await runPentestWorkflow({
636
+ target: "https://example.com",
637
+ model: {} as PentestWorkflowInput["model"],
638
+ session,
639
+ findingPolicy: "external",
640
+ });
641
+ expect(result).toMatchObject({
642
+ findings: [],
643
+ reportedFindings: [],
644
+ objectiveResults: [],
645
+ reportPath: null,
646
+ });
647
+ });
648
+
649
+ it("rejects an external worker result without objective outcomes", async () => {
650
+ mocks.consumeImpl = async () => ({
651
+ findings: [],
652
+ reportedFindings: ["A finding"],
653
+ findingsPath: session.findingsPath,
654
+ pocsPath: session.pocsPath,
655
+ });
656
+ await expect(
657
+ runPentestWorkflow({
658
+ target: "https://example.com",
659
+ model: {} as PentestWorkflowInput["model"],
660
+ session,
661
+ findingPolicy: "external",
662
+ }),
663
+ ).rejects.toThrow("incomplete swarm target");
664
+ });
665
+
666
+ it("fails rather than treating an incomplete external worker as no finding", async () => {
667
+ mocks.consumeImpl = async () => {
668
+ throw new Error("worker failed");
669
+ };
670
+ await expect(
671
+ runPentestWorkflow({
672
+ target: "https://example.com",
673
+ model: {} as PentestWorkflowInput["model"],
674
+ session,
675
+ findingPolicy: "external",
676
+ }),
677
+ ).rejects.toThrow("incomplete swarm target");
678
+ });
679
+ });
@@ -7,7 +7,7 @@ import type {
7
7
  ToolSet,
8
8
  } from "ai";
9
9
  import { hasToolCall } from "ai";
10
- import type { AgentHooks, Finding } from "../agents/offSecAgent";
10
+ import type { AgentHooks, Finding, FindingPolicy } from "../agents/offSecAgent";
11
11
  import {
12
12
  OffensiveSecurityAgent,
13
13
  PLAN_MODE_TOOL_NAMES,
@@ -39,6 +39,7 @@ import type {
39
39
  AIModel,
40
40
  CacheMetrics,
41
41
  OpenAIReasoningEffort,
42
+ ReasoningSetting,
42
43
  ThinkingEffort,
43
44
  UsageRecorder,
44
45
  } from "../ai";
@@ -64,6 +65,8 @@ import {
64
65
  finalizeManifest,
65
66
  getCompletedAgentIds,
66
67
  loadSubagentMessages,
68
+ loadSubagentObjectiveResults,
69
+ loadSubagentReportedFindings,
67
70
  readAgentManifest,
68
71
  reconcileManifestOnResume,
69
72
  type SwarmTarget,
@@ -99,6 +102,7 @@ export const DEFAULT_CONCURRENCY = 10;
99
102
  // ---------------------------------------------------------------------------
100
103
 
101
104
  export interface PentestWorkflowInput {
105
+ findingPolicy?: FindingPolicy;
102
106
  /** Live target URL — always required */
103
107
  target: string;
104
108
 
@@ -125,6 +129,7 @@ export interface PentestWorkflowInput {
125
129
 
126
130
  /** OpenAI reasoning effort for GPT/o-series reasoning models. */
127
131
  openAIReasoningEffort?: OpenAIReasoningEffort | null;
132
+ reasoning?: ReasoningSetting;
128
133
 
129
134
  /** Optional guidance prompt injected into discovery agent system prompts */
130
135
  prompt?: string;
@@ -170,6 +175,11 @@ export interface PentestWorkflowInput {
170
175
 
171
176
  export interface PentestWorkflowResult {
172
177
  findings: Finding[];
178
+ reportedFindings?: string[];
179
+ objectiveResults?: PentestResult["objectiveResults"];
180
+ submissionJson?: string;
181
+ solved?: boolean;
182
+ summary?: string;
173
183
  findingsPath: string;
174
184
  pocsPath: string;
175
185
  reportPath: string | null;
@@ -178,6 +188,7 @@ export interface PentestWorkflowResult {
178
188
  export type { SwarmTarget } from "../session/persistence";
179
189
 
180
190
  export interface PentestSwarmInput {
191
+ findingPolicy?: FindingPolicy;
181
192
  targets: SwarmTarget[];
182
193
  model: AIModel;
183
194
  session: SessionInfo;
@@ -192,6 +203,7 @@ export interface PentestSwarmInput {
192
203
  enableThinking?: boolean;
193
204
  thinkingEffort?: ThinkingEffort | null;
194
205
  openAIReasoningEffort?: OpenAIReasoningEffort | null;
206
+ reasoning?: ReasoningSetting;
195
207
  /**
196
208
  * X display (e.g. `":11"`) for every swarm sub-agent's browser, so a swarm
197
209
  * launched by an orchestrator on a per-endpoint virtual desktop keeps its
@@ -243,6 +255,7 @@ export interface PentestSwarmTargetInput {
243
255
  enableThinking?: boolean;
244
256
  thinkingEffort?: ThinkingEffort | null;
245
257
  openAIReasoningEffort?: OpenAIReasoningEffort | null;
258
+ reasoning?: ReasoningSetting;
246
259
  display?: string;
247
260
  onError?: (e: unknown) => void;
248
261
  /** The swarm's shared {@link AgentHooks}; merged with `seams.hooksForItem` for this target. */
@@ -278,6 +291,7 @@ export async function runPentestSwarmTarget(
278
291
  enableThinking,
279
292
  thinkingEffort,
280
293
  openAIReasoningEffort,
294
+ reasoning,
281
295
  display,
282
296
  onError,
283
297
  hooks: sharedHooks,
@@ -290,7 +304,23 @@ export async function runPentestSwarmTarget(
290
304
  const subagentId = entry.id;
291
305
  const subagentName = target.name ?? entry.name;
292
306
 
293
- if (completedIds.has(subagentId)) return null;
307
+ if (completedIds.has(subagentId)) {
308
+ if (sharedHooks.findingPolicy !== "external") return null;
309
+ const reportedFindings = loadSubagentReportedFindings(session, subagentId);
310
+ const objectiveResults = loadSubagentObjectiveResults(session, subagentId);
311
+ if (!reportedFindings || !objectiveResults) {
312
+ throw new Error(
313
+ `Cannot resume externally graded pentest: findings or objective results for completed agent ${subagentId} are unavailable`,
314
+ );
315
+ }
316
+ return {
317
+ findings: [],
318
+ reportedFindings,
319
+ objectiveResults,
320
+ findingsPath: session.findingsPath,
321
+ pocsPath: session.pocsPath,
322
+ };
323
+ }
294
324
 
295
325
  // Per-target AgentHooks: seams.hooksForItem can give this one target
296
326
  // (e.g. its own lease/backends, its own politeness budget) different
@@ -367,6 +397,7 @@ export async function runPentestSwarmTarget(
367
397
  enableThinking,
368
398
  thinkingEffort,
369
399
  openAIReasoningEffort,
400
+ reasoning,
370
401
  display,
371
402
  ...itemHooks,
372
403
  abortSignal: childSignal,
@@ -412,21 +443,44 @@ export async function runPentestSwarmTarget(
412
443
  enableThinking,
413
444
  thinkingEffort,
414
445
  openAIReasoningEffort,
446
+ reasoning,
415
447
  display,
416
448
  ...itemHooks,
417
449
  abortSignal: childSignal,
418
450
  });
419
451
 
420
452
  const result = await agent.consume();
453
+ if (
454
+ itemHooks.findingPolicy === "external" &&
455
+ (!Array.isArray(result.reportedFindings) ||
456
+ !Array.isArray(result.objectiveResults))
457
+ ) {
458
+ throw new Error(
459
+ `Externally graded pentest worker ${subagentId} ended without structured findings and objective results`,
460
+ );
461
+ }
421
462
 
422
463
  saveSubagentData(session, {
423
464
  agentName: subagentId,
424
465
  target: target.target,
425
466
  objective: objectiveStr,
426
467
  status: "completed",
427
- findingsCount: result.findings.length,
468
+ findingsCount: result.reportedFindings?.length ?? result.findings.length,
469
+ ...(itemHooks.findingPolicy === "external"
470
+ ? {
471
+ reportedFindings: result.reportedFindings,
472
+ objectiveResults: result.objectiveResults,
473
+ }
474
+ : {}),
428
475
  messages: [...previousMessages, ...lastMessages],
429
- systemPrompt: buildPentestSystemPrompt(session),
476
+ systemPrompt: buildPentestSystemPrompt(
477
+ session,
478
+ "orchestrator",
479
+ "default",
480
+ undefined,
481
+ undefined,
482
+ itemHooks.findingPolicy,
483
+ ),
430
484
  userPrompt: agent.userPrompt,
431
485
  });
432
486
  updateManifestEntryStatus(session, subagentId, "completed");
@@ -450,7 +504,14 @@ export async function runPentestSwarmTarget(
450
504
  status: "failed",
451
505
  error: error instanceof Error ? error.message : String(error),
452
506
  messages: [...previousMessages, ...lastMessages],
453
- systemPrompt: buildPentestSystemPrompt(session),
507
+ systemPrompt: buildPentestSystemPrompt(
508
+ session,
509
+ "orchestrator",
510
+ "default",
511
+ undefined,
512
+ undefined,
513
+ itemHooks.findingPolicy,
514
+ ),
454
515
  userPrompt: agent?.userPrompt ?? "",
455
516
  });
456
517
  updateManifestEntryStatus(session, subagentId, "failed");
@@ -497,6 +558,7 @@ export async function runPentestSwarm(
497
558
  enableThinking,
498
559
  thinkingEffort,
499
560
  openAIReasoningEffort,
561
+ reasoning,
500
562
  display,
501
563
  subagentSpawner,
502
564
  languageModelMiddleware,
@@ -522,6 +584,7 @@ export async function runPentestSwarm(
522
584
  usageRecorder,
523
585
  streamIdFactory,
524
586
  ...hooks,
587
+ findingPolicy: hooks?.findingPolicy ?? input.findingPolicy,
525
588
  };
526
589
 
527
590
  // Ops-typed by the seam; TargetedPentestAgent/OffensiveSecurityAgent only
@@ -558,6 +621,7 @@ export async function runPentestSwarm(
558
621
  enableThinking,
559
622
  thinkingEffort,
560
623
  openAIReasoningEffort,
624
+ reasoning,
561
625
  display,
562
626
  onError,
563
627
  hooks: sharedHooks,
@@ -574,6 +638,15 @@ export async function runPentestSwarm(
574
638
 
575
639
  finalizeManifest(session, manifestEntries, results);
576
640
 
641
+ if (
642
+ sharedHooks.findingPolicy === "external" &&
643
+ results.some((result) => result === null)
644
+ ) {
645
+ throw new Error(
646
+ "Externally graded pentest has an incomplete swarm target; findings cannot be aggregated",
647
+ );
648
+ }
649
+
577
650
  return results;
578
651
  }
579
652
 
@@ -609,10 +682,12 @@ export async function runPentestWorkflow(
609
682
  enableThinking,
610
683
  thinkingEffort,
611
684
  openAIReasoningEffort,
685
+ reasoning,
612
686
  prompt,
613
687
  threatModel,
614
688
  surfaceIntegrationEnabled,
615
689
  } = input;
690
+ const findingPolicy = input.hooks?.findingPolicy ?? input.findingPolicy;
616
691
 
617
692
  // Default registries mirror today's `FindingsRegistry.fromDirectory` resume
618
693
  // behavior (dedup against on-disk findings, model-backed semantic dedup).
@@ -626,6 +701,7 @@ export async function runPentestWorkflow(
626
701
  findings: () =>
627
702
  FindingsRegistry.fromDirectory(session.findingsPath, {
628
703
  model,
704
+ reasoning,
629
705
  authConfig,
630
706
  abortSignal,
631
707
  sessionId: session.id,
@@ -698,6 +774,7 @@ export async function runPentestWorkflow(
698
774
  onStepFinish,
699
775
  onCacheMetrics,
700
776
  openAIReasoningEffort,
777
+ reasoning,
701
778
  surfaceIntegrationEnabled,
702
779
  subagentSpawner: input.subagentSpawner,
703
780
  hooks: {
@@ -706,6 +783,7 @@ export async function runPentestWorkflow(
706
783
  usageRecorder: input.usageRecorder,
707
784
  streamIdFactory: input.streamIdFactory,
708
785
  ...input.hooks,
786
+ findingPolicy,
709
787
  },
710
788
  seams,
711
789
  });
@@ -720,12 +798,14 @@ export async function runPentestWorkflow(
720
798
  onStepFinish,
721
799
  onCacheMetrics,
722
800
  openAIReasoningEffort,
801
+ reasoning,
723
802
  hooks: {
724
803
  subagentSpawner: input.subagentSpawner,
725
804
  languageModelMiddleware: input.languageModelMiddleware,
726
805
  usageRecorder: input.usageRecorder,
727
806
  streamIdFactory: input.streamIdFactory,
728
807
  ...input.hooks,
808
+ findingPolicy,
729
809
  },
730
810
  });
731
811
  }
@@ -744,6 +824,16 @@ export async function runPentestWorkflow(
744
824
  }
745
825
 
746
826
  if (swarmTargets.length === 0) {
827
+ if (findingPolicy === "external") {
828
+ return {
829
+ findings: [],
830
+ reportedFindings: [],
831
+ objectiveResults: [],
832
+ findingsPath: session.findingsPath,
833
+ pocsPath: session.pocsPath,
834
+ reportPath: null,
835
+ };
836
+ }
747
837
  const report = buildPentestReport([], {
748
838
  target,
749
839
  model,
@@ -778,10 +868,15 @@ export async function runPentestWorkflow(
778
868
  const findingsRegistry = seams.registries.findings() as FindingsRegistry;
779
869
 
780
870
  const completedCount = getCompletedAgentIds(session).size;
871
+ let externalReportedFindings: string[] = [];
872
+ let externalObjectiveResults: NonNullable<
873
+ PentestResult["objectiveResults"]
874
+ > = [];
781
875
 
782
- if (completedCount < swarmTargets.length) {
783
- await runPentestSwarm({
876
+ if (findingPolicy === "external" || completedCount < swarmTargets.length) {
877
+ const swarmResults = await runPentestSwarm({
784
878
  targets: swarmTargets,
879
+ findingPolicy,
785
880
  model,
786
881
  session,
787
882
  authConfig,
@@ -793,6 +888,7 @@ export async function runPentestWorkflow(
793
888
  enableThinking,
794
889
  thinkingEffort,
795
890
  openAIReasoningEffort,
891
+ reasoning,
796
892
  subagentSpawner: input.subagentSpawner,
797
893
  languageModelMiddleware: input.languageModelMiddleware,
798
894
  usageRecorder: input.usageRecorder,
@@ -800,12 +896,33 @@ export async function runPentestWorkflow(
800
896
  hooks: input.hooks,
801
897
  seams,
802
898
  });
899
+ externalReportedFindings = swarmResults.flatMap(
900
+ (result) => result?.reportedFindings ?? [],
901
+ );
902
+ externalObjectiveResults = swarmResults.flatMap(
903
+ (result) => result?.objectiveResults ?? [],
904
+ );
803
905
  }
804
906
 
805
907
  if (abortSignal?.aborted) {
806
908
  throw new DOMException("Pentest aborted by user", "AbortError");
807
909
  }
808
910
 
911
+ if (findingPolicy === "external") {
912
+ eventBus?.emit("workflow-phase-complete", {
913
+ phase: "pentesting",
914
+ summary: { findingsCount: externalReportedFindings.length },
915
+ });
916
+ return {
917
+ findings: [],
918
+ reportedFindings: externalReportedFindings,
919
+ objectiveResults: externalObjectiveResults,
920
+ findingsPath: session.findingsPath,
921
+ pocsPath: session.pocsPath,
922
+ reportPath: null,
923
+ };
924
+ }
925
+
809
926
  // =========================================================================
810
927
  // Phase 3: Aggregate results
811
928
  // =========================================================================
@@ -881,6 +998,7 @@ async function runWhiteboxPhase(opts: {
881
998
  onStepFinish?: StreamTextOnStepFinishCallback<ToolSet>;
882
999
  onCacheMetrics?: (metrics: CacheMetrics) => void;
883
1000
  openAIReasoningEffort?: OpenAIReasoningEffort | null;
1001
+ reasoning?: ReasoningSetting;
884
1002
  surfaceIntegrationEnabled?: boolean;
885
1003
  subagentSpawner?: SubagentSpawner;
886
1004
  hooks?: AgentHooks;
@@ -896,6 +1014,7 @@ async function runWhiteboxPhase(opts: {
896
1014
  onStepFinish: opts.onStepFinish,
897
1015
  onCacheMetrics: opts.onCacheMetrics,
898
1016
  openAIReasoningEffort: opts.openAIReasoningEffort,
1017
+ reasoning: opts.reasoning,
899
1018
  surfaceIntegrationEnabled: opts.surfaceIntegrationEnabled,
900
1019
  subagentSpawner: opts.subagentSpawner,
901
1020
  hooks: opts.hooks,
@@ -931,6 +1050,7 @@ async function runBlackboxPhase(opts: {
931
1050
  onStepFinish?: StreamTextOnStepFinishCallback<ToolSet>;
932
1051
  onCacheMetrics?: (metrics: CacheMetrics) => void;
933
1052
  openAIReasoningEffort?: OpenAIReasoningEffort | null;
1053
+ reasoning?: ReasoningSetting;
934
1054
  }): Promise<SwarmTarget[]> {
935
1055
  let lastMessages: ModelMessage[] = [];
936
1056
 
@@ -954,6 +1074,7 @@ async function runBlackboxPhase(opts: {
954
1074
  },
955
1075
  onCacheMetrics: opts.onCacheMetrics,
956
1076
  openAIReasoningEffort: opts.openAIReasoningEffort,
1077
+ reasoning: opts.reasoning,
957
1078
  };
958
1079
 
959
1080
  const agent = new BlackboxAttackSurfaceAgent(agentInput);