@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.
- package/build/{agent-ayreaj6z.js → agent-3g1b578v.js} +6 -5
- package/build/{agent-yby11zaq.js → agent-p8cezgbb.js} +7 -6
- package/build/{agent-runs-g37vkrjr.js → agent-runs-f86emmgg.js} +23 -20
- package/build/agent-vaq31je6.js +14 -0
- package/build/agent-vvdqga09.js +14 -0
- package/build/{apps-xn6km9yn.js → apps-3stwnmen.js} +16 -13
- package/build/{auth-d105wc64.js → auth-7md9kwda.js} +16 -13
- package/build/blackboxAgent-ct8bejef.js +14 -0
- package/build/{blackboxPentest-ps78v3w7.js → blackboxPentest-w2nr4md3.js} +21 -15
- package/build/{cli-c05y640n.js → cli-1j28cbhy.js} +1 -1
- package/build/{cli-d37a42dc.js → cli-2jz2jz06.js} +206 -90
- package/build/{cli-az3xq70v.js → cli-4a8dsz2k.js} +4 -4
- package/build/{cli-qgz1q9g5.js → cli-58781vcf.js} +1192 -1473
- package/build/{cli-gx71jcke.js → cli-7067zzeb.js} +1 -1
- package/build/{cli-3sp6nd8d.js → cli-81smmd5w.js} +5 -5
- package/build/{cli-k2nwxta0.js → cli-8b25dhky.js} +2 -2
- package/build/{cli-fy6vh2a7.js → cli-8sw5zpqz.js} +1 -1
- package/build/{cli-709rvekb.js → cli-cg3ypx5n.js} +14 -7
- package/build/{cli-dpsw2fe6.js → cli-e9v6zg6h.js} +40941 -40172
- package/build/cli-fjxmphg1.js +36 -0
- package/build/{cli-7eea4e2y.js → cli-gzb6qrzh.js} +31 -60
- package/build/{cli-65g6rcat.js → cli-khnf5xrf.js} +2 -1
- package/build/{cli-1ve18npr.js → cli-mdzt2e8j.js} +4 -2
- package/build/{cli-n2en7j5b.js → cli-n8mmw4c4.js} +1 -1
- package/build/cli-pndb7mh9.js +1193 -0
- package/build/cli-px6ehzqx.js +54 -0
- package/build/{cli-xtc2t6me.js → cli-qs4msn6x.js} +111 -47
- package/build/{cli-kb21qhvm.js → cli-rvybdhgn.js} +4 -3
- package/build/{cli-ae8jqxqr.js → cli-v739h7hb.js} +2 -2
- package/build/{cli-ngq9dr8n.js → cli-wm8ech0x.js} +3 -3
- package/build/cli.js +170 -51
- package/build/{config-zpqkd5cz.js → config-e5v1bkh1.js} +1 -1
- package/build/{fastStrike-mcjdvwez.js → fastStrike-efp0rjs3.js} +74 -17
- package/build/{fixes-kytx5dmj.js → fixes-926cw1ej.js} +16 -13
- package/build/{index-n65z6v7b.js → index-3t41p258.js} +7 -6
- package/build/{index-qv4cc65p.js → index-bv09af6g.js} +19 -15
- package/build/{index-je6hcsqr.js → index-bv6dtwse.js} +1 -1
- package/build/{index-4vqdgb42.js → index-jt52ffgn.js} +1657 -1400
- package/build/{index-ns4m4s01.js → index-np405b9e.js} +11 -7
- package/build/{index-r9yjkfrv.js → index-qys178kp.js} +5 -4
- package/build/{index-wqetp6gq.js → index-tpyzq2wq.js} +42 -11
- package/build/{index-fv64eb32.js → index-vrcdyebh.js} +2 -2
- package/build/{index-dr3tg1w3.js → index-z1g8vbtm.js} +1 -1
- package/build/{issues-eb3q9d3a.js → issues-sncyqh14.js} +16 -13
- package/build/{knowledge-fqsvtx0f.js → knowledge-esgvy5ws.js} +16 -13
- package/build/lance-sacfgs1z.js +232 -0
- package/build/lib/{chunk-9yksqgzr.js → chunk-4aevegnc.js} +77 -45
- package/build/lib/{chunk-9tt2021m.js → chunk-4kf4a15q.js} +13 -13
- package/build/lib/{chunk-fy3bbbqy.js → chunk-5avnedwm.js} +7 -1
- package/build/lib/{chunk-bb0ekhkw.js → chunk-5nz5tk3p.js} +76 -22
- package/build/lib/{chunk-4hwzqthr.js → chunk-7s8e5wrd.js} +3 -2
- package/build/lib/{chunk-yrnrbjes.js → chunk-85kpxwzs.js} +4 -2
- package/build/lib/{chunk-mg03jdmt.js → chunk-9jkzk9nc.js} +1 -1
- package/build/lib/{chunk-r4j8ybn9.js → chunk-9wwts6r6.js} +824 -77
- package/build/lib/{chunk-nhfax4gb.js → chunk-bxznzxdy.js} +5 -1
- package/build/lib/{chunk-r2dkp4tb.js → chunk-cqxm95md.js} +1 -1
- package/build/lib/{chunk-w66ce82a.js → chunk-e3agqspc.js} +12 -4
- package/build/lib/{chunk-ba41jh41.js → chunk-eh5yw385.js} +1137 -333
- package/build/lib/{chunk-1azdancq.js → chunk-ewc9r84m.js} +5 -1
- package/build/lib/{chunk-2njv7wz3.js → chunk-hbyxyxdc.js} +4 -0
- package/build/lib/{chunk-e8yy2k73.js → chunk-jhhwts6w.js} +28 -2
- package/build/lib/{chunk-1jvwqnem.js → chunk-kbxbd1kn.js} +108 -44
- package/build/lib/{chunk-7pwa92rn.js → chunk-mph5fvk7.js} +1 -1
- package/build/lib/chunk-psc23epq.js +107 -0
- package/build/lib/{chunk-acgbyf5h.js → chunk-qxad7qjd.js} +4 -2
- package/build/lib/{chunk-3c00n0cy.js → chunk-r0v3vvgm.js} +54 -33
- package/build/lib/{chunk-1yct6cra.js → chunk-rt13120a.js} +1 -1
- package/build/lib/{chunk-v3dk9j7k.js → chunk-sgya016v.js} +93 -22
- package/build/lib/{chunk-1fy8azee.js → chunk-txef92hq.js} +1 -1
- package/build/lib/{chunk-2qsq76xq.js → chunk-vcd90qrn.js} +189 -90
- package/build/lib/{chunk-7gjdygcx.js → chunk-vwr9w3ab.js} +2 -0
- package/build/lib/{chunk-z137x08b.js → chunk-wf6ak0m3.js} +92 -70
- package/build/lib/{chunk-xhdpgpkm.js → chunk-xft0ms9j.js} +2 -2
- package/build/lib/core/agents/agentRuntime.d.ts +3 -1
- package/build/lib/core/agents/defineAgent.d.ts +3 -2
- package/build/lib/core/agents/lance.d.ts +153 -0
- package/build/lib/core/agents/lanceJudge.d.ts +3 -0
- package/build/lib/core/agents/offSecAgent/index.d.ts +3 -1
- package/build/lib/core/agents/offSecAgent/index.js +16 -14
- package/build/lib/core/agents/offSecAgent/offensiveSecurityAgent.d.ts +4 -0
- package/build/lib/core/agents/offSecAgent/prompt.d.ts +3 -1
- package/build/lib/core/agents/offSecAgent/subagentSpawner.d.ts +18 -2
- package/build/lib/core/agents/offSecAgent/subagentSpawner.js +10 -8
- package/build/lib/core/agents/offSecAgent/toolExecutionContext.d.ts +3 -0
- package/build/lib/core/agents/offSecAgent/toolLifecycle.d.ts +1 -0
- package/build/lib/core/agents/offSecAgent/tools/index.js +10 -6
- package/build/lib/core/agents/offSecAgent/tools/listMemories.d.ts +11 -1
- package/build/lib/core/agents/offSecAgent/tools/reportError.d.ts +2 -1
- package/build/lib/core/agents/offSecAgent/tools/runPentestWorkflow.d.ts +20 -1
- package/build/lib/core/agents/offSecAgent/tools/spawnPentestAgent.d.ts +4 -7
- package/build/lib/core/agents/offSecAgent/tools/spawnPentestSwarm.d.ts +7 -1
- package/build/lib/core/agents/offSecAgent/tools/types.d.ts +6 -3
- package/build/lib/core/agents/offSecAgent/tools/whiteboxCandidates.d.ts +2 -2
- package/build/lib/core/agents/offSecAgent/types.d.ts +19 -3
- package/build/lib/core/agents/specialized/attackSurface/blackboxAgent.js +13 -13
- package/build/lib/core/agents/specialized/attackSurface/schemas.js +12 -12
- package/build/lib/core/agents/specialized/authenticationAgent/index.js +14 -14
- package/build/lib/core/agents/specialized/codeAgent/agent.js +13 -13
- package/build/lib/core/agents/specialized/codeAgent/prompts.d.ts +1 -0
- package/build/lib/core/agents/specialized/cvssScorer/index.d.ts +2 -1
- package/build/lib/core/agents/specialized/findingJudge/agent.js +11 -11
- package/build/lib/core/agents/specialized/findingJudge/index.d.ts +2 -1
- package/build/lib/core/agents/specialized/findingJudge/index.js +4 -4
- package/build/lib/core/agents/specialized/patching/index.js +14 -14
- package/build/lib/core/agents/specialized/pentest/agent.d.ts +18 -4
- package/build/lib/core/agents/specialized/pentest/agent.js +15 -13
- package/build/lib/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.d.ts +2 -1
- package/build/lib/core/agents/specialized/whiteboxAttackSurface/index.js +13 -13
- package/build/lib/core/ai/ai.d.ts +29 -70
- package/build/lib/core/ai/ai.js +5 -3
- package/build/lib/core/ai/index.d.ts +1 -0
- package/build/lib/core/ai/index.js +16 -4
- package/build/lib/core/ai/models/claude-capabilities.d.ts +3 -2
- package/build/lib/core/ai/models/concentrate.d.ts +1 -0
- package/build/lib/core/ai/models/index.d.ts +1 -1
- package/build/lib/core/ai/providers/concentrate.d.ts +1 -0
- package/build/lib/core/ai/reasoning.d.ts +44 -0
- package/build/lib/core/ai/utils.d.ts +2 -0
- package/build/lib/core/ai/utils.js +5 -3
- package/build/lib/core/api/index.d.ts +6 -0
- package/build/lib/core/api/index.js +32 -23
- package/build/lib/core/api/lance.d.ts +6 -0
- package/build/lib/core/api/offesecAgent.d.ts +1 -0
- package/build/lib/core/api/reasoning.d.ts +12 -0
- package/build/lib/core/api/targetedPentest.d.ts +1 -0
- package/build/lib/core/api/targetedPentest.js +14 -14
- package/build/lib/core/cli/reasoning.d.ts +4 -0
- package/build/lib/core/config/config.d.ts +10 -2
- package/build/lib/core/config/customProviders.d.ts +11 -0
- package/build/lib/core/config/index.js +1 -1
- package/build/lib/core/eventBus.js +1 -1
- package/build/lib/core/findings/registry.d.ts +3 -1
- package/build/lib/core/findings/registry.js +4 -4
- package/build/lib/core/observability/index.js +1 -1
- package/build/lib/core/observability/telemetry.d.ts +4 -0
- package/build/lib/core/session/index.d.ts +47 -0
- package/build/lib/core/session/index.js +6 -4
- package/build/lib/core/session/persistence.d.ts +7 -0
- package/build/lib/core/session/persistence.js +8 -4
- package/build/lib/core/tools/backends/index.js +1 -1
- package/build/lib/core/tools/backends/resolve.d.ts +1 -0
- package/build/lib/core/workflows/fastStrike.d.ts +3 -1
- package/build/lib/core/workflows/pentest.d.ts +12 -2
- package/build/lib/core/workflows/pentest.js +19 -19
- package/build/lib/core/workflows/seams.js +5 -5
- package/build/lib/core/workflows/threatModel.d.ts +1 -0
- package/build/lib/core/workflows/whiteboxAttackSurface.d.ts +6 -4
- package/build/lib/core/workflows/whiteboxAttackSurface.js +15 -15
- package/build/lib/index.js +25 -25
- package/build/{logs-x62b7hk1.js → logs-bcbjpjp6.js} +16 -13
- package/build/{native-rollout-evidence-c9yhfqcs.js → native-rollout-evidence-rph1774m.js} +5 -5
- package/build/offesecAgent-81h1p6ee.js +32 -0
- package/build/pentest-2q20q6n1.js +23 -0
- package/build/{pentests-e0f32aqx.js → pentests-jgw031mc.js} +16 -13
- package/build/recordedRunClient-6axj6fba.js +11 -0
- package/build/subagentSpawner-a861gvt0.js +12 -0
- package/build/targetedPentest-6yxznt7s.js +91 -0
- package/build/{targets-xj9g3q0z.js → targets-bjah19dp.js} +16 -13
- package/build/threatModel-739kpexm.js +24 -0
- package/build/{uninstall-7s784q4a.js → uninstall-yxrn9edt.js} +1 -1
- package/build/{upload-4gvhwmvr.js → upload-34ewrjp5.js} +4 -4
- package/build/{utils-cjrz64pc.js → utils-fzm66efw.js} +5 -5
- package/package.json +2 -1
- package/src/cli/issues.test.ts +17 -1
- package/src/cli/lance-verifier.test.ts +57 -0
- package/src/cli/lance-verifier.ts +73 -0
- package/src/cli/lance.test.ts +71 -0
- package/src/cli/lance.ts +48 -0
- package/src/cli.ts +109 -19
- package/src/core/agents/agentRuntime.test.ts +42 -0
- package/src/core/agents/agentRuntime.ts +7 -2
- package/src/core/agents/defineAgent.ts +3 -1
- package/src/core/agents/lance.test.ts +421 -0
- package/src/core/agents/lance.ts +701 -0
- package/src/core/agents/lanceJudge.test.ts +166 -0
- package/src/core/agents/lanceJudge.ts +115 -0
- package/src/core/agents/offSecAgent/index.ts +3 -0
- package/src/core/agents/offSecAgent/interruptedStepFinalization.test.ts +51 -0
- package/src/core/agents/offSecAgent/lance.test.ts +188 -0
- package/src/core/agents/offSecAgent/offensiveSecurityAgent.test.ts +92 -5
- package/src/core/agents/offSecAgent/offensiveSecurityAgent.ts +244 -41
- package/src/core/agents/offSecAgent/prompt.test.ts +46 -0
- package/src/core/agents/offSecAgent/prompt.ts +52 -8
- package/src/core/agents/offSecAgent/selectiveConstruction.publicPath.test.ts +38 -0
- package/src/core/agents/offSecAgent/selectiveConstruction.test.ts +64 -0
- package/src/core/agents/offSecAgent/subagentSpawner.hooks.test.ts +11 -3
- package/src/core/agents/offSecAgent/subagentSpawner.reasoning.test.ts +167 -0
- package/src/core/agents/offSecAgent/subagentSpawner.ts +82 -28
- package/src/core/agents/offSecAgent/toolExecutionContext.test.ts +230 -0
- package/src/core/agents/offSecAgent/toolExecutionContext.ts +56 -0
- package/src/core/agents/offSecAgent/toolLifecycle.ts +2 -0
- package/src/core/agents/offSecAgent/tools/delegateAuth.ts +4 -2
- package/src/core/agents/offSecAgent/tools/documentFinding.test.ts +9 -4
- package/src/core/agents/offSecAgent/tools/documentFinding.ts +10 -9
- package/src/core/agents/offSecAgent/tools/listMemories.test.ts +230 -0
- package/src/core/agents/offSecAgent/tools/listMemories.ts +73 -6
- package/src/core/agents/offSecAgent/tools/reportError.test.ts +6 -0
- package/src/core/agents/offSecAgent/tools/reportError.ts +10 -2
- package/src/core/agents/offSecAgent/tools/runAttackSurface.ts +7 -4
- package/src/core/agents/offSecAgent/tools/runPentestWorkflow.test.ts +57 -0
- package/src/core/agents/offSecAgent/tools/runPentestWorkflow.ts +21 -1
- package/src/core/agents/offSecAgent/tools/runWhiteboxScan.ts +5 -2
- package/src/core/agents/offSecAgent/tools/spawnCodingAgent.ts +50 -67
- package/src/core/agents/offSecAgent/tools/spawnPentestAgent.test.ts +79 -3
- package/src/core/agents/offSecAgent/tools/spawnPentestAgent.ts +36 -16
- package/src/core/agents/offSecAgent/tools/spawnPentestSwarm.ts +28 -5
- package/src/core/agents/offSecAgent/tools/threatModelGenerator.ts +4 -3
- package/src/core/agents/offSecAgent/tools/types.ts +12 -2
- package/src/core/agents/offSecAgent/tools/updateTask.ts +5 -1
- package/src/core/agents/offSecAgent/tools/whiteboxCandidates.ts +11 -4
- package/src/core/agents/offSecAgent/types.ts +22 -1
- package/src/core/agents/specialized/codeAgent/agent.test.ts +7 -0
- package/src/core/agents/specialized/codeAgent/agent.ts +9 -2
- package/src/core/agents/specialized/codeAgent/prompts.ts +6 -0
- package/src/core/agents/specialized/cvssScorer/index.test.ts +2 -1
- package/src/core/agents/specialized/cvssScorer/index.ts +3 -0
- package/src/core/agents/specialized/findingJudge/index.ts +6 -0
- package/src/core/agents/specialized/pentest/agent.test.ts +155 -0
- package/src/core/agents/specialized/pentest/agent.ts +181 -52
- package/src/core/agents/specialized/pentest/results.test.ts +59 -4
- package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.concurrency.test.ts +59 -2
- package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.hooks.test.ts +127 -0
- package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.ts +74 -68
- package/src/core/ai/ai.durability-hooks.test.ts +25 -7
- package/src/core/ai/ai.lance.test.ts +581 -0
- package/src/core/ai/ai.operator-context.bench.test.ts +1390 -0
- package/src/core/ai/ai.recorded-context.test.ts +89 -8
- package/src/core/ai/ai.test.ts +81 -59
- package/src/core/ai/ai.ts +453 -327
- package/src/core/ai/compactionTelemetry.test.ts +69 -17
- package/src/core/ai/index.ts +11 -0
- package/src/core/ai/models/anthropic.ts +6 -0
- package/src/core/ai/models/bedrock.ts +18 -0
- package/src/core/ai/models/claude-capabilities.ts +29 -8
- package/src/core/ai/models/concentrate.ts +52 -0
- package/src/core/ai/models/index.ts +25 -4
- package/src/core/ai/models/models.test.ts +179 -7
- package/src/core/ai/models/openrouter-reasoning.json +219 -0
- package/src/core/ai/models/openrouter.ts +43 -1
- package/src/core/ai/providers/anthropic-models.test.ts +60 -1
- package/src/core/ai/providers/bedrock-models.test.ts +81 -0
- package/src/core/ai/providers/concentrate.test.ts +31 -0
- package/src/core/ai/providers/concentrate.ts +11 -0
- package/src/core/ai/providers/custom.test.ts +74 -0
- package/src/core/ai/providers/custom.ts +3 -1
- package/src/core/ai/providers/gateway-models.test.ts +16 -6
- package/src/core/ai/providers/pensarFormatters.ts +8 -1
- package/src/core/ai/reasoning.test.ts +313 -0
- package/src/core/ai/reasoning.ts +500 -0
- package/src/core/ai/streamIdleTimeout.test.ts +21 -0
- package/src/core/ai/utils.native-rollout-evidence.test.ts +21 -8
- package/src/core/ai/utils.ts +269 -163
- package/src/core/api/attackSurface.ts +1 -0
- package/src/core/api/blackboxPentest.ts +9 -1
- package/src/core/api/index.ts +17 -0
- package/src/core/api/lance.ts +23 -0
- package/src/core/api/offesecAgent.test.ts +51 -0
- package/src/core/api/offesecAgent.ts +15 -1
- package/src/core/api/reasoning.test.ts +213 -0
- package/src/core/api/reasoning.ts +44 -0
- package/src/core/api/recordedRun.loop.test.ts +5 -1
- package/src/core/api/targetedPentest.test.ts +14 -2
- package/src/core/api/targetedPentest.ts +52 -5
- package/src/core/cli/reasoning.test.ts +149 -0
- package/src/core/cli/reasoning.ts +95 -0
- package/src/core/config/config.test.ts +35 -0
- package/src/core/config/config.ts +11 -0
- package/src/core/config/customProviders.ts +21 -2
- package/src/core/eventBus.ts +1 -0
- package/src/core/findings/registry.test.ts +9 -1
- package/src/core/findings/registry.ts +6 -0
- package/src/core/observability/telemetry.test.ts +131 -79
- package/src/core/observability/telemetry.ts +5 -0
- package/src/core/runtime/sqliteRunObservation.test.ts +1 -1
- package/src/core/session/index.ts +38 -0
- package/src/core/session/persistence.test.ts +62 -0
- package/src/core/session/persistence.ts +56 -0
- package/src/core/tools/backends/resolve.ts +7 -0
- package/src/core/tools/backends/whiteboxJobs.test.ts +9 -8
- package/src/core/workflows/fastStrike.seams.test.ts +149 -0
- package/src/core/workflows/fastStrike.test.ts +18 -1
- package/src/core/workflows/fastStrike.ts +106 -11
- package/src/core/workflows/pentest.test.ts +104 -0
- package/src/core/workflows/pentest.ts +128 -7
- package/src/core/workflows/seams.test.ts +13 -0
- package/src/core/workflows/seams.ts +5 -1
- package/src/core/workflows/threatModel.ts +2 -0
- package/src/core/workflows/whiteboxAttackSurface.test.ts +74 -2
- package/src/core/workflows/whiteboxAttackSurface.ts +102 -73
- package/src/tui/command-registry.test.ts +12 -0
- package/src/tui/command-registry.ts +10 -0
- package/src/tui/components/chat/config-view.tsx +6 -10
- package/src/tui/components/chat/home-view.tsx +2 -2
- package/src/tui/components/chat/input-area.tsx +2 -2
- package/src/tui/components/chat/loading-indicator.tsx +20 -4
- package/src/tui/components/chat/message-list.tsx +1 -0
- package/src/tui/components/commands/__tests__/advanced-settings-lifecycle.tsx +164 -0
- package/src/tui/components/commands/advanced-settings.test.ts +34 -0
- package/src/tui/components/commands/advanced-settings.tsx +220 -31
- package/src/tui/components/commands/models-display.tsx +7 -1
- package/src/tui/components/model-picker/ModelPicker.tsx +65 -103
- package/src/tui/components/model-picker/ModelPickerDialog.tsx +8 -14
- package/src/tui/components/model-picker/__tests__/reasoning-control.tsx +162 -0
- package/src/tui/components/model-picker/reasoning-control.test.ts +15 -0
- package/src/tui/components/operator-dashboard/index.tsx +166 -50
- package/src/tui/components/operator-dashboard/logic.test.ts +144 -2
- package/src/tui/components/operator-dashboard/logic.ts +54 -3
- package/src/tui/components/operator-dashboard/run-events.test.ts +3 -1
- package/src/tui/components/shared/__tests__/tool-renderer-lifecycle.tsx +18 -0
- package/src/tui/components/shared/action-label.test.ts +9 -0
- package/src/tui/components/shared/compacting-icon.tsx +18 -0
- package/src/tui/components/shared/index.ts +1 -0
- package/src/tui/components/shared/tool-registry.ts +1 -0
- package/src/tui/components/shared/tool-renderer.tsx +21 -7
- package/src/tui/context/agent.tsx +32 -79
- package/src/tui/context/route.tsx +2 -0
- package/src/tui/index.tsx +1 -0
- package/src/tui/keybindings/registry.ts +2 -0
- package/build/agent-6w7egcf1.js +0 -13
- package/build/agent-9zx75feh.js +0 -13
- package/build/blackboxAgent-s56sgx9b.js +0 -13
- package/build/cli-g06s7edq.js +0 -11
- package/build/lib/chunk-z96pbjhd.js +0 -56
- package/build/offesecAgent-5whnpeta.js +0 -22
- package/build/pentest-e61hc38s.js +0 -22
- package/build/recordedRunClient-dp032c32.js +0 -11
- package/build/targetedPentest-rcbs4nvp.js +0 -57
- package/build/threatModel-jkxfa40s.js +0 -21
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, expectTypeOf, it, vi } from "vitest";
|
|
2
2
|
import type { CreateAgentInput } from "../agents/offSecAgent";
|
|
3
|
+
import { sessions } from "../session";
|
|
3
4
|
import {
|
|
4
5
|
createOffensiveSecurityAgentClient,
|
|
5
6
|
type OffensiveSecurityAgentClientInput,
|
|
@@ -80,6 +81,7 @@ function existingSessionInput(overrides: Record<string, unknown> = {}) {
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
beforeEach(() => {
|
|
84
|
+
vi.restoreAllMocks();
|
|
83
85
|
Fake.order.length = 0;
|
|
84
86
|
Fake.instances.length = 0;
|
|
85
87
|
Fake.createGate = null;
|
|
@@ -88,6 +90,55 @@ beforeEach(() => {
|
|
|
88
90
|
});
|
|
89
91
|
|
|
90
92
|
describe("runOffensiveSecurityAgent", () => {
|
|
93
|
+
it("restores the session's effort only for the matching model", async () => {
|
|
94
|
+
const session = {
|
|
95
|
+
id: "ses_existing",
|
|
96
|
+
config: { modelReasoning: { model: "gpt-6.1-sol", setting: "low" } },
|
|
97
|
+
};
|
|
98
|
+
await runOffensiveSecurityAgent(
|
|
99
|
+
existingSessionInput({ model: "gpt-6.1-sol", session }) as never,
|
|
100
|
+
);
|
|
101
|
+
expect(Fake.instances[0].input.reasoning).toBe("low");
|
|
102
|
+
await runOffensiveSecurityAgent(
|
|
103
|
+
existingSessionInput({ model: "gemini-3-pro-preview", session }) as never,
|
|
104
|
+
);
|
|
105
|
+
expect(Fake.instances[1].input.reasoning).toBeUndefined();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("persists an explicit override before starting the resumed agent", async () => {
|
|
109
|
+
const session = {
|
|
110
|
+
id: "ses_existing",
|
|
111
|
+
config: { modelReasoning: { model: "gpt-6.1-sol", setting: "low" } },
|
|
112
|
+
};
|
|
113
|
+
const update = vi
|
|
114
|
+
.spyOn(sessions, "updateModelReasoning")
|
|
115
|
+
.mockResolvedValue({
|
|
116
|
+
...session,
|
|
117
|
+
config: { modelReasoning: { model: "gpt-6.1-sol", setting: "high" } },
|
|
118
|
+
} as never);
|
|
119
|
+
await runOffensiveSecurityAgent(
|
|
120
|
+
existingSessionInput({
|
|
121
|
+
model: "gpt-6.1-sol",
|
|
122
|
+
session,
|
|
123
|
+
reasoning: "high",
|
|
124
|
+
}) as never,
|
|
125
|
+
);
|
|
126
|
+
expect(update).toHaveBeenCalledWith("ses_existing", "gpt-6.1-sol", "high");
|
|
127
|
+
expect(Fake.instances[0].input.reasoning).toBe("high");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("rejects unsupported explicit effort before constructing an agent", async () => {
|
|
131
|
+
await expect(
|
|
132
|
+
runOffensiveSecurityAgent(
|
|
133
|
+
existingSessionInput({
|
|
134
|
+
model: "gemini-3-pro-preview",
|
|
135
|
+
reasoning: "max",
|
|
136
|
+
}) as never,
|
|
137
|
+
),
|
|
138
|
+
).rejects.toThrow("not supported");
|
|
139
|
+
expect(Fake.instances).toHaveLength(0);
|
|
140
|
+
});
|
|
141
|
+
|
|
91
142
|
it("constructs synchronously for a pre-existing session and fires onSessionReady before consume", async () => {
|
|
92
143
|
const onSessionReady = vi.fn(() => Fake.order.push("onSessionReady"));
|
|
93
144
|
const running = runOffensiveSecurityAgent(
|
|
@@ -6,10 +6,15 @@ import {
|
|
|
6
6
|
} from "../agents/offSecAgent";
|
|
7
7
|
import { startAgentExecution } from "../runtime/agentExecution";
|
|
8
8
|
import type { SessionInfo } from "../session";
|
|
9
|
+
import {
|
|
10
|
+
persistRootReasoningOverride,
|
|
11
|
+
resolveRootReasoning,
|
|
12
|
+
} from "./reasoning";
|
|
9
13
|
|
|
10
14
|
export interface RunAgentResult {
|
|
11
15
|
streamResult: StreamTextResult<ToolSet, never>;
|
|
12
16
|
session: SessionInfo;
|
|
17
|
+
lance?: import("../agents/lance").LanceMetrics;
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
/**
|
|
@@ -27,6 +32,9 @@ export async function runOffensiveSecurityAgent(
|
|
|
27
32
|
onSessionReady?: (session: SessionInfo) => void;
|
|
28
33
|
},
|
|
29
34
|
): Promise<RunAgentResult> {
|
|
35
|
+
const resolved = resolveRootReasoning(input);
|
|
36
|
+
const persisted = persistRootReasoningOverride(input);
|
|
37
|
+
input = persisted ? { ...resolved, session: await persisted } : resolved;
|
|
30
38
|
const agent = input.session
|
|
31
39
|
? new OffensiveSecurityAgent(input as OffensiveSecurityAgentInput)
|
|
32
40
|
: await OffensiveSecurityAgent.create(input as CreateAgentInput);
|
|
@@ -34,7 +42,13 @@ export async function runOffensiveSecurityAgent(
|
|
|
34
42
|
input.onSessionReady?.(agent.session);
|
|
35
43
|
|
|
36
44
|
await startAgentExecution(agent).result;
|
|
37
|
-
return {
|
|
45
|
+
return {
|
|
46
|
+
streamResult: agent.streamResult,
|
|
47
|
+
session: agent.session,
|
|
48
|
+
...(agent.lanceRuntime
|
|
49
|
+
? { lance: agent.lanceRuntime.metrics(input.model) }
|
|
50
|
+
: {}),
|
|
51
|
+
};
|
|
38
52
|
}
|
|
39
53
|
|
|
40
54
|
/** Either agent-input shape, minus the abort signal the client owns. */
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import type { ReasoningSetting } from "../ai";
|
|
3
|
+
import type { SessionInfo } from "../session";
|
|
4
|
+
import { runOffensiveSecurityAgent } from "./offesecAgent";
|
|
5
|
+
import { startTargetedPentestAgent } from "./targetedPentest";
|
|
6
|
+
|
|
7
|
+
const mocks = vi.hoisted(() => {
|
|
8
|
+
const inputs: Array<Record<string, unknown>> = [];
|
|
9
|
+
const consume = vi.fn(async () => ({ findings: [] }));
|
|
10
|
+
const update = vi.fn();
|
|
11
|
+
const abortAndDrain = vi.fn(async () => {});
|
|
12
|
+
class Agent {
|
|
13
|
+
session: unknown;
|
|
14
|
+
streamResult = {};
|
|
15
|
+
eventBus = { on: vi.fn() };
|
|
16
|
+
drained = Promise.resolve();
|
|
17
|
+
consume = consume;
|
|
18
|
+
abortAndDrain = abortAndDrain;
|
|
19
|
+
constructor(input: Record<string, unknown>) {
|
|
20
|
+
inputs.push(input);
|
|
21
|
+
this.session = input.session;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { inputs, consume, update, abortAndDrain, Agent };
|
|
25
|
+
});
|
|
26
|
+
vi.mock("../agents/offSecAgent", () => ({
|
|
27
|
+
OffensiveSecurityAgent: mocks.Agent,
|
|
28
|
+
}));
|
|
29
|
+
vi.mock("../agents/specialized/pentest/agent", () => ({
|
|
30
|
+
TargetedPentestAgent: mocks.Agent,
|
|
31
|
+
}));
|
|
32
|
+
vi.mock("../session", () => ({
|
|
33
|
+
sessions: { updateModelReasoning: mocks.update },
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
function session(model = "gpt-6.1-sol", setting: ReasoningSetting = "low") {
|
|
37
|
+
return {
|
|
38
|
+
id: "root",
|
|
39
|
+
config: { modelReasoning: { model, setting } },
|
|
40
|
+
} as SessionInfo;
|
|
41
|
+
}
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
mocks.inputs.length = 0;
|
|
44
|
+
mocks.consume.mockClear();
|
|
45
|
+
mocks.abortAndDrain.mockClear();
|
|
46
|
+
mocks.update
|
|
47
|
+
.mockReset()
|
|
48
|
+
.mockImplementation(async (_id, model, setting) => session(model, setting));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe.each([
|
|
52
|
+
{
|
|
53
|
+
name: "generic",
|
|
54
|
+
run: (input: Record<string, unknown>) =>
|
|
55
|
+
runOffensiveSecurityAgent(input as never),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "targeted",
|
|
59
|
+
run: (input: Record<string, unknown>) =>
|
|
60
|
+
startTargetedPentestAgent(input as never).result,
|
|
61
|
+
},
|
|
62
|
+
])("$name root reasoning", ({ run }) => {
|
|
63
|
+
it.each([
|
|
64
|
+
{
|
|
65
|
+
name: "matching saved selection",
|
|
66
|
+
overrides: {},
|
|
67
|
+
expected: "low",
|
|
68
|
+
persists: false,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "explicit override",
|
|
72
|
+
overrides: { reasoning: "high" },
|
|
73
|
+
expected: "high",
|
|
74
|
+
persists: true,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "explicit default",
|
|
78
|
+
overrides: { reasoning: "default" },
|
|
79
|
+
expected: "default",
|
|
80
|
+
persists: true,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "saved ahead of legacy",
|
|
84
|
+
overrides: { openAIReasoningEffort: "high" },
|
|
85
|
+
expected: "low",
|
|
86
|
+
persists: false,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "model switch",
|
|
90
|
+
overrides: { model: "gpt-5" },
|
|
91
|
+
expected: "medium",
|
|
92
|
+
persists: false,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: "legacy fallback on model switch",
|
|
96
|
+
overrides: {
|
|
97
|
+
model: "concentrate:glm-5.3",
|
|
98
|
+
openAIReasoningEffort: "xhigh",
|
|
99
|
+
},
|
|
100
|
+
expected: "max",
|
|
101
|
+
persists: false,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: "explicit none",
|
|
105
|
+
overrides: { model: "gpt-5.1", reasoning: "none" },
|
|
106
|
+
expected: "none",
|
|
107
|
+
persists: true,
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: "explicit disabled",
|
|
111
|
+
overrides: { model: "claude-sonnet-4-5", reasoning: "disabled" },
|
|
112
|
+
expected: "disabled",
|
|
113
|
+
persists: true,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: "child override",
|
|
117
|
+
overrides: { subagentId: "child", reasoning: "high" },
|
|
118
|
+
expected: "high",
|
|
119
|
+
persists: false,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: "child with no inherited choice",
|
|
123
|
+
overrides: { subagentId: "child" },
|
|
124
|
+
expected: "medium",
|
|
125
|
+
persists: false,
|
|
126
|
+
},
|
|
127
|
+
])("resolves $name before constructing the agent", async ({
|
|
128
|
+
overrides,
|
|
129
|
+
expected,
|
|
130
|
+
persists,
|
|
131
|
+
}) => {
|
|
132
|
+
const saved = session();
|
|
133
|
+
const input = {
|
|
134
|
+
session: saved,
|
|
135
|
+
model: "gpt-6.1-sol",
|
|
136
|
+
eventBus: {},
|
|
137
|
+
...overrides,
|
|
138
|
+
};
|
|
139
|
+
await run(input);
|
|
140
|
+
expect(mocks.inputs).toHaveLength(1);
|
|
141
|
+
expect(mocks.inputs[0]).toMatchObject({
|
|
142
|
+
model: input.model,
|
|
143
|
+
reasoning: expected,
|
|
144
|
+
});
|
|
145
|
+
expect(mocks.update).toHaveBeenCalledTimes(persists ? 1 : 0);
|
|
146
|
+
if (persists) {
|
|
147
|
+
expect(mocks.update).toHaveBeenCalledWith("root", input.model, expected);
|
|
148
|
+
expect(
|
|
149
|
+
(mocks.inputs[0].session as SessionInfo).config?.modelReasoning,
|
|
150
|
+
).toEqual({ model: input.model, setting: expected });
|
|
151
|
+
}
|
|
152
|
+
expect(saved.config?.modelReasoning).toEqual({
|
|
153
|
+
model: "gpt-6.1-sol",
|
|
154
|
+
setting: "low",
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("rejects unsupported explicit settings before construction or persistence", async () => {
|
|
159
|
+
await expect(async () =>
|
|
160
|
+
run({ session: session(), model: "gpt-6.1-sol", reasoning: "none" }),
|
|
161
|
+
).rejects.toThrow("not supported");
|
|
162
|
+
expect(mocks.inputs).toHaveLength(0);
|
|
163
|
+
expect(mocks.update).not.toHaveBeenCalled();
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe("targeted reasoning persistence lifecycle", () => {
|
|
168
|
+
function pendingSave() {
|
|
169
|
+
let resolve!: (session: SessionInfo) => void;
|
|
170
|
+
mocks.update.mockReturnValue(
|
|
171
|
+
new Promise<SessionInfo>((done) => {
|
|
172
|
+
resolve = done;
|
|
173
|
+
}),
|
|
174
|
+
);
|
|
175
|
+
const handle = startTargetedPentestAgent({
|
|
176
|
+
session: session(),
|
|
177
|
+
model: "gpt-6.1-sol",
|
|
178
|
+
reasoning: "high",
|
|
179
|
+
eventBus: {},
|
|
180
|
+
} as never);
|
|
181
|
+
return { handle, resolve };
|
|
182
|
+
}
|
|
183
|
+
it("returns a handle immediately and waits for persistence before constructing", async () => {
|
|
184
|
+
const { handle, resolve } = pendingSave();
|
|
185
|
+
expect(mocks.inputs).toHaveLength(0);
|
|
186
|
+
resolve(session("gpt-6.1-sol", "high"));
|
|
187
|
+
await handle.result;
|
|
188
|
+
expect(mocks.consume).toHaveBeenCalledOnce();
|
|
189
|
+
await handle.abortAndDrain();
|
|
190
|
+
expect(mocks.abortAndDrain).toHaveBeenCalledOnce();
|
|
191
|
+
await handle.drained;
|
|
192
|
+
});
|
|
193
|
+
it("does not start a late agent after teardown while persistence is pending", async () => {
|
|
194
|
+
const { handle, resolve } = pendingSave();
|
|
195
|
+
await handle.abortAndDrain();
|
|
196
|
+
resolve(session("gpt-6.1-sol", "high"));
|
|
197
|
+
await expect(handle.result).rejects.toMatchObject({ name: "AbortError" });
|
|
198
|
+
await handle.drained;
|
|
199
|
+
expect(mocks.inputs).toHaveLength(0);
|
|
200
|
+
expect(mocks.consume).not.toHaveBeenCalled();
|
|
201
|
+
});
|
|
202
|
+
it("propagates persistence failure without starting the agent", async () => {
|
|
203
|
+
mocks.update.mockRejectedValue(new Error("save failed"));
|
|
204
|
+
const handle = startTargetedPentestAgent({
|
|
205
|
+
session: session(),
|
|
206
|
+
model: "gpt-6.1-sol",
|
|
207
|
+
reasoning: "high",
|
|
208
|
+
} as never);
|
|
209
|
+
await expect(handle.result).rejects.toThrow("save failed");
|
|
210
|
+
await handle.drained;
|
|
211
|
+
expect(mocks.inputs).toHaveLength(0);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getModelSettings } from "../ai";
|
|
2
|
+
import type { ReasoningInput } from "../ai/reasoning";
|
|
3
|
+
import type { AIAuthConfig } from "../ai/utils";
|
|
4
|
+
import { type SessionInfo, sessions } from "../session";
|
|
5
|
+
|
|
6
|
+
type AgentReasoningInput = ReasoningInput & {
|
|
7
|
+
model: string;
|
|
8
|
+
authConfig?: AIAuthConfig;
|
|
9
|
+
session?: SessionInfo;
|
|
10
|
+
subagentId?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function resolveRootReasoning<T extends AgentReasoningInput>(
|
|
14
|
+
input: T,
|
|
15
|
+
): T {
|
|
16
|
+
const saved = input.session?.config?.modelReasoning;
|
|
17
|
+
const reasoning = getModelSettings({
|
|
18
|
+
...input,
|
|
19
|
+
reasoning:
|
|
20
|
+
input.reasoning ??
|
|
21
|
+
(!input.subagentId && saved?.model === input.model
|
|
22
|
+
? saved.setting
|
|
23
|
+
: undefined),
|
|
24
|
+
}).reasoning;
|
|
25
|
+
return reasoning === input.reasoning ? input : { ...input, reasoning };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function persistRootReasoningOverride(
|
|
29
|
+
input: AgentReasoningInput,
|
|
30
|
+
): Promise<SessionInfo> | undefined {
|
|
31
|
+
const saved = input.session?.config?.modelReasoning;
|
|
32
|
+
if (
|
|
33
|
+
input.session &&
|
|
34
|
+
!input.subagentId &&
|
|
35
|
+
input.reasoning !== undefined &&
|
|
36
|
+
(saved?.model !== input.model || saved.setting !== input.reasoning)
|
|
37
|
+
) {
|
|
38
|
+
return sessions.updateModelReasoning(
|
|
39
|
+
input.session.id,
|
|
40
|
+
input.model,
|
|
41
|
+
input.reasoning,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -173,7 +173,11 @@ describe("recorded runs through the real agent and SDK loop", () => {
|
|
|
173
173
|
void running.catch(() => {});
|
|
174
174
|
|
|
175
175
|
try {
|
|
176
|
-
|
|
176
|
+
// Booting the agent and reaching the first tool call exceeds waitFor's 1s
|
|
177
|
+
// default on a loaded machine; the pause assertions below are the contract.
|
|
178
|
+
await vi.waitFor(() => expect(hits).toEqual(["/first"]), {
|
|
179
|
+
timeout: 15_000,
|
|
180
|
+
});
|
|
177
181
|
const client = await openSqliteRunStore(join(root, "runs.sqlite"));
|
|
178
182
|
try {
|
|
179
183
|
const control = await client.getControl(input.runId);
|
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
// Mock ignores everything except __testResult; cast so the real input shape isn't required.
|
|
50
50
|
function run(testResult: unknown) {
|
|
51
51
|
return runTargetedPentestAgent({
|
|
52
|
+
model: "test-model",
|
|
52
53
|
__testResult: testResult,
|
|
53
54
|
eventBus: {},
|
|
54
55
|
} as never);
|
|
@@ -104,6 +105,7 @@ describe("runTargetedPentestAgent result-shape resilience", () => {
|
|
|
104
105
|
describe("startTargetedPentestAgent handle", () => {
|
|
105
106
|
it("exposes result + abortAndDrain before consume settles", async () => {
|
|
106
107
|
const handle = startTargetedPentestAgent({
|
|
108
|
+
model: "test-model",
|
|
107
109
|
__testResult: { findings: [] },
|
|
108
110
|
eventBus: {},
|
|
109
111
|
} as never);
|
|
@@ -115,6 +117,7 @@ describe("startTargetedPentestAgent handle", () => {
|
|
|
115
117
|
it("exposes the post-consume drain promise, not the pre-run placeholder", () => {
|
|
116
118
|
const gate = Promise.resolve();
|
|
117
119
|
const handle = startTargetedPentestAgent({
|
|
120
|
+
model: "test-model",
|
|
118
121
|
__testResult: { findings: [] },
|
|
119
122
|
__drainGate: gate,
|
|
120
123
|
eventBus: {},
|
|
@@ -123,13 +126,20 @@ describe("startTargetedPentestAgent handle", () => {
|
|
|
123
126
|
});
|
|
124
127
|
|
|
125
128
|
it("passes the caller's input object to the agent untouched", () => {
|
|
126
|
-
const input = {
|
|
129
|
+
const input = {
|
|
130
|
+
model: "test-model",
|
|
131
|
+
__testResult: { findings: [] },
|
|
132
|
+
eventBus: {},
|
|
133
|
+
};
|
|
127
134
|
startTargetedPentestAgent(input as never);
|
|
128
135
|
expect(constructions[0]?.input).toBe(input);
|
|
129
136
|
});
|
|
130
137
|
|
|
131
138
|
it("wires default logging listeners only when no eventBus is provided", () => {
|
|
132
|
-
startTargetedPentestAgent({
|
|
139
|
+
startTargetedPentestAgent({
|
|
140
|
+
model: "test-model",
|
|
141
|
+
__testResult: { findings: [] },
|
|
142
|
+
} as never);
|
|
133
143
|
expect(constructions[0]?.on).toEqual([
|
|
134
144
|
"text-delta",
|
|
135
145
|
"tool-call-complete",
|
|
@@ -140,6 +150,7 @@ describe("startTargetedPentestAgent handle", () => {
|
|
|
140
150
|
|
|
141
151
|
it("leaves the agent bus untouched when the input provides an eventBus", () => {
|
|
142
152
|
startTargetedPentestAgent({
|
|
153
|
+
model: "test-model",
|
|
143
154
|
__testResult: { findings: [] },
|
|
144
155
|
eventBus: {},
|
|
145
156
|
} as never);
|
|
@@ -154,6 +165,7 @@ describe("runTargetedPentestAgent completion boundary", () => {
|
|
|
154
165
|
settleDrain = resolve;
|
|
155
166
|
});
|
|
156
167
|
const runPromise = runTargetedPentestAgent({
|
|
168
|
+
model: "test-model",
|
|
157
169
|
__testResult: { findings: [] },
|
|
158
170
|
__drainGate: drainGate,
|
|
159
171
|
eventBus: {},
|
|
@@ -7,9 +7,14 @@ import {
|
|
|
7
7
|
type AgentExecutionHandle,
|
|
8
8
|
startAgentExecution,
|
|
9
9
|
} from "../runtime/agentExecution";
|
|
10
|
+
import {
|
|
11
|
+
persistRootReasoningOverride,
|
|
12
|
+
resolveRootReasoning,
|
|
13
|
+
} from "./reasoning";
|
|
10
14
|
|
|
11
15
|
export type TargetedPentestResult = {
|
|
12
16
|
findings: PentestResult["findings"];
|
|
17
|
+
reportedFindings?: string[];
|
|
13
18
|
findingsPath?: string;
|
|
14
19
|
pocsPath?: string;
|
|
15
20
|
objectiveResults?: PentestResult["objectiveResults"];
|
|
@@ -20,7 +25,10 @@ export type TargetedPentestResult = {
|
|
|
20
25
|
export type TargetedPentestHandle = AgentExecutionHandle<TargetedPentestResult>;
|
|
21
26
|
|
|
22
27
|
/** Guard against malformed consume() shapes (don't crash on findings.length). */
|
|
23
|
-
function normalizeResult(
|
|
28
|
+
function normalizeResult(
|
|
29
|
+
result: unknown,
|
|
30
|
+
findingPolicy?: PentestAgentInput["findingPolicy"],
|
|
31
|
+
): TargetedPentestResult {
|
|
24
32
|
const r = result as Partial<PentestResult> | null | undefined;
|
|
25
33
|
if (!r || !Array.isArray(r.findings)) {
|
|
26
34
|
console.error(
|
|
@@ -30,12 +38,22 @@ function normalizeResult(result: unknown): TargetedPentestResult {
|
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
const findings = Array.isArray(r?.findings) ? r.findings : [];
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
const reportedFindings = Array.isArray(r?.reportedFindings)
|
|
42
|
+
? r.reportedFindings
|
|
43
|
+
: [];
|
|
44
|
+
if (findingPolicy === "external") {
|
|
45
|
+
console.log(
|
|
46
|
+
`\nReported ${reportedFindings.length} finding(s) for external evaluation`,
|
|
47
|
+
);
|
|
48
|
+
} else {
|
|
49
|
+
console.log(`\nFound ${findings.length} vulnerabilities`);
|
|
50
|
+
console.log(`Findings: ${r?.findingsPath}`);
|
|
51
|
+
console.log(`POCs: ${r?.pocsPath}`);
|
|
52
|
+
}
|
|
36
53
|
|
|
37
54
|
return {
|
|
38
55
|
findings,
|
|
56
|
+
...(findingPolicy === "external" ? { reportedFindings } : {}),
|
|
39
57
|
findingsPath: r?.findingsPath,
|
|
40
58
|
pocsPath: r?.pocsPath,
|
|
41
59
|
objectiveResults: r?.objectiveResults,
|
|
@@ -47,6 +65,35 @@ function normalizeResult(result: unknown): TargetedPentestResult {
|
|
|
47
65
|
export function startTargetedPentestAgent(
|
|
48
66
|
input: PentestAgentInput,
|
|
49
67
|
): TargetedPentestHandle {
|
|
68
|
+
const resolved = resolveRootReasoning(input);
|
|
69
|
+
const persisted = persistRootReasoningOverride(input);
|
|
70
|
+
input = resolved;
|
|
71
|
+
if (!persisted) return startResolvedAgent(input);
|
|
72
|
+
|
|
73
|
+
let execution: TargetedPentestHandle | undefined;
|
|
74
|
+
let cancelled = false;
|
|
75
|
+
const started = persisted.then((session) => {
|
|
76
|
+
// A teardown during persistence must prevent a later agent from starting.
|
|
77
|
+
if (cancelled)
|
|
78
|
+
throw new DOMException("Agent aborted before start", "AbortError");
|
|
79
|
+
input.abortSignal?.throwIfAborted();
|
|
80
|
+
execution = startResolvedAgent({ ...input, session });
|
|
81
|
+
return execution;
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
result: started.then((handle) => handle.result),
|
|
85
|
+
drained: started.then(
|
|
86
|
+
(handle) => handle.drained,
|
|
87
|
+
() => {},
|
|
88
|
+
),
|
|
89
|
+
abortAndDrain: async () => {
|
|
90
|
+
cancelled = true;
|
|
91
|
+
await execution?.abortAndDrain();
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function startResolvedAgent(input: PentestAgentInput): TargetedPentestHandle {
|
|
50
97
|
const agent = new TargetedPentestAgent(input);
|
|
51
98
|
|
|
52
99
|
if (!input.eventBus) {
|
|
@@ -62,7 +109,7 @@ export function startTargetedPentestAgent(
|
|
|
62
109
|
|
|
63
110
|
const { result, abortAndDrain, drained } = startAgentExecution(agent);
|
|
64
111
|
return {
|
|
65
|
-
result: result.then(normalizeResult),
|
|
112
|
+
result: result.then((value) => normalizeResult(value, input.findingPolicy)),
|
|
66
113
|
abortAndDrain,
|
|
67
114
|
drained,
|
|
68
115
|
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { buildReasoningProviderOptions } from "../ai";
|
|
3
|
+
import type { Config } from "../config/config";
|
|
4
|
+
import { getSavedReasoning, resolveCliReasoning } from "./reasoning";
|
|
5
|
+
|
|
6
|
+
describe("CLI reasoning selection", () => {
|
|
7
|
+
it.each([
|
|
8
|
+
"claude-sonnet-4-5",
|
|
9
|
+
"claude-opus-5-5",
|
|
10
|
+
"anthropic/claude-opus-4.6",
|
|
11
|
+
"anthropic/claude-sonnet-4.5",
|
|
12
|
+
])("ignores a leftover GPT preference when switching to %s", (model) => {
|
|
13
|
+
const reasoning = getSavedReasoning(model, {
|
|
14
|
+
openAIReasoningEffort: "high",
|
|
15
|
+
});
|
|
16
|
+
expect(reasoning).toBe("default");
|
|
17
|
+
expect(buildReasoningProviderOptions(model, { reasoning })).toEqual(
|
|
18
|
+
buildReasoningProviderOptions(model, {
|
|
19
|
+
reasoning: getSavedReasoning(model, {}),
|
|
20
|
+
}),
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
it.each([
|
|
24
|
+
true,
|
|
25
|
+
false,
|
|
26
|
+
])("ignores a leftover Claude toggle (%s) when switching to GPT", (enabled) => {
|
|
27
|
+
const model = "gpt-6.1-sol";
|
|
28
|
+
expect(getSavedReasoning(model, { reasoningEnabled: enabled })).toBe(
|
|
29
|
+
"default",
|
|
30
|
+
);
|
|
31
|
+
expect(
|
|
32
|
+
getSavedReasoning(model, {
|
|
33
|
+
reasoningEnabled: enabled,
|
|
34
|
+
openAIReasoningEffort: "low",
|
|
35
|
+
}),
|
|
36
|
+
).toBe("low");
|
|
37
|
+
});
|
|
38
|
+
it.each([
|
|
39
|
+
["concentrate:glm-5.3", "none", "low"],
|
|
40
|
+
["concentrate:glm-5.3", "minimal", "max"],
|
|
41
|
+
["concentrate:glm-5.3", "low", "low"],
|
|
42
|
+
["concentrate:glm-5.3", "medium", "high"],
|
|
43
|
+
["concentrate:glm-5.3", "high", "high"],
|
|
44
|
+
["concentrate:glm-5.3", "xhigh", "max"],
|
|
45
|
+
["concentrate:glm-5.3", "max", "max"],
|
|
46
|
+
["concentrate:glm-5.3", "ultra", "max"],
|
|
47
|
+
["gpt-6.1-sol", "ultra", "max"],
|
|
48
|
+
["gpt-5", "xhigh", "medium"],
|
|
49
|
+
] as const)("migrates %s / %s to %s identically to the API", (model, legacy, expected) => {
|
|
50
|
+
const reasoning = getSavedReasoning(model, {
|
|
51
|
+
openAIReasoningEffort: legacy,
|
|
52
|
+
});
|
|
53
|
+
expect(reasoning).toBe(expected);
|
|
54
|
+
expect(buildReasoningProviderOptions(model, { reasoning })).toEqual(
|
|
55
|
+
buildReasoningProviderOptions(model, { openAIReasoningEffort: legacy }),
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
it.each([
|
|
59
|
+
"claude-sonnet-4-5",
|
|
60
|
+
"claude-opus-5-5",
|
|
61
|
+
"anthropic/claude-opus-4.6",
|
|
62
|
+
])("migrates the %s thinking toggle identically to the API", (model) => {
|
|
63
|
+
for (const enabled of [true, false]) {
|
|
64
|
+
const reasoning = getSavedReasoning(model, { reasoningEnabled: enabled });
|
|
65
|
+
expect(buildReasoningProviderOptions(model, { reasoning })).toEqual(
|
|
66
|
+
buildReasoningProviderOptions(model, { enableThinking: enabled }),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
it("keeps explicit canonical values ahead of legacy migration and rejects invalid new selections", () => {
|
|
71
|
+
const model = "concentrate:glm-5.3";
|
|
72
|
+
const config: Pick<Config, "modelReasoning" | "openAIReasoningEffort"> = {
|
|
73
|
+
modelReasoning: { [model]: "low" },
|
|
74
|
+
openAIReasoningEffort: "xhigh",
|
|
75
|
+
};
|
|
76
|
+
const reasoning = getSavedReasoning(model, config);
|
|
77
|
+
expect(reasoning).toBe("low");
|
|
78
|
+
expect(
|
|
79
|
+
buildReasoningProviderOptions(model, {
|
|
80
|
+
reasoning,
|
|
81
|
+
openAIReasoningEffort: "xhigh",
|
|
82
|
+
}),
|
|
83
|
+
).toEqual(
|
|
84
|
+
buildReasoningProviderOptions(model, { openAIReasoningEffort: "low" }),
|
|
85
|
+
);
|
|
86
|
+
expect(() =>
|
|
87
|
+
resolveCliReasoning(["--effort", "xhigh"], model, config),
|
|
88
|
+
).toThrow("not supported");
|
|
89
|
+
expect(() =>
|
|
90
|
+
buildReasoningProviderOptions(model, { reasoning: "xhigh" }),
|
|
91
|
+
).toThrow("not supported");
|
|
92
|
+
});
|
|
93
|
+
it("gives an invocation precedence over the saved per-model preference", () => {
|
|
94
|
+
expect(
|
|
95
|
+
resolveCliReasoning(["--effort", "low"], "claude-opus-5-5", {
|
|
96
|
+
modelReasoning: { "claude-opus-5-5": "max" },
|
|
97
|
+
}),
|
|
98
|
+
).toBe("low");
|
|
99
|
+
expect(resolveCliReasoning(["--effort=high"], "gpt-6.1-sol", {})).toBe(
|
|
100
|
+
"high",
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
it("does not carry incompatible settings between models", () => {
|
|
104
|
+
const config = { modelReasoning: { "gpt-6.1-sol": "max" as const } };
|
|
105
|
+
expect(getSavedReasoning("gemini-3-pro-preview", config)).toBe("default");
|
|
106
|
+
expect(getSavedReasoning("gpt-6.1-sol", config)).toBe("max");
|
|
107
|
+
});
|
|
108
|
+
it("migrates supported legacy preferences without offering the ultra alias", () => {
|
|
109
|
+
expect(
|
|
110
|
+
getSavedReasoning("gpt-6.1-sol", { openAIReasoningEffort: "ultra" }),
|
|
111
|
+
).toBe("max");
|
|
112
|
+
expect(
|
|
113
|
+
getSavedReasoning("claude-sonnet-4-5", { reasoningEnabled: true }),
|
|
114
|
+
).toBe("enabled");
|
|
115
|
+
expect(
|
|
116
|
+
getSavedReasoning("anthropic/claude-opus-4.6", {
|
|
117
|
+
reasoningEnabled: true,
|
|
118
|
+
}),
|
|
119
|
+
).toBe("high");
|
|
120
|
+
expect(
|
|
121
|
+
getSavedReasoning("anthropic/claude-opus-4.6", {
|
|
122
|
+
reasoningEnabled: false,
|
|
123
|
+
}),
|
|
124
|
+
).toBe("none");
|
|
125
|
+
});
|
|
126
|
+
it.each([
|
|
127
|
+
["--effort"],
|
|
128
|
+
["--effort", "--model"],
|
|
129
|
+
["--effort", "ultra"],
|
|
130
|
+
["--effort", "none"],
|
|
131
|
+
["--effort", "low", "--extended-thinking"],
|
|
132
|
+
])("rejects invalid flags before execution: %j", (...args) => {
|
|
133
|
+
expect(() => resolveCliReasoning(args, "gpt-6.1-sol", {})).toThrow();
|
|
134
|
+
});
|
|
135
|
+
it("uses a toggle only where the model supports it", () => {
|
|
136
|
+
expect(
|
|
137
|
+
resolveCliReasoning(["--extended-thinking"], "claude-sonnet-4-5", {}),
|
|
138
|
+
).toBe("enabled");
|
|
139
|
+
expect(
|
|
140
|
+
resolveCliReasoning(["--no-extended-thinking"], "gemini-2.5-flash", {}),
|
|
141
|
+
).toBe("disabled");
|
|
142
|
+
expect(() =>
|
|
143
|
+
resolveCliReasoning(["--no-extended-thinking"], "claude-opus-5-5", {}),
|
|
144
|
+
).toThrow();
|
|
145
|
+
expect(() =>
|
|
146
|
+
resolveCliReasoning(["--extended-thinking"], "unknown", {}),
|
|
147
|
+
).toThrow();
|
|
148
|
+
});
|
|
149
|
+
});
|