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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/build/{agent-yby11zaq.js → agent-68t2wzmc.js} +6 -6
  2. package/build/agent-jhekpern.js +13 -0
  3. package/build/{agent-ayreaj6z.js → agent-qvreg8a4.js} +5 -5
  4. package/build/{agent-runs-g37vkrjr.js → agent-runs-30dvbvmm.js} +18 -18
  5. package/build/agent-vybw255f.js +13 -0
  6. package/build/{apps-xn6km9yn.js → apps-na0y0992.js} +13 -13
  7. package/build/{auth-d105wc64.js → auth-dz2c5thx.js} +13 -13
  8. package/build/{blackboxAgent-s56sgx9b.js → blackboxAgent-2mwvaajb.js} +6 -6
  9. package/build/{blackboxPentest-ps78v3w7.js → blackboxPentest-s2mfze9p.js} +17 -12
  10. package/build/{cli-kb21qhvm.js → cli-1pmfnz1a.js} +4 -3
  11. package/build/{cli-ae8jqxqr.js → cli-563yntrb.js} +2 -2
  12. package/build/{cli-7eea4e2y.js → cli-5ab346kd.js} +6 -6
  13. package/build/{cli-1ve18npr.js → cli-72y76bpg.js} +3 -2
  14. package/build/{cli-fy6vh2a7.js → cli-98wmmphf.js} +1 -1
  15. package/build/{cli-c05y640n.js → cli-9jscnhtg.js} +1 -1
  16. package/build/{cli-3sp6nd8d.js → cli-9kk9tm58.js} +2 -2
  17. package/build/{cli-d37a42dc.js → cli-9pz9wjt2.js} +101 -18
  18. package/build/{cli-dpsw2fe6.js → cli-a6x4jm5a.js} +476 -193
  19. package/build/{cli-az3xq70v.js → cli-cfrdr25z.js} +1 -1
  20. package/build/{cli-ngq9dr8n.js → cli-fdrrbb9p.js} +3 -3
  21. package/build/{cli-k2nwxta0.js → cli-t09zj814.js} +2 -2
  22. package/build/{cli-qgz1q9g5.js → cli-t4q4t4dx.js} +223 -76
  23. package/build/{cli-xtc2t6me.js → cli-vdd1s3x9.js} +103 -39
  24. package/build/{cli-gx71jcke.js → cli-vn4cwac3.js} +1 -1
  25. package/build/{cli-65g6rcat.js → cli-w9nw2a4z.js} +1 -1
  26. package/build/{cli-n2en7j5b.js → cli-z7ysyrr4.js} +1 -1
  27. package/build/{cli-709rvekb.js → cli-zbbtr4sb.js} +4 -2
  28. package/build/cli.js +70 -40
  29. package/build/{config-zpqkd5cz.js → config-nkzm9arw.js} +1 -1
  30. package/build/{fastStrike-mcjdvwez.js → fastStrike-16jw1s61.js} +67 -15
  31. package/build/{fixes-kytx5dmj.js → fixes-6wxk22v5.js} +13 -13
  32. package/build/{index-4vqdgb42.js → index-36p56pcf.js} +1473 -1217
  33. package/build/{index-je6hcsqr.js → index-6nejymsc.js} +1 -1
  34. package/build/{index-fv64eb32.js → index-asm4y7e4.js} +2 -2
  35. package/build/{index-n65z6v7b.js → index-bfmn52za.js} +6 -6
  36. package/build/{index-qv4cc65p.js → index-bt4r13q7.js} +13 -13
  37. package/build/{index-dr3tg1w3.js → index-gfq1rp1q.js} +1 -1
  38. package/build/{index-wqetp6gq.js → index-j9pxgrzd.js} +12 -7
  39. package/build/{index-r9yjkfrv.js → index-n9kapn1x.js} +4 -4
  40. package/build/{index-ns4m4s01.js → index-tr6evhyg.js} +4 -4
  41. package/build/{issues-eb3q9d3a.js → issues-7rmf4ker.js} +13 -13
  42. package/build/{knowledge-fqsvtx0f.js → knowledge-s2sxh8nb.js} +13 -13
  43. package/build/lib/{chunk-1azdancq.js → chunk-0dnx79b1.js} +1 -1
  44. package/build/lib/{chunk-4hwzqthr.js → chunk-161ak2am.js} +3 -2
  45. package/build/lib/{chunk-z137x08b.js → chunk-1rqbhx4b.js} +7 -5
  46. package/build/lib/{chunk-1yct6cra.js → chunk-3hjvdf67.js} +1 -1
  47. package/build/lib/{chunk-r2dkp4tb.js → chunk-5j8pp0xf.js} +1 -1
  48. package/build/lib/{chunk-3c00n0cy.js → chunk-77se1d35.js} +8 -3
  49. package/build/lib/{chunk-yrnrbjes.js → chunk-7tft4b8b.js} +4 -2
  50. package/build/lib/{chunk-w66ce82a.js → chunk-8njjwwcw.js} +4 -2
  51. package/build/lib/{chunk-v3dk9j7k.js → chunk-aq9eyxas.js} +80 -21
  52. package/build/lib/{chunk-9tt2021m.js → chunk-d05cvnv8.js} +9 -9
  53. package/build/lib/{chunk-e8yy2k73.js → chunk-f3sq4896.js} +28 -2
  54. package/build/lib/{chunk-1fy8azee.js → chunk-fasqtjq1.js} +1 -1
  55. package/build/lib/{chunk-1jvwqnem.js → chunk-gm3b9j4y.js} +104 -40
  56. package/build/lib/{chunk-r4j8ybn9.js → chunk-hys7nemr.js} +87 -36
  57. package/build/lib/{chunk-7pwa92rn.js → chunk-j9668h16.js} +1 -1
  58. package/build/lib/{chunk-ba41jh41.js → chunk-kcw4cpr3.js} +418 -133
  59. package/build/lib/{chunk-z96pbjhd.js → chunk-m4swxy5r.js} +13 -6
  60. package/build/lib/{chunk-xhdpgpkm.js → chunk-mbsde9d0.js} +2 -2
  61. package/build/lib/{chunk-9yksqgzr.js → chunk-mpvjfvz0.js} +20 -13
  62. package/build/lib/{chunk-2qsq76xq.js → chunk-qkz5sm0d.js} +128 -34
  63. package/build/lib/{chunk-nhfax4gb.js → chunk-rfyjqybp.js} +1 -1
  64. package/build/lib/{chunk-mg03jdmt.js → chunk-vsnwzne5.js} +1 -1
  65. package/build/lib/{chunk-acgbyf5h.js → chunk-ydxhkgjw.js} +3 -2
  66. package/build/lib/{chunk-bb0ekhkw.js → chunk-z8kej0sp.js} +69 -18
  67. package/build/lib/core/agents/agentRuntime.d.ts +2 -1
  68. package/build/lib/core/agents/defineAgent.d.ts +3 -2
  69. package/build/lib/core/agents/offSecAgent/index.d.ts +3 -1
  70. package/build/lib/core/agents/offSecAgent/index.js +10 -8
  71. package/build/lib/core/agents/offSecAgent/prompt.d.ts +3 -1
  72. package/build/lib/core/agents/offSecAgent/subagentSpawner.d.ts +2 -1
  73. package/build/lib/core/agents/offSecAgent/subagentSpawner.js +5 -5
  74. package/build/lib/core/agents/offSecAgent/tools/index.js +4 -4
  75. package/build/lib/core/agents/offSecAgent/tools/listMemories.d.ts +11 -1
  76. package/build/lib/core/agents/offSecAgent/tools/reportError.d.ts +2 -1
  77. package/build/lib/core/agents/offSecAgent/tools/runPentestWorkflow.d.ts +20 -1
  78. package/build/lib/core/agents/offSecAgent/tools/spawnPentestAgent.d.ts +4 -7
  79. package/build/lib/core/agents/offSecAgent/tools/spawnPentestSwarm.d.ts +7 -1
  80. package/build/lib/core/agents/offSecAgent/tools/types.d.ts +2 -1
  81. package/build/lib/core/agents/offSecAgent/types.d.ts +7 -1
  82. package/build/lib/core/agents/specialized/attackSurface/blackboxAgent.js +9 -9
  83. package/build/lib/core/agents/specialized/attackSurface/schemas.js +9 -9
  84. package/build/lib/core/agents/specialized/authenticationAgent/index.js +10 -10
  85. package/build/lib/core/agents/specialized/codeAgent/agent.js +9 -9
  86. package/build/lib/core/agents/specialized/codeAgent/prompts.d.ts +1 -0
  87. package/build/lib/core/agents/specialized/findingJudge/agent.js +8 -8
  88. package/build/lib/core/agents/specialized/findingJudge/index.js +3 -3
  89. package/build/lib/core/agents/specialized/patching/index.js +10 -10
  90. package/build/lib/core/agents/specialized/pentest/agent.d.ts +18 -4
  91. package/build/lib/core/agents/specialized/pentest/agent.js +11 -9
  92. package/build/lib/core/agents/specialized/whiteboxAttackSurface/index.js +9 -9
  93. package/build/lib/core/ai/ai.d.ts +23 -6
  94. package/build/lib/core/ai/ai.js +2 -2
  95. package/build/lib/core/ai/index.js +2 -2
  96. package/build/lib/core/ai/models/claude-capabilities.d.ts +3 -2
  97. package/build/lib/core/ai/models/concentrate.d.ts +1 -0
  98. package/build/lib/core/ai/models/index.d.ts +1 -1
  99. package/build/lib/core/ai/providers/concentrate.d.ts +1 -0
  100. package/build/lib/core/ai/utils.d.ts +2 -0
  101. package/build/lib/core/ai/utils.js +4 -2
  102. package/build/lib/core/api/index.d.ts +3 -0
  103. package/build/lib/core/api/index.js +22 -19
  104. package/build/lib/core/api/targetedPentest.d.ts +1 -0
  105. package/build/lib/core/api/targetedPentest.js +10 -10
  106. package/build/lib/core/config/config.d.ts +3 -0
  107. package/build/lib/core/config/index.js +1 -1
  108. package/build/lib/core/findings/registry.js +3 -3
  109. package/build/lib/core/session/index.d.ts +12 -0
  110. package/build/lib/core/session/index.js +2 -2
  111. package/build/lib/core/session/persistence.d.ts +7 -0
  112. package/build/lib/core/session/persistence.js +7 -3
  113. package/build/lib/core/workflows/fastStrike.d.ts +3 -1
  114. package/build/lib/core/workflows/pentest.d.ts +8 -1
  115. package/build/lib/core/workflows/pentest.js +15 -15
  116. package/build/lib/core/workflows/seams.js +4 -4
  117. package/build/lib/core/workflows/whiteboxAttackSurface.js +11 -11
  118. package/build/lib/index.js +22 -22
  119. package/build/{logs-x62b7hk1.js → logs-70fyr848.js} +13 -13
  120. package/build/{native-rollout-evidence-c9yhfqcs.js → native-rollout-evidence-ntensvzb.js} +3 -3
  121. package/build/{offesecAgent-5whnpeta.js → offesecAgent-kk62tf60.js} +4 -4
  122. package/build/pentest-4jgznn7h.js +22 -0
  123. package/build/{pentests-e0f32aqx.js → pentests-3e41hj08.js} +13 -13
  124. package/build/recordedRunClient-w34kcch1.js +11 -0
  125. package/build/{targetedPentest-rcbs4nvp.js → targetedPentest-t9snxcj2.js} +18 -11
  126. package/build/{targets-xj9g3q0z.js → targets-1pyf21c7.js} +13 -13
  127. package/build/threatModel-48y5qjze.js +21 -0
  128. package/build/{uninstall-7s784q4a.js → uninstall-fbydd9t6.js} +1 -1
  129. package/build/{upload-4gvhwmvr.js → upload-sbhwe2fq.js} +4 -4
  130. package/build/{utils-cjrz64pc.js → utils-23903gcj.js} +3 -3
  131. package/package.json +1 -1
  132. package/src/cli.ts +48 -14
  133. package/src/core/agents/agentRuntime.test.ts +38 -0
  134. package/src/core/agents/agentRuntime.ts +5 -2
  135. package/src/core/agents/defineAgent.ts +3 -1
  136. package/src/core/agents/offSecAgent/index.ts +3 -0
  137. package/src/core/agents/offSecAgent/offensiveSecurityAgent.test.ts +92 -5
  138. package/src/core/agents/offSecAgent/offensiveSecurityAgent.ts +70 -31
  139. package/src/core/agents/offSecAgent/prompt.test.ts +46 -0
  140. package/src/core/agents/offSecAgent/prompt.ts +52 -8
  141. package/src/core/agents/offSecAgent/selectiveConstruction.publicPath.test.ts +38 -0
  142. package/src/core/agents/offSecAgent/selectiveConstruction.test.ts +64 -0
  143. package/src/core/agents/offSecAgent/subagentSpawner.ts +12 -1
  144. package/src/core/agents/offSecAgent/tools/delegateAuth.ts +1 -0
  145. package/src/core/agents/offSecAgent/tools/listMemories.test.ts +230 -0
  146. package/src/core/agents/offSecAgent/tools/listMemories.ts +73 -6
  147. package/src/core/agents/offSecAgent/tools/reportError.test.ts +6 -0
  148. package/src/core/agents/offSecAgent/tools/reportError.ts +10 -2
  149. package/src/core/agents/offSecAgent/tools/runAttackSurface.ts +2 -0
  150. package/src/core/agents/offSecAgent/tools/runPentestWorkflow.test.ts +57 -0
  151. package/src/core/agents/offSecAgent/tools/runPentestWorkflow.ts +19 -1
  152. package/src/core/agents/offSecAgent/tools/runWhiteboxScan.ts +5 -2
  153. package/src/core/agents/offSecAgent/tools/spawnCodingAgent.ts +1 -0
  154. package/src/core/agents/offSecAgent/tools/spawnPentestAgent.test.ts +72 -2
  155. package/src/core/agents/offSecAgent/tools/spawnPentestAgent.ts +32 -11
  156. package/src/core/agents/offSecAgent/tools/spawnPentestSwarm.ts +25 -5
  157. package/src/core/agents/offSecAgent/tools/threatModelGenerator.ts +1 -0
  158. package/src/core/agents/offSecAgent/tools/types.ts +6 -1
  159. package/src/core/agents/offSecAgent/tools/updateTask.ts +5 -1
  160. package/src/core/agents/offSecAgent/tools/whiteboxCandidates.ts +11 -4
  161. package/src/core/agents/offSecAgent/types.ts +9 -0
  162. package/src/core/agents/specialized/codeAgent/agent.test.ts +7 -0
  163. package/src/core/agents/specialized/codeAgent/agent.ts +9 -2
  164. package/src/core/agents/specialized/codeAgent/prompts.ts +6 -0
  165. package/src/core/agents/specialized/pentest/agent.test.ts +116 -0
  166. package/src/core/agents/specialized/pentest/agent.ts +178 -49
  167. package/src/core/agents/specialized/pentest/results.test.ts +59 -4
  168. package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.ts +1 -0
  169. package/src/core/ai/ai.operator-context.bench.test.ts +1408 -0
  170. package/src/core/ai/ai.recorded-context.test.ts +74 -0
  171. package/src/core/ai/ai.test.ts +54 -0
  172. package/src/core/ai/ai.ts +312 -82
  173. package/src/core/ai/compactionTelemetry.test.ts +2 -2
  174. package/src/core/ai/models/anthropic.ts +6 -0
  175. package/src/core/ai/models/bedrock.ts +18 -0
  176. package/src/core/ai/models/claude-capabilities.ts +29 -8
  177. package/src/core/ai/models/concentrate.ts +52 -0
  178. package/src/core/ai/models/index.ts +25 -4
  179. package/src/core/ai/models/models.test.ts +154 -0
  180. package/src/core/ai/models/openrouter.ts +43 -1
  181. package/src/core/ai/providers/anthropic-models.test.ts +60 -1
  182. package/src/core/ai/providers/bedrock-models.test.ts +81 -0
  183. package/src/core/ai/providers/concentrate.test.ts +31 -0
  184. package/src/core/ai/providers/concentrate.ts +11 -0
  185. package/src/core/ai/providers/gateway-models.test.ts +16 -6
  186. package/src/core/ai/utils.native-rollout-evidence.test.ts +1 -0
  187. package/src/core/ai/utils.ts +189 -143
  188. package/src/core/api/blackboxPentest.ts +9 -1
  189. package/src/core/api/index.ts +9 -0
  190. package/src/core/api/targetedPentest.ts +19 -5
  191. package/src/core/config/config.test.ts +35 -0
  192. package/src/core/config/config.ts +5 -0
  193. package/src/core/observability/telemetry.test.ts +2 -3
  194. package/src/core/session/index.ts +5 -0
  195. package/src/core/session/persistence.test.ts +62 -0
  196. package/src/core/session/persistence.ts +56 -0
  197. package/src/core/workflows/fastStrike.seams.test.ts +149 -0
  198. package/src/core/workflows/fastStrike.test.ts +18 -1
  199. package/src/core/workflows/fastStrike.ts +103 -11
  200. package/src/core/workflows/pentest.test.ts +104 -0
  201. package/src/core/workflows/pentest.ts +110 -7
  202. package/src/core/workflows/seams.test.ts +13 -0
  203. package/src/core/workflows/seams.ts +5 -1
  204. package/src/core/workflows/whiteboxAttackSurface.ts +1 -0
  205. package/src/tui/components/chat/loading-indicator.tsx +20 -4
  206. package/src/tui/components/chat/message-list.tsx +1 -0
  207. package/src/tui/components/commands/__tests__/advanced-settings-lifecycle.tsx +176 -0
  208. package/src/tui/components/commands/advanced-settings.test.ts +28 -0
  209. package/src/tui/components/commands/advanced-settings.tsx +237 -19
  210. package/src/tui/components/operator-dashboard/index.tsx +46 -9
  211. package/src/tui/components/operator-dashboard/logic.test.ts +106 -0
  212. package/src/tui/components/operator-dashboard/logic.ts +42 -2
  213. package/src/tui/components/operator-dashboard/run-events.test.ts +3 -1
  214. package/src/tui/components/shared/__tests__/tool-renderer-lifecycle.tsx +18 -0
  215. package/src/tui/components/shared/action-label.test.ts +9 -0
  216. package/src/tui/components/shared/compacting-icon.tsx +18 -0
  217. package/src/tui/components/shared/index.ts +1 -0
  218. package/src/tui/components/shared/tool-registry.ts +1 -0
  219. package/src/tui/components/shared/tool-renderer.tsx +21 -7
  220. package/src/tui/context/route.tsx +1 -0
  221. package/src/tui/keybindings/registry.ts +2 -0
  222. package/build/agent-6w7egcf1.js +0 -13
  223. package/build/agent-9zx75feh.js +0 -13
  224. package/build/pentest-e61hc38s.js +0 -22
  225. package/build/recordedRunClient-dp032c32.js +0 -11
  226. package/build/threatModel-jkxfa40s.js +0 -21
