@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,3 +1,4 @@
1
+ import { getSavedReasoning } from "../../../core/cli/reasoning";
1
2
  /**
2
3
  * Operator Dashboard
3
4
  *
@@ -29,14 +30,13 @@ import {
29
30
  type AgentMode,
30
31
  ALL_TOOL_NAMES,
31
32
  ASK_USER_QUESTIONS_TOOL_NAME,
33
+ type FindingPolicy,
32
34
  PLAN_MODE_TOOL_NAMES,
33
35
  SKILL_TOOL_NAMES,
34
36
  } from "../../../core/agents/offSecAgent";
35
37
  import {
36
38
  buildAuthConfig,
37
39
  getContextWindow,
38
- modelSupportsOpenAIReasoning,
39
- modelSupportsThinking,
40
40
  normalizeStepUsage,
41
41
  } from "../../../core/ai";
42
42
  import {
@@ -108,12 +108,15 @@ import {
108
108
  type DashboardStatus,
109
109
  filterOperatorAutocomplete,
110
110
  resolveAbortAction,
111
+ resolveActiveFindingPolicy,
111
112
  resolveInputFocused,
112
113
  resolveKeyboardShortcut,
113
114
  resolveOperatorAgentMode,
115
+ resolveOperatorFindingPolicy,
114
116
  resolveOperatorStrikeMode,
115
117
  resolveSubmit,
116
118
  routeCommand,
119
+ shouldHandleQueuedMessageKey,
117
120
  } from "./logic";
118
121
  import {
119
122
  createQueuedMessage,
@@ -159,6 +162,8 @@ export default function OperatorDashboard({
159
162
  target?: string;
160
163
  operatorMode?: OperatorMode;
161
164
  strikeMode?: boolean;
165
+ lanceMode?: boolean;
166
+ findingPolicy?: FindingPolicy;
162
167
  sandbox?: boolean;
163
168
  taskDriven?: boolean;
164
169
  headers?: Record<string, string>;
@@ -177,8 +182,8 @@ export default function OperatorDashboard({
177
182
  usageStore,
178
183
  markExecuted,
179
184
  setSessionCwd,
180
- reasoningEnabled,
181
- openAIReasoningEffort,
185
+ reasoning: preferredReasoning,
186
+ setReasoning,
182
187
  } = useAgent();
183
188
  const {
184
189
  autocompleteOptions: allAutocompleteOptions,
@@ -198,6 +203,11 @@ export default function OperatorDashboard({
198
203
  const { refocusPromptIfNoActiveEditor } = useFocus();
199
204
  const herdr = useHerdr();
200
205
  const { enabled: obfuscateEnabled } = useObfuscation();
206
+ const [lanceMode, setLanceMode] = useState(
207
+ sessionId
208
+ ? false
209
+ : (initialConfig?.lanceMode ?? config.data.lanceMode ?? false),
210
+ );
201
211
  const initialStrikeModeRef = useRef(
202
212
  resolveOperatorStrikeMode({
203
213
  resuming: !!sessionId,
@@ -206,18 +216,36 @@ export default function OperatorDashboard({
206
216
  }),
207
217
  );
208
218
  const [strikeMode, setStrikeMode] = useState(initialStrikeModeRef.current);
219
+ const initialFindingPolicyRef = useRef(
220
+ resolveOperatorFindingPolicy({
221
+ resuming: !!sessionId,
222
+ strikeMode: initialStrikeModeRef.current,
223
+ lanceMode,
224
+ routeOverride: initialConfig?.findingPolicy,
225
+ persistedDefault: config.data.findingPolicy,
226
+ }),
227
+ );
228
+ const [findingPolicy, setFindingPolicy] = useState(
229
+ initialFindingPolicyRef.current,
230
+ );
209
231
 
210
232
  // biome-ignore lint/correctness/useExhaustiveDependencies: `skillsVersion` is an intentional cache-buster — forces recomputation when skills are refreshed even though `skillsRegistry` (stable ref) hasn't changed.
211
233
  const autocompleteOptions = useMemo(() => {
212
234
  const commandOptions = filterOperatorAutocomplete(
213
235
  allAutocompleteOptions,
214
- ).filter((option) => !strikeMode || option.value !== "/pentest");
236
+ ).filter(
237
+ (option) =>
238
+ (!strikeMode || option.value !== "/pentest") &&
239
+ (!lanceMode ||
240
+ !["/pentest", "/plan", "/skills"].includes(option.value)),
241
+ );
215
242
  const skillOptions = skillsRegistry
216
243
  .list()
217
244
  .filter(
218
245
  (skill) =>
219
- !strikeMode ||
220
- (skill.slug !== "pentest" && skill.slug !== "threat-model"),
246
+ !lanceMode &&
247
+ (!strikeMode ||
248
+ (skill.slug !== "pentest" && skill.slug !== "threat-model")),
221
249
  )
222
250
  .map((skill) => {
223
251
  const slug = `/${skill.slug}`;
@@ -235,7 +263,13 @@ export default function OperatorDashboard({
235
263
  },
236
264
  ];
237
265
  return [...commandOptions, ...operatorOnlyOptions, ...skillOptions];
238
- }, [allAutocompleteOptions, skillsRegistry, skillsVersion, strikeMode]);
266
+ }, [
267
+ allAutocompleteOptions,
268
+ skillsRegistry,
269
+ skillsVersion,
270
+ strikeMode,
271
+ lanceMode,
272
+ ]);
239
273
 
240
274
  // Session state
241
275
  const [session, setSession] = useState<SessionInfo | null>(null);
@@ -360,6 +394,11 @@ export default function OperatorDashboard({
360
394
  const agentMode: AgentMode = resolveOperatorAgentMode(
361
395
  operatorMode,
362
396
  strikeMode,
397
+ lanceMode,
398
+ );
399
+ const activeFindingPolicy = resolveActiveFindingPolicy(
400
+ agentMode,
401
+ findingPolicy,
363
402
  );
364
403
  const requireApproval = operatorMode === "manual";
365
404
  // Plan mode review state
@@ -418,6 +457,32 @@ export default function OperatorDashboard({
418
457
  };
419
458
  }, []);
420
459
 
460
+ const restoredReasoningSession = useRef<string | null>(null);
461
+ const savedReasoning = session?.config?.modelReasoning;
462
+ const restoredPreference =
463
+ savedReasoning?.model === model.id
464
+ ? getSavedReasoning(model.id, {
465
+ customProviders: config.data.customProviders,
466
+ modelReasoning: { [model.id]: savedReasoning.setting },
467
+ })
468
+ : undefined;
469
+ // An auto-started resumed turn can precede the preference write completing.
470
+ const reasoning =
471
+ session && restoredReasoningSession.current !== session.id
472
+ ? (restoredPreference ?? preferredReasoning)
473
+ : preferredReasoning;
474
+ useEffect(() => {
475
+ if (!session || restoredReasoningSession.current === session.id) return;
476
+ if (
477
+ restoredPreference !== undefined &&
478
+ restoredPreference !== preferredReasoning
479
+ ) {
480
+ setReasoning(restoredPreference);
481
+ } else {
482
+ restoredReasoningSession.current = session.id;
483
+ }
484
+ }, [session, restoredPreference, preferredReasoning, setReasoning]);
485
+
421
486
  // Load existing session or initialise operator state for a new one.
422
487
  // New sessions are created lazily by the agent on the first runAgent call.
423
488
  useEffect(() => {
@@ -426,11 +491,22 @@ export default function OperatorDashboard({
426
491
  if (sessionId) {
427
492
  const s = await sessions.get(sessionId);
428
493
  setSession(s);
494
+ setLanceMode(
495
+ existsSync(join(s.rootPath, "lance-result.json")) ||
496
+ (s.config?.operatorSettings?.lanceMode ?? false),
497
+ );
429
498
  setSessionCwd(s.rootPath);
430
- setStrikeMode(
431
- resolveOperatorStrikeMode({
499
+ const resumedStrikeMode = resolveOperatorStrikeMode({
500
+ resuming: true,
501
+ savedSessionValue: s.config?.operatorSettings?.strikeMode,
502
+ });
503
+ setStrikeMode(resumedStrikeMode);
504
+ setFindingPolicy(
505
+ resolveOperatorFindingPolicy({
432
506
  resuming: true,
433
- savedSessionValue: s.config?.operatorSettings?.strikeMode,
507
+ strikeMode: resumedStrikeMode,
508
+ lanceMode: s.config?.operatorSettings?.lanceMode,
509
+ savedSessionValue: s.config?.operatorSettings?.findingPolicy,
434
510
  }),
435
511
  );
436
512
 
@@ -516,6 +592,7 @@ export default function OperatorDashboard({
516
592
  // New session — just set up operator config; the agent creates the
517
593
  // session on the first runAgent call.
518
594
  setStrikeMode(initialStrikeModeRef.current);
595
+ setFindingPolicy(initialFindingPolicyRef.current);
519
596
  const newMode = initialConfig?.operatorMode ?? "manual";
520
597
  setOperatorMode(newMode);
521
598
  const requireApproval = newMode === "manual";
@@ -855,7 +932,9 @@ export default function OperatorDashboard({
855
932
  handlers: runEventProjections.handlers,
856
933
  });
857
934
 
858
- const skillsCatalog = skillsRegistry.buildCatalog() || undefined;
935
+ const skillsCatalog = lanceMode
936
+ ? undefined
937
+ : skillsRegistry.buildCatalog() || undefined;
859
938
 
860
939
  const commonInput = {
861
940
  prompt: prompt ?? "",
@@ -871,14 +950,13 @@ export default function OperatorDashboard({
871
950
  ...SKILL_TOOL_NAMES,
872
951
  ] as string[],
873
952
  mode: agentMode,
953
+ lance: lanceMode ? { interactive: true } : undefined,
954
+ findingPolicy: activeFindingPolicy,
874
955
  authConfig: buildAuthConfig(config.data),
875
956
  approvalGate: approvalGateRef.current,
876
957
  commandCancelHandle: cancelHandleRef.current,
877
958
  skillsRegistry,
878
- enableThinking: reasoningEnabled && modelSupportsThinking(model.id),
879
- openAIReasoningEffort: modelSupportsOpenAIReasoning(model.id)
880
- ? openAIReasoningEffort
881
- : undefined,
959
+ reasoning,
882
960
  surfaceIntegrationEnabled: config.data?.surfaceIntegrationEnabled,
883
961
  promptInjectionLibrarySource:
884
962
  initialConfig?.promptInjectionLibrarySource ??
@@ -905,34 +983,37 @@ export default function OperatorDashboard({
905
983
  try {
906
984
  let agentResult: RunAgentResult;
907
985
 
908
- const systemPrompt = buildOperatorSystemPrompt(
909
- initialConfig?.target,
910
- operatorState,
911
- agentMode,
912
- {
913
- requireApproval,
914
- sandboxMode: !!initialConfig?.sandbox,
915
- skillsCatalog,
916
- planFilePath: sessionRef.current
917
- ? getPlanFilePath(sessionRef.current.rootPath)
918
- : undefined,
919
- existingPlanContent:
920
- agentMode === "plan" &&
921
- planRejectedRef.current &&
922
- sessionRef.current
923
- ? readPlan(sessionRef.current.rootPath)
924
- : null,
925
- approvedPlanContent:
926
- agentMode !== "plan" ? approvedPlanRef.current : null,
927
- taskDriven:
928
- session?.config?.taskDriven ?? initialConfig?.taskDriven,
929
- },
930
- );
986
+ const systemPrompt = lanceMode
987
+ ? undefined
988
+ : buildOperatorSystemPrompt(
989
+ initialConfig?.target,
990
+ operatorState,
991
+ agentMode,
992
+ {
993
+ requireApproval,
994
+ sandboxMode: !!initialConfig?.sandbox,
995
+ skillsCatalog,
996
+ planFilePath: sessionRef.current
997
+ ? getPlanFilePath(sessionRef.current.rootPath)
998
+ : undefined,
999
+ existingPlanContent:
1000
+ agentMode === "plan" &&
1001
+ planRejectedRef.current &&
1002
+ sessionRef.current
1003
+ ? readPlan(sessionRef.current.rootPath)
1004
+ : null,
1005
+ approvedPlanContent:
1006
+ agentMode !== "plan" ? approvedPlanRef.current : null,
1007
+ taskDriven:
1008
+ session?.config?.taskDriven ?? initialConfig?.taskDriven,
1009
+ findingPolicy: activeFindingPolicy,
1010
+ },
1011
+ );
931
1012
 
932
1013
  if (session) {
933
1014
  agentResult = await agentClient.run({
934
1015
  ...commonInput,
935
- system: systemPrompt,
1016
+ ...(lanceMode ? {} : { system: systemPrompt }),
936
1017
  session,
937
1018
  });
938
1019
  } else {
@@ -945,6 +1026,8 @@ export default function OperatorDashboard({
945
1026
  requireApproval,
946
1027
  enableSuggestions: true,
947
1028
  strikeMode,
1029
+ lanceMode,
1030
+ findingPolicy,
948
1031
  },
949
1032
  agentCwd: initialConfig?.sandbox ? undefined : process.cwd(),
950
1033
  codebasePath: initialConfig?.sandbox ? undefined : process.cwd(),
@@ -960,7 +1043,7 @@ export default function OperatorDashboard({
960
1043
  };
961
1044
  agentResult = await agentClient.run({
962
1045
  ...commonInput,
963
- system: systemPrompt,
1046
+ ...(lanceMode ? {} : { system: systemPrompt }),
964
1047
  sessionConfig,
965
1048
  onNameGenerated: (name: string) => {
966
1049
  pendingNameRef.current = name;
@@ -977,6 +1060,18 @@ export default function OperatorDashboard({
977
1060
  setSessionCwd(created.rootPath);
978
1061
  }
979
1062
 
1063
+ const lanceResult = agentResult.lance;
1064
+ if (lanceResult && lanceResult.outcome !== "awaiting_input") {
1065
+ setMessages((prev) => [
1066
+ ...prev,
1067
+ {
1068
+ role: "system",
1069
+ content: `Lance: ${lanceResult.outcome}. ${lanceResult.reason}`,
1070
+ createdAt: new Date(),
1071
+ },
1072
+ ]);
1073
+ }
1074
+
980
1075
  // Sync conversationRef from the agent's persisted state.
981
1076
  // messages.json is the single source of truth — after summarization
982
1077
  // the agent discards stale history, so reading back avoids
@@ -1082,13 +1177,15 @@ export default function OperatorDashboard({
1082
1177
  initialConfig?.headers,
1083
1178
  initialConfig?.promptInjectionLibrarySource,
1084
1179
  strikeMode,
1180
+ lanceMode,
1181
+ findingPolicy,
1182
+ activeFindingPolicy,
1085
1183
  route.data,
1086
1184
  setSessionCwd,
1087
1185
  skillsRegistry.buildCatalog,
1088
1186
  requireApproval,
1089
1187
  skillsRegistry,
1090
- reasoningEnabled,
1091
- openAIReasoningEffort,
1188
+ reasoning,
1092
1189
  usageStore,
1093
1190
  markExecuted,
1094
1191
  ],
@@ -1365,6 +1462,15 @@ This three-phase flow is specific to the TUI \`/threat-model\` command. The same
1365
1462
  async (command: string) => {
1366
1463
  const action = routeCommand(command, resolveSkillContent);
1367
1464
 
1465
+ if (
1466
+ lanceMode &&
1467
+ ["run-skill", "show-plan", "execute-command"].includes(action.type)
1468
+ ) {
1469
+ addSystemMessage(
1470
+ "Lance accepts direct objectives only. Start a new session to use production workflows.",
1471
+ );
1472
+ return;
1473
+ }
1368
1474
  switch (action.type) {
1369
1475
  case "show-models":
1370
1476
  showModelPicker();
@@ -1449,6 +1555,7 @@ This three-phase flow is specific to the TUI \`/threat-model\` command. The same
1449
1555
  addSystemMessage,
1450
1556
  handleHeadersSlash,
1451
1557
  strikeMode,
1558
+ lanceMode,
1452
1559
  ],
1453
1560
  );
1454
1561
 
@@ -1634,11 +1741,16 @@ This three-phase flow is specific to the TUI \`/threat-model\` command. The same
1634
1741
 
1635
1742
  // Keyboard shortcuts
1636
1743
  useKeyboard((key) => {
1744
+ const dialogOpen =
1745
+ stack.length > 0 || externalDialogOpen || !!pendingQuestions;
1637
1746
  // Queue navigation: handle before general shortcuts
1638
1747
  if (
1639
- status === "running" &&
1640
- queuedMessages.length > 0 &&
1641
- !inputValue.trim()
1748
+ shouldHandleQueuedMessageKey(
1749
+ status,
1750
+ queuedMessages.length,
1751
+ inputValue,
1752
+ dialogOpen,
1753
+ )
1642
1754
  ) {
1643
1755
  if (key.name === "up") {
1644
1756
  key.preventDefault?.();
@@ -1765,9 +1877,6 @@ This three-phase flow is specific to the TUI \`/threat-model\` command. The same
1765
1877
 
1766
1878
  // Treat the questions form as a dialog so that dashboard-level shortcuts
1767
1879
  // (Shift+Tab cycle-mode, etc.) don't fire while the form owns the keyboard.
1768
- const dialogOpen =
1769
- stack.length > 0 || externalDialogOpen || !!pendingQuestions;
1770
-
1771
1880
  // Ctrl+A to open subagent dialog (skip if another dialog is open)
1772
1881
  if (
1773
1882
  key.ctrl &&
@@ -1925,7 +2034,14 @@ This three-phase flow is specific to the TUI \`/threat-model\` command. The same
1925
2034
  >
1926
2035
  <box flexDirection="row" gap={2}>
1927
2036
  <text fg={colors.text}>{session?.name ?? "New Session"}</text>
1928
- {strikeMode && <text fg={colors.warning}>STRIKE</text>}
2037
+ {lanceMode ? (
2038
+ <text fg={colors.warning}>LANCE</text>
2039
+ ) : (
2040
+ strikeMode && <text fg={colors.warning}>STRIKE</text>
2041
+ )}
2042
+ {activeFindingPolicy === "external" && (
2043
+ <text fg={colors.textMuted}>JUDGE OFF</text>
2044
+ )}
1929
2045
  {(session?.targets[0] || initialConfig?.target) && (
1930
2046
  <>
1931
2047
  <text fg={colors.textMuted}>•</text>
@@ -6,12 +6,15 @@ import {
6
6
  type DashboardStatus,
7
7
  filterOperatorAutocomplete,
8
8
  resolveAbortAction,
9
+ resolveActiveFindingPolicy,
9
10
  resolveInputFocused,
10
11
  resolveKeyboardShortcut,
11
12
  resolveOperatorAgentMode,
13
+ resolveOperatorFindingPolicy,
12
14
  resolveOperatorStrikeMode,
13
15
  resolveSubmit,
14
16
  routeCommand,
17
+ shouldHandleQueuedMessageKey,
15
18
  } from "./logic";
16
19
 
17
20
  // ---------------------------------------------------------------------------
@@ -23,6 +26,7 @@ const allOptions: AutocompleteOption[] = [
23
26
  { value: "/pentest", label: "/pentest", description: "Run a pentest" },
24
27
  { value: "/help", label: "/help", description: "Show help" },
25
28
  { value: "/models", label: "/models", description: "Switch model" },
29
+ { value: "/effort", label: "/effort", description: "Adjust model effort" },
26
30
  { value: "/skills", label: "/skills", description: "View skills" },
27
31
  {
28
32
  value: "/advanced",
@@ -36,10 +40,11 @@ const allOptions: AutocompleteOption[] = [
36
40
  // ---------------------------------------------------------------------------
37
41
 
38
42
  describe("filterOperatorAutocomplete", () => {
39
- it("includes allowed commands (/models, /skills, /advanced)", () => {
43
+ it("includes allowed commands (/models, /effort, /skills, /advanced)", () => {
40
44
  const result = filterOperatorAutocomplete(allOptions);
41
45
  const values = result.map((o) => o.value);
42
46
  expect(values).toContain("/models");
47
+ expect(values).toContain("/effort");
43
48
  expect(values).toContain("/skills");
44
49
  expect(values).toContain("/advanced");
45
50
  });
@@ -52,7 +57,7 @@ describe("filterOperatorAutocomplete", () => {
52
57
 
53
58
  it("returns only allowed commands", () => {
54
59
  const result = filterOperatorAutocomplete(allOptions);
55
- expect(result).toHaveLength(5);
60
+ expect(result).toHaveLength(6);
56
61
  });
57
62
 
58
63
  it("preserves description on allowed commands", () => {
@@ -130,6 +135,115 @@ describe("resolveOperatorStrikeMode", () => {
130
135
  });
131
136
  });
132
137
 
138
+ describe("resolveOperatorFindingPolicy", () => {
139
+ it("defaults new sessions to Apex finding review", () => {
140
+ expect(
141
+ resolveOperatorFindingPolicy({ resuming: false, strikeMode: true }),
142
+ ).toBe("native");
143
+ });
144
+
145
+ it("uses the saved preference for new Strike sessions", () => {
146
+ expect(
147
+ resolveOperatorFindingPolicy({
148
+ resuming: false,
149
+ strikeMode: true,
150
+ persistedDefault: "external",
151
+ }),
152
+ ).toBe("external");
153
+ });
154
+
155
+ it("lets an explicit workflow override the saved preference", () => {
156
+ expect(
157
+ resolveOperatorFindingPolicy({
158
+ resuming: false,
159
+ strikeMode: true,
160
+ routeOverride: "native",
161
+ persistedDefault: "external",
162
+ }),
163
+ ).toBe("native");
164
+ });
165
+
166
+ it("restores the policy saved with a resumed Strike session", () => {
167
+ expect(
168
+ resolveOperatorFindingPolicy({
169
+ resuming: true,
170
+ strikeMode: true,
171
+ savedSessionValue: "external",
172
+ persistedDefault: "native",
173
+ }),
174
+ ).toBe("external");
175
+ });
176
+
177
+ it("keeps legacy resumed sessions native despite a newer global preference", () => {
178
+ expect(
179
+ resolveOperatorFindingPolicy({
180
+ resuming: true,
181
+ strikeMode: true,
182
+ persistedDefault: "external",
183
+ }),
184
+ ).toBe("native");
185
+ });
186
+
187
+ it("keeps standard sessions native despite external preferences or overrides", () => {
188
+ expect(
189
+ resolveOperatorFindingPolicy({
190
+ resuming: false,
191
+ strikeMode: false,
192
+ routeOverride: "external",
193
+ persistedDefault: "external",
194
+ }),
195
+ ).toBe("native");
196
+ expect(
197
+ resolveOperatorFindingPolicy({
198
+ resuming: true,
199
+ strikeMode: false,
200
+ savedSessionValue: "external",
201
+ }),
202
+ ).toBe("native");
203
+ });
204
+ });
205
+
206
+ describe("resolveActiveFindingPolicy", () => {
207
+ it("preserves the judge toggle in Strike and Lance", () => {
208
+ expect(resolveActiveFindingPolicy("lance", "external")).toBe("external");
209
+ expect(
210
+ resolveOperatorFindingPolicy({
211
+ resuming: false,
212
+ strikeMode: false,
213
+ lanceMode: true,
214
+ persistedDefault: "external",
215
+ }),
216
+ ).toBe("external");
217
+ expect(
218
+ resolveOperatorFindingPolicy({
219
+ resuming: true,
220
+ strikeMode: false,
221
+ lanceMode: true,
222
+ savedSessionValue: "external",
223
+ }),
224
+ ).toBe("external");
225
+ expect(resolveActiveFindingPolicy("fast-strike", "external")).toBe(
226
+ "external",
227
+ );
228
+ expect(resolveActiveFindingPolicy("default", "external")).toBe("native");
229
+ expect(resolveActiveFindingPolicy("plan", "external")).toBe("native");
230
+ });
231
+ });
232
+
233
+ // ---------------------------------------------------------------------------
234
+ // Queue navigation
235
+ // ---------------------------------------------------------------------------
236
+
237
+ describe("shouldHandleQueuedMessageKey", () => {
238
+ it("leaves queue navigation to the dashboard only when no dialog owns the keyboard", () => {
239
+ expect(shouldHandleQueuedMessageKey("running", 2, "", false)).toBe(true);
240
+ expect(shouldHandleQueuedMessageKey("running", 2, "", true)).toBe(false);
241
+ expect(
242
+ shouldHandleQueuedMessageKey("running", 2, " /advanced ", false),
243
+ ).toBe(false);
244
+ });
245
+ });
246
+
133
247
  // ---------------------------------------------------------------------------
134
248
  // resolveSubmit
135
249
  // ---------------------------------------------------------------------------
@@ -195,6 +309,17 @@ describe("routeCommand", () => {
195
309
  expect(routeCommand("/MODELS", noSkill)).toEqual({ type: "show-models" });
196
310
  });
197
311
 
312
+ it.each([
313
+ "/effort",
314
+ "/EFFORT",
315
+ ])("routes %s to the picker before resolving skills", (command) => {
316
+ const resolveSkill = vi.fn(() => "An effort skill");
317
+ expect(routeCommand(command, resolveSkill)).toEqual({
318
+ type: "show-models",
319
+ });
320
+ expect(resolveSkill).not.toHaveBeenCalled();
321
+ });
322
+
198
323
  it("routes a skill command to run-skill with slug", () => {
199
324
  const resolveSkill = (cmd: string) =>
200
325
  cmd === "/sql-injection" ? "Perform SQL injection testing" : null;
@@ -623,6 +748,17 @@ describe("buildOperatorSystemPrompt", () => {
623
748
  expect(prompt).toContain("account for each viable primitive");
624
749
  });
625
750
 
751
+ it("keeps Strike's research loop while leaving finding review to the caller", () => {
752
+ const prompt = buildOperatorSystemPrompt(target, state, "fast-strike", {
753
+ findingPolicy: "external",
754
+ });
755
+ expect(prompt).toContain("# Strike Mode");
756
+ expect(prompt).toContain("requested output format");
757
+ expect(prompt).not.toContain("document_vulnerability");
758
+ expect(prompt).not.toContain("document confirmed vulnerabilities");
759
+ expect(prompt).not.toContain("official report is generated automatically");
760
+ });
761
+
626
762
  it("does not include Strike Mode instructions in the standard mode", () => {
627
763
  const prompt = buildOperatorSystemPrompt(target, state, "default");
628
764
  expect(prompt).not.toContain("# Strike Mode");
@@ -751,3 +887,9 @@ describe("resolveInputFocused", () => {
751
887
  });
752
888
 
753
889
  // ---------------------------------------------------------------------------
890
+
891
+ it("keeps Lance independent of Strike and planning", () => {
892
+ expect(resolveOperatorAgentMode("manual", false, true)).toBe("lance");
893
+ expect(resolveOperatorAgentMode("auto", true, true)).toBe("lance");
894
+ expect(resolveOperatorAgentMode("plan", false, true)).toBe("lance");
895
+ });