@@ -298,6 +298,7 @@ export class OffensiveSecurityAgent<TResult = void> {
298
298
 
299
299
  private readonly resolveResult?: (
300
300
  streamResult: StreamTextResult<ToolSet, never>,
301
+ validatedResponse?: unknown,
301
302
  ) => TResult | Promise<TResult>;
302
303
 
303
304
  /** The structured result captured by the `response` tool, or `null` until it fires. */
@@ -552,6 +553,7 @@ export class OffensiveSecurityAgent<TResult = void> {
552
553
  input.credentialManager ?? input.session.credentialManager;
553
554
 
554
555
  const toolCtx = {
556
+ findingPolicy: input.findingPolicy,
555
557
  ...(input.attackSurfaceArtifactsPath
556
558
  ? { attackSurfaceArtifactsPath: input.attackSurfaceArtifactsPath }
557
559
  : {}),
@@ -658,6 +660,12 @@ export class OffensiveSecurityAgent<TResult = void> {
658
660
  }
659
661
  }
660
662
 
663
+ if (input.findingPolicy === "external") {
664
+ activeTools = activeTools.filter(
665
+ (name) => name !== "document_vulnerability",
666
+ );
667
+ }
668
+
661
669
  activeTools = filterWorkspaceToolsForRun(
662
670
  activeTools,
663
671
  input.prompt,
@@ -666,8 +674,16 @@ export class OffensiveSecurityAgent<TResult = void> {
666
674
 
667
675
  const builtinTools = createToolsForNames(toolCtx, activeTools);
668
676
 
669
- let tools: ToolSet = input.extraTools
670
- ? { ...builtinTools, ...input.extraTools }
677
+ const extraTools =
678
+ input.findingPolicy === "external" && input.extraTools
679
+ ? Object.fromEntries(
680
+ Object.entries(input.extraTools).filter(
681
+ ([name]) => name !== "document_vulnerability",
682
+ ),
683
+ )
684
+ : input.extraTools;
685
+ let tools: ToolSet = extraTools
686
+ ? { ...builtinTools, ...extraTools }
671
687
  : { ...builtinTools };
672
688
 
673
689
  // Only approved calls may reach the execution journal.
@@ -712,7 +728,7 @@ export class OffensiveSecurityAgent<TResult = void> {
712
728
  Promise.race([sr.response, this._drainRejection]),
713
729
  },
714
730
  });
715
- return customResolve(guarded);
731
+ return customResolve(guarded, result);
716
732
  })
717
733
  .then((resolved) => {
718
734
  this._capturedResponse = resolved;
@@ -759,16 +775,36 @@ export class OffensiveSecurityAgent<TResult = void> {
759
775
  }
760
776
  this.messagesPath = join(messagesDir, "messages.json");
761
777
 
762
- // Mutable so that summarization can clear stale history.
763
- const initialMessagesRef: { current: ModelMessage[] } = {
764
- current: input.messages
765
- ? [...input.messages]
766
- : [
767
- {
768
- role: "user" as const,
769
- content: [{ type: "text", text: input.prompt }],
770
- },
771
- ],
778
+ const preparedContextRef = {
779
+ current: {
780
+ messages: input.messages
781
+ ? [...input.messages]
782
+ : [
783
+ {
784
+ role: "user" as const,
785
+ content: [{ type: "text" as const, text: input.prompt }],
786
+ },
787
+ ],
788
+ responseMessageOffset: 0,
789
+ },
790
+ };
791
+ const legacyMessages = (responseMessages: ModelMessage[]) => [
792
+ ...preparedContextRef.current.messages,
793
+ ...responseMessages.slice(
794
+ preparedContextRef.current.responseMessageOffset,
795
+ ),
796
+ ];
797
+ const persistedMessages = (messages: ModelMessage[]) => {
798
+ // Cache-marked system messages are transport context; the next turn
799
+ // supplies a fresh system prompt and must not replay saved copies.
800
+ let first = 0;
801
+ while (
802
+ messages[first]?.role === "system" &&
803
+ messages[first]?.providerOptions
804
+ ) {
805
+ first++;
806
+ }
807
+ return first === 0 ? messages : messages.slice(first);
772
808
  };
773
809
 
774
810
  // Agent-local persistence: debounce, latest snapshot, and the
@@ -796,6 +832,7 @@ export class OffensiveSecurityAgent<TResult = void> {
796
832
  input.system ??
797
833
  buildBaseSystemPrompt({
798
834
  sandboxMode: agentCwd === input.session.rootPath,
835
+ findingPolicy: input.findingPolicy,
799
836
  });
800
837
  const commandPlatform = resolveCommandPlatform(toolCtx);
801
838
  const workspaceSection = buildSessionWorkspaceSection(
@@ -803,6 +840,7 @@ export class OffensiveSecurityAgent<TResult = void> {
803
840
  agentCwd,
804
841
  activeTools,
805
842
  fileWorkspaceRoot,
843
+ input.findingPolicy,
806
844
  );
807
845
  // Bundled wordlist paths are host-local: advertise them only when
808
846
  // commands execute locally.
@@ -861,6 +899,12 @@ export class OffensiveSecurityAgent<TResult = void> {
861
899
  usageRecorder: input.usageRecorder,
862
900
  contextRecorder: input.contextRecorder,
863
901
  inferenceRecorder: input.inferenceRecorder,
902
+ onContextPrepared: (context) => {
903
+ preparedContextRef.current = {
904
+ messages: [...context.messages],
905
+ responseMessageOffset: context.responseMessageOffset,
906
+ };
907
+ },
864
908
  // Per-subagent so the overflow tool-result dumps land next to this
865
909
  // agent's messages.json (`subagents/{id}/tool-results/`) and a host
866
910
  // can reclaim them when the subagent finishes, instead of piling up
@@ -880,16 +924,10 @@ export class OffensiveSecurityAgent<TResult = void> {
880
924
  return;
881
925
  }
882
926
 
883
- // Recorded runs project the canonical committed context; the
884
- // legacy path reconstructs from the (possibly stale after a fit)
885
- // initial prefix. Canonical latest() already includes this step's
886
- // messages — committing happens upstream in the ai layer's
887
- // onStepFinish wrapper, before this user callback runs.
888
- const canonical = input.contextRecorder?.latest() ?? [
889
- ...initialMessagesRef.current,
890
- ...event.response.messages,
891
- ];
892
- this.writer.setLatest(canonical);
927
+ const canonical =
928
+ input.contextRecorder?.latest() ??
929
+ legacyMessages(event.response.messages);
930
+ this.writer.setLatest(persistedMessages(canonical));
893
931
  schedulePersist();
894
932
  traceWriter.recordStep(
895
933
  event.response.messages as ModelMessage[],
@@ -905,9 +943,10 @@ export class OffensiveSecurityAgent<TResult = void> {
905
943
  await input.onStepFinish?.(event);
906
944
  },
907
945
  onSummarized: () => {
908
- // Context was reset by summarization — discard the old history so
909
- // subsequent onStepFinish writes only persist post-summary messages.
910
- initialMessagesRef.current = [];
946
+ preparedContextRef.current = {
947
+ messages: [],
948
+ responseMessageOffset: 0,
949
+ };
911
950
  traceWriter.markSummarized();
912
951
  },
913
952
  onFinish: async (event) => {
@@ -915,11 +954,11 @@ export class OffensiveSecurityAgent<TResult = void> {
915
954
  this.writer.cancelTimer();
916
955
  // Skip if the interrupted-step finalizer already wrote the abort snapshot.
917
956
  if (!this.writer.syntheticsPersisted) {
918
- const finalMessages = input.contextRecorder?.latest() ??
919
- this.writer.latest ?? [
920
- ...initialMessagesRef.current,
921
- ...event.response.messages,
922
- ];
957
+ const finalMessages = persistedMessages(
958
+ input.contextRecorder?.latest() ??
959
+ this.writer.latest ??
960
+ legacyMessages(event.response.messages),
961
+ );
923
962
  this.writer.setLatest(finalMessages);
924
963
  await this.writer.enqueueWrite(finalMessages).catch(() => {});
925
964
  }
@@ -70,3 +70,49 @@ describe("buildBaseSystemPrompt workspace management", () => {
70
70
  );
71
71
  });
72
72
  });
73
+
74
+ describe("external finding policy prompts", () => {
75
+ const session = {
76
+ rootPath: "/tmp/apex-external-prompt",
77
+ findingsPath: "/tmp/apex-external-prompt/findings",
78
+ pocsPath: "/tmp/apex-external-prompt/pocs",
79
+ scratchpadPath: "/tmp/apex-external-prompt/scratchpad",
80
+ logsPath: "/tmp/apex-external-prompt/logs",
81
+ };
82
+
83
+ it("keeps exploration guidance without directing an unavailable finding tool", () => {
84
+ const prompt = buildBaseSystemPrompt({ findingPolicy: "external" });
85
+ expect(prompt).toContain("Source code analysis");
86
+ expect(prompt).toContain("caller grades them after the run");
87
+ expect(prompt).not.toContain("document_vulnerability");
88
+ expect(prompt).not.toContain("published PoCs");
89
+ });
90
+
91
+ it.each([
92
+ [session.rootPath, undefined],
93
+ ["/project", undefined],
94
+ ["/project", "/tmp/apex-external-prompt/helpers"],
95
+ ])("uses the caller's finding contract for cwd %s", (cwd, fileWorkspaceRoot) => {
96
+ const prompt = buildSessionWorkspaceSection(
97
+ session,
98
+ cwd,
99
+ ["execute_command", "read_file"],
100
+ fileWorkspaceRoot,
101
+ "external",
102
+ );
103
+ expect(prompt).toContain("caller's output contract");
104
+ expect(prompt).not.toContain("document_vulnerability");
105
+ });
106
+
107
+ it("keeps source guidance policy-safe even when tool names are not supplied", () => {
108
+ const prompt = buildSessionWorkspaceSection(
109
+ { ...session, config: { codebasePath: "/project" } },
110
+ "/project",
111
+ undefined,
112
+ undefined,
113
+ "external",
114
+ );
115
+ expect(prompt).toContain("Source Code Assessment");
116
+ expect(prompt).not.toContain("document_vulnerability");
117
+ });
118
+ });
@@ -1,5 +1,6 @@
1
1
  import { existsSync, readdirSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import type { FindingPolicy } from "./types";
3
4
 
4
5
  interface SessionPaths {
5
6
  rootPath: string;
@@ -77,6 +78,7 @@ export function buildSessionWorkspaceSection(
77
78
  agentCwd: string,
78
79
  activeTools?: readonly string[],
79
80
  fileWorkspaceRoot?: string,
81
+ findingPolicy: FindingPolicy = "native",
80
82
  ): string {
81
83
  const sandboxMode = agentCwd === session.rootPath;
82
84
  const providedFilesSection = buildProvidedFilesSection(
@@ -87,9 +89,14 @@ export function buildSessionWorkspaceSection(
87
89
  session,
88
90
  agentCwd,
89
91
  { sandboxMode, activeTools, fileWorkspaceRoot },
92
+ findingPolicy,
90
93
  );
91
94
 
92
95
  if (fileWorkspaceRoot) {
96
+ const artifactsGuidance =
97
+ findingPolicy === "external"
98
+ ? `Session artifacts live at ${session.rootPath}. Follow the caller's output contract for findings; browser tools save their own evidence.`
99
+ : `Session artifacts live at ${session.rootPath}. Findings and published PoCs are written by \`document_vulnerability\`, and browser tools save their own evidence.`;
93
100
  return `
94
101
 
95
102
  # Working Directory and File Workspace
@@ -98,10 +105,17 @@ Your shell starts in ${agentCwd}. Native file tools are confined to ${fileWorksp
98
105
 
99
106
  Create and edit helper scripts inside the file workspace. Use absolute helper paths with \`execute_command\` to check and run them. Do not prefix native file-tool paths with \`scratchpad/\` or \`provided_files/\` to reach session directories outside the file workspace.
100
107
 
101
- Session artifacts live at ${session.rootPath}. Findings and published PoCs are written by \`document_vulnerability\`, and browser tools save their own evidence. Session logs and provided files outside the file workspace must be inspected through command tools or copied into the file workspace before native file tools can read them.${providedFilesSection}${sourceAssessmentSection}`;
108
+ ${artifactsGuidance} Session logs and provided files outside the file workspace must be inspected through command tools or copied into the file workspace before native file tools can read them.${providedFilesSection}${sourceAssessmentSection}`;
102
109
  }
103
110
 
104
111
  if (sandboxMode) {
112
+ if (findingPolicy === "external") {
113
+ return `
114
+
115
+ # Session Workspace
116
+
117
+ Your shell is already set to the session directory. Use relative paths for session files. The session provides scratchpad/ for temporary work, logs/ for execution logs, evidence/ for browser evidence, and provided_files/ for supplied inputs. Follow the caller's output contract for findings.${providedFilesSection}${sourceAssessmentSection}`;
118
+ }
105
119
  return `
106
120
 
107
121
  # Session Workspace
@@ -119,6 +133,15 @@ The session directory (${session.rootPath}) contains these subdirectories:
119
133
  Tools like \`document_vulnerability\` and browser evidence capture write to the correct subdirectories automatically.${providedFilesSection}${sourceAssessmentSection}`;
120
134
  }
121
135
 
136
+ if (findingPolicy === "external") {
137
+ return `
138
+
139
+ # Working Directory
140
+
141
+ Your shell starts in the user's project directory: ${agentCwd}
142
+ Use relative paths to reference project files. Session artifacts are stored at ${session.rootPath}; use scratchpad/ for temporary work, logs/ for execution logs, and evidence/ for browser evidence. Follow the caller's output contract for findings.${providedFilesSection}${sourceAssessmentSection}`;
143
+ }
144
+
122
145
  return `
123
146
 
124
147
  # Working Directory
@@ -168,6 +191,7 @@ function buildSourceAssessmentSection(
168
191
  activeTools?: readonly string[];
169
192
  fileWorkspaceRoot?: string;
170
193
  },
194
+ findingPolicy: FindingPolicy,
171
195
  ): string {
172
196
  const codebasePath = session.config?.codebasePath;
173
197
  // A helper workspace does not make the shell's cwd an authorized target
@@ -214,7 +238,8 @@ function buildSourceAssessmentSection(
214
238
  "- Use `spawn_coding_agent` for independent deep dives.",
215
239
  hasTool("create_whitebox_candidate") &&
216
240
  "- Track unverified hypotheses with `create_whitebox_candidate`.",
217
- hasTool("document_vulnerability") &&
241
+ findingPolicy !== "external" &&
242
+ hasTool("document_vulnerability") &&
218
243
  "- Only call `document_vulnerability` after a PoC, crash reproducer, or dynamic check confirms exploitability.",
219
244
  hasTool("start_whitebox_job") &&
220
245
  hasTool("poll_whitebox_job") &&
@@ -238,12 +263,32 @@ ${toolGuidance}${repoEditBullet}`;
238
263
  export interface BaseSystemPromptOptions {
239
264
  /** When true, include the "Stay in the session folder" instruction. Defaults to true. */
240
265
  sandboxMode?: boolean;
266
+ findingPolicy?: FindingPolicy;
241
267
  }
242
268
 
243
269
  export function buildBaseSystemPrompt(
244
270
  options?: BaseSystemPromptOptions,
245
271
  ): string {
246
272
  const sandboxMode = options?.sandboxMode ?? true;
273
+ const externalFindings = options?.findingPolicy === "external";
274
+
275
+ const exploitationCapability = externalFindings
276
+ ? "- **Exploitation and proof development** — use the available tools only as far as the assigned objective requires"
277
+ : "- **Exploitation & proof-of-concept development** — provide PoC scripts inline to document_vulnerability which executes and validates them";
278
+ const reportingCapability = externalFindings
279
+ ? "- **Reporting** — submit concise findings through the caller's requested output contract; the caller grades them after the run"
280
+ : "- **Reporting** — document confirmed findings with evidence, impact, and remediation";
281
+ const findingToolReference = externalFindings
282
+ ? `## Findings
283
+ - Use the caller's requested output format for any discovered vulnerabilities. Include the affected surface, observed behavior, and likely security impact.`
284
+ : `## Findings & PoCs
285
+ - **document_vulnerability** — Create, execute, and validate a proof-of-concept script, then document the confirmed vulnerability with CVSS 4.0 scoring. Provide your POC script inline via pocContent. The tool handles execution, validation, scoring, and persistence.`;
286
+ const findingWorkRule = externalFindings
287
+ ? "6. **Report findings through the requested output.** Focus on discovering vulnerabilities and gathering the evidence required by the user's task. Do not spend time on a separate in-run finding review."
288
+ : "6. **Document security work as you go.** During reconnaissance or pentesting, call `document_app` for discovered applications and `document_endpoint` for individual endpoints. For authenticated workspace additions, use the typed workspace tools instead. Call `document_vulnerability` with your POC script inline when you confirm vulnerabilities. Don't defer documentation to the end.";
289
+ const reportRule = externalFindings
290
+ ? "5. **Follow the caller's output contract.** Keep intermediate files in scratchpad/ and submit final finding artifacts only where the caller requested."
291
+ : "5. **No reports in scratchpad.** Do NOT write synthesized report documents to scratchpad/ (e.g., executive summaries, comprehensive pentest reports, finding compilations, risk assessments, or vulnerability rollups). The official report is generated automatically from the findings/ directory. Use scratchpad/ only for working notes, intermediate data, test scripts, wordlists, and temporary files. Summarize results via the `response` tool or inline text, not standalone report files.";
247
292
 
248
293
  const stayInSessionParagraph = sandboxMode
249
294
  ? `\n\n**Stay in the session folder.** Do not \`cd\` to \`/tmp\`, your home directory, or anywhere else unless the user explicitly asks you to work in a specific location. Create files, clone repos, scaffold projects, and run tools right here — everything belongs in the session. Use relative paths (e.g. \`scratchpad/notes.txt\`, \`test-app/\`) rather than absolute paths to external directories.`
@@ -258,9 +303,9 @@ You work interactively with the user. Execute the task they ask for, report your
258
303
  You can perform the full lifecycle of a penetration test and support a wide range of cybersecurity tasks:
259
304
  - **Reconnaissance & attack surface mapping** — subdomain enumeration, endpoint discovery, JS analysis, authenticated crawling
260
305
  - **Vulnerability testing** — injection flaws, auth bypasses, access control issues, misconfigurations, business logic flaws
261
- - **Exploitation & proof-of-concept development** — provide PoC scripts inline to document_vulnerability which executes and validates them
306
+ ${exploitationCapability}
262
307
  - **Source code analysis** — read and search codebases for security-relevant patterns, misconfigurations, and vulnerabilities
263
- - **Reporting** — document confirmed findings with evidence, impact, and remediation
308
+ ${reportingCapability}
264
309
 
265
310
  # Tool Reference
266
311
 
@@ -311,8 +356,7 @@ You can perform the full lifecycle of a penetration test and support a wide rang
311
356
 
312
357
  These tools use the authenticated API client in the running Apex process. Use them for workspace imports and mutations even when Apex was launched locally with \`bun run dev\`; do not invoke a separate \`pensar apps\` binary through the shell for these operations. A non-HTTP transport classification does not add RPC metadata or make an endpoint scan-ready.
313
358
 
314
- ## Findings & PoCs
315
- - **document_vulnerability** — Create, execute, and validate a proof-of-concept script, then document the confirmed vulnerability with CVSS 4.0 scoring. Provide your POC script inline via pocContent. The tool handles execution, validation, scoring, and persistence.
359
+ ${findingToolReference}
316
360
 
317
361
  ## Orchestration
318
362
  - **run_attack_surface** — Launch a full attack surface discovery workflow. Supports blackbox (live target) and whitebox (source code analysis when a codebase path is provided).
@@ -340,7 +384,7 @@ Run \`pensar --help\` or \`pensar <command> --help\` for exact flags — the CLI
340
384
  3. **Be thorough within the ask.** When given a task, see it through completely. Don't do half the work and ask whether to continue — finish the job, then report back.
341
385
  4. **Use the right level of automation.** For large tasks (e.g., "pentest this entire application"), use orchestration tools like \`run_attack_surface\` and \`spawn_pentest_swarm\` to parallelize the work. For focused tasks (e.g., "check if this parameter is vulnerable to XSS"), work directly with \`http_request\`, \`execute_command\`, or the browser tools.
342
386
  5. **Authenticate when needed.** If the user provides credentials or auth instructions, use them. Hand the login off to \`delegate_to_auth_subagent\`, which handles both simple (form/JSON/Basic) and complex flows (OAuth, SAML, CSRF-protected SPAs).
343
- 6. **Document security work as you go.** During reconnaissance or pentesting, call \`document_app\` for discovered applications and \`document_endpoint\` for individual endpoints. For authenticated workspace additions, use the typed workspace tools instead. Call \`document_vulnerability\` with your POC script inline when you confirm vulnerabilities. Don't defer documentation to the end.
387
+ ${findingWorkRule}
344
388
  7. **Consult memories first.** When you begin testing a specific application, framework, or path, call \`list_memories\` to check for saved knowledge from previous sessions — past findings, useful payloads, endpoint patterns, or target-specific notes. Use relevant memories to inform your approach before starting from scratch.
345
389
 
346
390
  # Command Execution
@@ -358,7 +402,7 @@ For long-running services (servers, listeners, watchers), background them with *
358
402
  2. **Stay in scope.** Only test targets and systems explicitly provided by the user or discovered within the authorized scope. Respect any scope constraints in the session config.
359
403
  3. **Handle failures gracefully.** If a tool call fails or a technique doesn't work, try alternative approaches. If your PoC approach fails repeatedly, pivot to a different technique.
360
404
  4. **Summarize results.** After completing a task, give the user a clear summary of what you found, what you tried, and what the next steps could be.
361
- 5. **No reports in scratchpad.** Do NOT write synthesized report documents to scratchpad/ (e.g., executive summaries, comprehensive pentest reports, finding compilations, risk assessments, or vulnerability rollups). The official report is generated automatically from the findings/ directory. Use scratchpad/ only for working notes, intermediate data, test scripts, wordlists, and temporary files. Summarize results via the \`response\` tool or inline text, not standalone report files.`;
405
+ ${reportRule}`;
362
406
  }
363
407
 
364
408
  /**
@@ -201,6 +201,20 @@ describe("OffensiveSecurityAgent public-path construction counts", () => {
201
201
  });
202
202
  });
203
203
 
204
+ it("external Fast Strike never constructs the native finding tool", async () => {
205
+ await withRoot(async (root) => {
206
+ reset();
207
+ observed.armed = true;
208
+ makeAgent(root, [], { mode: "fast-strike", findingPolicy: "external" });
209
+ observed.armed = false;
210
+ expect(observed.constructed).toBe(42);
211
+ expect(stream().tools).not.toHaveProperty("document_vulnerability");
212
+ expect(stream().tools).toHaveProperty("execute_command");
213
+ expect(stream().tools).toHaveProperty("http_request");
214
+ expect(stream().tools).toHaveProperty("read_file");
215
+ });
216
+ });
217
+
204
218
  it("selected seven plus five available conditional tools construct twelve", async () => {
205
219
  await withRoot(async (root) => {
206
220
  reset();
@@ -237,6 +251,30 @@ describe("OffensiveSecurityAgent public-path construction counts", () => {
237
251
  });
238
252
 
239
253
  describe("response injection on the real path", () => {
254
+ it("passes only the executed response tool's validated result to the resolver", async () => {
255
+ await withRoot(async (root) => {
256
+ reset();
257
+ const resolved = vi.fn(
258
+ (_streamResult: unknown, result: unknown) => result,
259
+ );
260
+ const agent = makeAgent(root, ["response"], {
261
+ responseSchema: z.object({ reportedFindings: z.array(z.string()) }),
262
+ resolveResult: resolved,
263
+ });
264
+ const response = stream().tools.response as {
265
+ execute: (input: unknown, options: unknown) => Promise<unknown>;
266
+ };
267
+ const finding = { reportedFindings: ["Observed cross-tenant access"] };
268
+ await response.execute(
269
+ { result: finding },
270
+ { toolCallId: "tc_response", messages: [] },
271
+ );
272
+
273
+ await expect(agent.responseCaptured).resolves.toEqual(finding);
274
+ expect(resolved).toHaveBeenCalledWith(expect.anything(), finding);
275
+ });
276
+ });
277
+
240
278
  it("fast-strike + responseSchema + non-enumerable own response extra still activates the injected response", async () => {
241
279
  await withRoot(async (root) => {
242
280
  reset();
@@ -289,6 +289,70 @@ describe("agent-level selective construction", () => {
289
289
  expect(Object.keys(streamTools())).toContain("custom_extra");
290
290
  });
291
291
 
292
+ it("external grading removes the finding tool before default-mode construction and extra-tool merge", () => {
293
+ const m = marker();
294
+ makeAgent(
295
+ baseInput({
296
+ findingPolicy: "external",
297
+ activeTools: [
298
+ "document_vulnerability",
299
+ "execute_command",
300
+ "http_request",
301
+ "spawn_pentest_agent",
302
+ ],
303
+ extraTools: {
304
+ document_vulnerability: {
305
+ description: "caller override",
306
+ inputSchema: {},
307
+ execute: async () => "should not run",
308
+ },
309
+ },
310
+ }) as never,
311
+ );
312
+
313
+ expect(constructedSince(m)).toEqual([
314
+ "execute_command",
315
+ "http_request",
316
+ "spawn_pentest_agent",
317
+ ]);
318
+ expect(streamTools()).not.toHaveProperty("document_vulnerability");
319
+ expect(streamTools()).toHaveProperty("spawn_pentest_agent");
320
+ expect(state.toolContexts.at(-1)).toMatchObject({
321
+ findingPolicy: "external",
322
+ });
323
+ });
324
+
325
+ it("external Fast Strike retains discovery tools while excluding the finding tool", () => {
326
+ const m = marker();
327
+ makeAgent(
328
+ baseInput({
329
+ mode: "fast-strike",
330
+ findingPolicy: "external",
331
+ activeTools: [],
332
+ responseSchema: { parse: () => {} },
333
+ extraTools: {
334
+ document_vulnerability: {
335
+ description: "caller override",
336
+ inputSchema: {},
337
+ execute: async () => "should not run",
338
+ },
339
+ },
340
+ }) as never,
341
+ );
342
+
343
+ expect(constructedSince(m)).not.toContain("document_vulnerability");
344
+ expect(streamTools()).not.toHaveProperty("document_vulnerability");
345
+ for (const name of [
346
+ "browser_navigate",
347
+ "http_request",
348
+ "execute_command",
349
+ "read_file",
350
+ "response",
351
+ ]) {
352
+ expect(streamTools()).toHaveProperty(name);
353
+ }
354
+ });
355
+
292
356
  it("fast-strike keeps a builtin extra override at its builtin position in the provider tool map", () => {
293
357
  // Baseline spread semantics: { ...builtinTools, ...extraTools } replaces
294
358
  // the value but KEEPS the builtin insertion position. A name-only merge
@@ -38,7 +38,12 @@ import type {
38
38
  TargetedPentestAgent,
39
39
  } from "../specialized/pentest/agent";
40
40
  import type { PlaywrightMcpSession, UnifiedSandbox } from "./tools";
41
- import type { AgentHooks, StreamIdFactory, SystemPentestScope } from "./types";
41
+ import type {
42
+ AgentHooks,
43
+ FindingPolicy,
44
+ StreamIdFactory,
45
+ SystemPentestScope,
46
+ } from "./types";
42
47
 
43
48
  /** Registry key selecting which child agent {@link SubagentSpawner.spawn} builds. */
44
49
  export type SubagentType =
@@ -101,6 +106,7 @@ export type SubagentSpec =
101
106
  * @public Consumed by Console's durable subagent runtime.
102
107
  */
103
108
  export interface SpawnRuntime {
109
+ findingPolicy?: FindingPolicy;
104
110
  session: SessionInfo;
105
111
  model: AIModel;
106
112
  authConfig?: AIAuthConfig;
@@ -354,6 +360,7 @@ const runPentestChild: SubagentRunner<"pentest"> = async (spec, ctx) => {
354
360
  openAIReasoningEffort: ctx.openAIReasoningEffort,
355
361
  display: ctx.display,
356
362
  hooks: {
363
+ findingPolicy: ctx.findingPolicy,
357
364
  backends: ctx.backends,
358
365
  sandbox: ctx.sandbox,
359
366
  languageModelMiddleware: ctx.languageModelMiddleware,
@@ -374,6 +381,7 @@ const runPentestChild: SubagentRunner<"pentest"> = async (spec, ctx) => {
374
381
  const runCodeChild: SubagentRunner<"code"> = async (spec, ctx) => {
375
382
  const { CodeAgent } = await import("../specialized/codeAgent/agent");
376
383
  const agent = new CodeAgent({
384
+ findingPolicy: ctx.findingPolicy,
377
385
  codebasePath: spec.codebasePath,
378
386
  objective: spec.objective,
379
387
  system: spec.system,
@@ -412,6 +420,7 @@ const runWhiteboxChild: SubagentRunner<"whitebox-attack-surface"> = async (
412
420
  "../specialized/whiteboxAttackSurface"
413
421
  );
414
422
  const agent = new WhiteboxAttackSurfaceAgent({
423
+ findingPolicy: ctx.findingPolicy,
415
424
  codebasePath: spec.codebasePath,
416
425
  attackSurfaceRegistry: spec.attackSurfaceRegistry,
417
426
  model: ctx.model,
@@ -438,6 +447,7 @@ const runBlackboxChild: SubagentRunner<"blackbox-attack-surface"> = async (
438
447
  "../specialized/attackSurface/blackboxAgent"
439
448
  );
440
449
  const agent = new BlackboxAttackSurfaceAgent({
450
+ findingPolicy: ctx.findingPolicy,
441
451
  target: spec.target,
442
452
  attackSurfaceRegistry: spec.attackSurfaceRegistry,
443
453
  model: ctx.model,
@@ -462,6 +472,7 @@ const runAuthChild: SubagentRunner<"authentication"> = async (spec, ctx) => {
462
472
  return {
463
473
  run: () =>
464
474
  runAuthenticationAgent({
475
+ findingPolicy: ctx.findingPolicy,
465
476
  target: spec.target,
466
477
  authHints: spec.authHints,
467
478
  session: ctx.session,
@@ -298,6 +298,7 @@ IMPORTANT: Pass protectedEndpoints in authHints when you've discovered 401/403 e
298
298
  const result = await spawner.spawn<AuthenticationResult>({
299
299
  spec: { type: "authentication", target, authHints },
300
300
  runtime: {
301
+ findingPolicy: ctx.findingPolicy,
301
302
  session: ctx.session,
302
303
  model: ctx.model,
303
304
  authConfig: ctx.authConfig,