@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
|
@@ -4,6 +4,7 @@ import { z } from "zod";
|
|
|
4
4
|
import { AttackPathSchema } from "../../../../lib/attack-path/types";
|
|
5
5
|
import { hasCanonicalName } from "../../../../lib/cwe/types";
|
|
6
6
|
import type { EvidenceFileEntry } from "../../../../lib/evidence/types";
|
|
7
|
+
import { getModelSettings } from "../../../ai";
|
|
7
8
|
import { createLogger } from "../../../logger/structured";
|
|
8
9
|
import { collectCommand } from "../../../tools/backends/collectCommand";
|
|
9
10
|
import {
|
|
@@ -11,7 +12,7 @@ import {
|
|
|
11
12
|
resolveArtifactFs,
|
|
12
13
|
resolveBackends,
|
|
13
14
|
resolveScriptRunner,
|
|
14
|
-
|
|
15
|
+
sandboxArtifactsRoot,
|
|
15
16
|
} from "../../../tools/backends/resolve";
|
|
16
17
|
import type { WriteOpts, WriteResult } from "../../../tools/backends/types";
|
|
17
18
|
import { scopedLogger } from "../../../util/lazyLogger";
|
|
@@ -200,18 +201,20 @@ function artifactPath(ctx: ToolContext, ...parts: string[]): string {
|
|
|
200
201
|
}
|
|
201
202
|
|
|
202
203
|
function artifactsRoot(ctx: ToolContext): string {
|
|
203
|
-
return isRealSandbox(ctx)
|
|
204
|
+
return isRealSandbox(ctx)
|
|
205
|
+
? sandboxArtifactsRoot(ctx.session)
|
|
206
|
+
: ctx.session.rootPath;
|
|
204
207
|
}
|
|
205
208
|
|
|
206
209
|
function pocsRoot(ctx: ToolContext): string {
|
|
207
210
|
return isRealSandbox(ctx)
|
|
208
|
-
? `${
|
|
211
|
+
? `${sandboxArtifactsRoot(ctx.session)}/pocs`
|
|
209
212
|
: ctx.session.pocsPath;
|
|
210
213
|
}
|
|
211
214
|
|
|
212
215
|
function findingsRoot(ctx: ToolContext): string {
|
|
213
216
|
return isRealSandbox(ctx)
|
|
214
|
-
? `${
|
|
217
|
+
? `${sandboxArtifactsRoot(ctx.session)}/findings`
|
|
215
218
|
: ctx.session.findingsPath;
|
|
216
219
|
}
|
|
217
220
|
|
|
@@ -497,16 +500,13 @@ CRITICAL RULES — READ BEFORE CALLING:
|
|
|
497
500
|
spec: { type: "finding-judge", judgeInput, target: ctx.target },
|
|
498
501
|
runtime: {
|
|
499
502
|
session: ctx.session,
|
|
500
|
-
|
|
501
|
-
authConfig: ctx.authConfig,
|
|
503
|
+
...getModelSettings(ctx),
|
|
502
504
|
abortSignal: ctx.abortSignal,
|
|
503
505
|
sandbox: ctx.sandbox,
|
|
504
506
|
backends: ctx.backends,
|
|
505
|
-
enableThinking: ctx.enableThinking,
|
|
506
|
-
thinkingEffort: ctx.thinkingEffort,
|
|
507
|
-
openAIReasoningEffort: ctx.openAIReasoningEffort,
|
|
508
507
|
languageModelMiddleware: ctx.languageModelMiddleware,
|
|
509
508
|
usageRecorder: ctx.usageRecorder,
|
|
509
|
+
toolExecutionContext: ctx.toolExecutionContext,
|
|
510
510
|
streamIdFactory: ctx.streamIdFactory,
|
|
511
511
|
},
|
|
512
512
|
parentBus: ctx.eventBus,
|
|
@@ -626,6 +626,7 @@ CRITICAL RULES — READ BEFORE CALLING:
|
|
|
626
626
|
ctx.abortSignal,
|
|
627
627
|
ctx.session.id,
|
|
628
628
|
{
|
|
629
|
+
reasoning: ctx.reasoning,
|
|
629
630
|
languageModelMiddleware: ctx.languageModelMiddleware,
|
|
630
631
|
usageRecorder: ctx.usageRecorder,
|
|
631
632
|
},
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
type Memory,
|
|
4
|
+
type MemoryBackend,
|
|
5
|
+
type MemorySummary,
|
|
6
|
+
withMemoryBackend,
|
|
7
|
+
} from "../../../memory";
|
|
8
|
+
import { type GetMemoryResult, getMemory } from "./getMemory";
|
|
9
|
+
import { type ListMemoriesResult, listMemories } from "./listMemories";
|
|
10
|
+
import type { ToolContext } from "./types";
|
|
11
|
+
|
|
12
|
+
const ctx = { session: { id: "synthetic" } } as ToolContext;
|
|
13
|
+
const options = { toolCallId: "synthetic-call", messages: [] };
|
|
14
|
+
const list = listMemories(ctx).execute;
|
|
15
|
+
const get = getMemory(ctx).execute;
|
|
16
|
+
if (!list || !get) throw new Error("Memory tools must be executable");
|
|
17
|
+
|
|
18
|
+
describe("list_memories pagination", () => {
|
|
19
|
+
it("continues from the first unreturned summary when the character budget fills", async () => {
|
|
20
|
+
const summaries: MemorySummary[] = Array.from({ length: 30 }, (_, i) => ({
|
|
21
|
+
id: `large-${i}`,
|
|
22
|
+
category: "general",
|
|
23
|
+
title: "t".repeat(300),
|
|
24
|
+
tags: Array.from({ length: 8 }, () => "tag".repeat(40)),
|
|
25
|
+
createdAt: new Date(Date.UTC(2026, 0, 1) - i * 1_000).toISOString(),
|
|
26
|
+
}));
|
|
27
|
+
const backend: MemoryBackend = {
|
|
28
|
+
add: vi.fn(async () => {
|
|
29
|
+
throw new Error("unexpected add");
|
|
30
|
+
}),
|
|
31
|
+
list: vi.fn(async () => summaries),
|
|
32
|
+
get: vi.fn(async () => null),
|
|
33
|
+
};
|
|
34
|
+
await withMemoryBackend(backend, async () => {
|
|
35
|
+
const first = (await list(
|
|
36
|
+
{ toolCallDescription: "List large synthetic memories" },
|
|
37
|
+
options,
|
|
38
|
+
)) as ListMemoriesResult;
|
|
39
|
+
expect(first.memories.length).toBeLessThan(20);
|
|
40
|
+
expect(JSON.stringify(first).length).toBeLessThan(12_500);
|
|
41
|
+
expect(first.nextCursor).toEqual({
|
|
42
|
+
id: first.memories.at(-1)?.id,
|
|
43
|
+
category: first.memories.at(-1)?.category,
|
|
44
|
+
createdAt: first.memories.at(-1)?.createdAt,
|
|
45
|
+
});
|
|
46
|
+
const second = (await list(
|
|
47
|
+
{
|
|
48
|
+
cursor: first.nextCursor ?? undefined,
|
|
49
|
+
toolCallDescription: "Continue large synthetic memories",
|
|
50
|
+
},
|
|
51
|
+
options,
|
|
52
|
+
)) as ListMemoriesResult;
|
|
53
|
+
expect(second.memories[0]?.id).toBe(summaries[first.memories.length]?.id);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("continues past the last returned memory when a newer one is added", async () => {
|
|
58
|
+
const original: MemorySummary[] = Array.from({ length: 30 }, (_, i) => ({
|
|
59
|
+
id: `memory-${i}`,
|
|
60
|
+
category: "general",
|
|
61
|
+
title: `Memory ${i}`,
|
|
62
|
+
tags: [],
|
|
63
|
+
createdAt: new Date(Date.UTC(2026, 0, 1) - i * 1_000).toISOString(),
|
|
64
|
+
}));
|
|
65
|
+
let summaries = original;
|
|
66
|
+
const backend: MemoryBackend = {
|
|
67
|
+
add: vi.fn(async () => {
|
|
68
|
+
throw new Error("unexpected add");
|
|
69
|
+
}),
|
|
70
|
+
list: vi.fn(async () => summaries),
|
|
71
|
+
get: vi.fn(async () => null),
|
|
72
|
+
};
|
|
73
|
+
await withMemoryBackend(backend, async () => {
|
|
74
|
+
const first = (await list(
|
|
75
|
+
{ toolCallDescription: "List first memory page" },
|
|
76
|
+
options,
|
|
77
|
+
)) as ListMemoriesResult;
|
|
78
|
+
summaries = [
|
|
79
|
+
{
|
|
80
|
+
...original[0],
|
|
81
|
+
id: "memory-new",
|
|
82
|
+
createdAt: new Date(Date.UTC(2026, 0, 2)).toISOString(),
|
|
83
|
+
},
|
|
84
|
+
...original,
|
|
85
|
+
];
|
|
86
|
+
const second = (await list(
|
|
87
|
+
{
|
|
88
|
+
cursor: first.nextCursor ?? undefined,
|
|
89
|
+
toolCallDescription: "Continue after an insertion",
|
|
90
|
+
},
|
|
91
|
+
options,
|
|
92
|
+
)) as ListMemoriesResult;
|
|
93
|
+
|
|
94
|
+
expect(first.memories.map((memory) => memory.id)).toEqual(
|
|
95
|
+
original.slice(0, 20).map((memory) => memory.id),
|
|
96
|
+
);
|
|
97
|
+
expect(second.memories.map((memory) => memory.id)).toEqual(
|
|
98
|
+
original.slice(20).map((memory) => memory.id),
|
|
99
|
+
);
|
|
100
|
+
expect(second.count).toBe(31);
|
|
101
|
+
expect(second.nextCursor).toBeNull();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("uses stable tie ordering when the cursor memory is deleted", async () => {
|
|
106
|
+
const original: MemorySummary[] = Array.from({ length: 25 }, (_, i) => ({
|
|
107
|
+
id: `memory-${i.toString().padStart(2, "0")}`,
|
|
108
|
+
category: "general",
|
|
109
|
+
title: `Memory ${i}`,
|
|
110
|
+
tags: [],
|
|
111
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
112
|
+
}));
|
|
113
|
+
let summaries = [...original].reverse();
|
|
114
|
+
const backend: MemoryBackend = {
|
|
115
|
+
add: vi.fn(async () => {
|
|
116
|
+
throw new Error("unexpected add");
|
|
117
|
+
}),
|
|
118
|
+
list: vi.fn(async () => summaries),
|
|
119
|
+
get: vi.fn(async () => null),
|
|
120
|
+
};
|
|
121
|
+
await withMemoryBackend(backend, async () => {
|
|
122
|
+
const first = (await list(
|
|
123
|
+
{ toolCallDescription: "List tied memories" },
|
|
124
|
+
options,
|
|
125
|
+
)) as ListMemoriesResult;
|
|
126
|
+
expect(first.memories.map((memory) => memory.id)).toEqual(
|
|
127
|
+
original.slice(0, 20).map((memory) => memory.id),
|
|
128
|
+
);
|
|
129
|
+
summaries = summaries.filter(
|
|
130
|
+
(memory) => memory.id !== first.nextCursor?.id,
|
|
131
|
+
);
|
|
132
|
+
const second = (await list(
|
|
133
|
+
{
|
|
134
|
+
cursor: first.nextCursor ?? undefined,
|
|
135
|
+
toolCallDescription: "Continue after anchor deletion",
|
|
136
|
+
},
|
|
137
|
+
options,
|
|
138
|
+
)) as ListMemoriesResult;
|
|
139
|
+
expect(second.memories.map((memory) => memory.id)).toEqual(
|
|
140
|
+
original.slice(20).map((memory) => memory.id),
|
|
141
|
+
);
|
|
142
|
+
expect(second.nextCursor).toBeNull();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("bounds each result while keeping every older memory discoverable", async () => {
|
|
147
|
+
const summaries: MemorySummary[] = Array.from({ length: 200 }, (_, i) => ({
|
|
148
|
+
id: `memory-${i}`,
|
|
149
|
+
category: "general",
|
|
150
|
+
title: `Memory ${i} ${"x".repeat(480)}`,
|
|
151
|
+
tags: ["synthetic", "history"],
|
|
152
|
+
createdAt: new Date(Date.UTC(2026, 0, 1) - i * 1_000).toISOString(),
|
|
153
|
+
}));
|
|
154
|
+
const backend: MemoryBackend = {
|
|
155
|
+
add: vi.fn(async () => {
|
|
156
|
+
throw new Error("unexpected add");
|
|
157
|
+
}),
|
|
158
|
+
list: vi.fn(async () =>
|
|
159
|
+
summaries.map((summary, index) =>
|
|
160
|
+
index === 0 ? { ...summary, content: "full-body-marker" } : summary,
|
|
161
|
+
),
|
|
162
|
+
),
|
|
163
|
+
get: vi.fn(async (_category, id) => {
|
|
164
|
+
const summary = summaries.find((item) => item.id === id);
|
|
165
|
+
return summary
|
|
166
|
+
? ({
|
|
167
|
+
...summary,
|
|
168
|
+
content: `Full ${id}`,
|
|
169
|
+
updatedAt: summary.createdAt,
|
|
170
|
+
} satisfies Memory)
|
|
171
|
+
: null;
|
|
172
|
+
}),
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
await withMemoryBackend(backend, async () => {
|
|
176
|
+
const allIds: string[] = [];
|
|
177
|
+
let cursor: ListMemoriesResult["nextCursor"] = null;
|
|
178
|
+
let firstResultChars = 0;
|
|
179
|
+
let pages = 0;
|
|
180
|
+
for (;;) {
|
|
181
|
+
const result = (await list(
|
|
182
|
+
{
|
|
183
|
+
cursor: cursor ?? undefined,
|
|
184
|
+
toolCallDescription: "List synthetic memories",
|
|
185
|
+
},
|
|
186
|
+
options,
|
|
187
|
+
)) as ListMemoriesResult;
|
|
188
|
+
expect(result.success).toBe(true);
|
|
189
|
+
expect(result.count).toBe(200);
|
|
190
|
+
expect(result.memories.length).toBeGreaterThan(0);
|
|
191
|
+
expect(result.memories.length).toBeLessThanOrEqual(20);
|
|
192
|
+
expect(JSON.stringify(result).length).toBeLessThan(12_500);
|
|
193
|
+
if (pages === 0) {
|
|
194
|
+
firstResultChars = JSON.stringify(result).length;
|
|
195
|
+
expect(JSON.stringify(result)).not.toContain("full-body-marker");
|
|
196
|
+
expect(result.memories[0]?.truncated).toBe(true);
|
|
197
|
+
expect(result.memories[0]?.title.length).toBe(160);
|
|
198
|
+
}
|
|
199
|
+
allIds.push(...result.memories.map((memory) => memory.id));
|
|
200
|
+
pages++;
|
|
201
|
+
if (result.nextCursor === null) break;
|
|
202
|
+
expect(result.nextCursor.id).toBe(result.memories.at(-1)?.id);
|
|
203
|
+
cursor = result.nextCursor;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
expect(allIds).toEqual(summaries.map((memory) => memory.id));
|
|
207
|
+
const oldest = (await get(
|
|
208
|
+
{
|
|
209
|
+
category: "general",
|
|
210
|
+
id: allIds.at(-1) ?? "",
|
|
211
|
+
toolCallDescription: "Read oldest synthetic memory",
|
|
212
|
+
},
|
|
213
|
+
options,
|
|
214
|
+
)) as GetMemoryResult;
|
|
215
|
+
expect(oldest.memory?.content).toBe("Full memory-199");
|
|
216
|
+
if (process.env.APEX_CONTEXT_BENCH === "1") {
|
|
217
|
+
const previousChars = JSON.stringify({
|
|
218
|
+
success: true,
|
|
219
|
+
error: "",
|
|
220
|
+
memories: summaries,
|
|
221
|
+
count: summaries.length,
|
|
222
|
+
}).length;
|
|
223
|
+
console.log(
|
|
224
|
+
"MEMORY_LIST_BENCH",
|
|
225
|
+
JSON.stringify({ previousChars, firstResultChars, pages }),
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|
|
@@ -7,6 +7,9 @@ import {
|
|
|
7
7
|
} from "../../../memory";
|
|
8
8
|
import type { ToolContext } from "./types";
|
|
9
9
|
|
|
10
|
+
const PAGE_SIZE = 20;
|
|
11
|
+
const PAGE_CHAR_BUDGET = 12_000;
|
|
12
|
+
|
|
10
13
|
const listMemoriesInputSchema = z.object({
|
|
11
14
|
category: z
|
|
12
15
|
.enum(MEMORY_CATEGORIES)
|
|
@@ -19,6 +22,14 @@ const listMemoriesInputSchema = z.object({
|
|
|
19
22
|
.string()
|
|
20
23
|
.optional()
|
|
21
24
|
.describe("Optional tag to filter memories by (exact match)"),
|
|
25
|
+
cursor: z
|
|
26
|
+
.object({
|
|
27
|
+
createdAt: z.string(),
|
|
28
|
+
category: z.enum(MEMORY_CATEGORIES),
|
|
29
|
+
id: z.string(),
|
|
30
|
+
})
|
|
31
|
+
.optional()
|
|
32
|
+
.describe("Pass the previous nextCursor to continue a listing"),
|
|
22
33
|
toolCallDescription: z
|
|
23
34
|
.string()
|
|
24
35
|
.describe(
|
|
@@ -27,24 +38,53 @@ const listMemoriesInputSchema = z.object({
|
|
|
27
38
|
});
|
|
28
39
|
|
|
29
40
|
type ListMemoriesInput = z.infer<typeof listMemoriesInputSchema>;
|
|
41
|
+
type MemoryCursor = Pick<MemorySummary, "createdAt" | "category" | "id">;
|
|
30
42
|
|
|
31
43
|
export type ListMemoriesResult = {
|
|
32
44
|
success: boolean;
|
|
33
45
|
error: string;
|
|
34
|
-
memories: MemorySummary
|
|
46
|
+
memories: Array<MemorySummary & { truncated?: true }>;
|
|
35
47
|
count: number;
|
|
48
|
+
nextCursor: MemoryCursor | null;
|
|
36
49
|
};
|
|
37
50
|
|
|
51
|
+
function compareMemories(a: MemoryCursor, b: MemoryCursor): number {
|
|
52
|
+
const byCreatedAt = Date.parse(b.createdAt) - Date.parse(a.createdAt);
|
|
53
|
+
if (byCreatedAt > 0 || byCreatedAt < 0) return byCreatedAt;
|
|
54
|
+
return a.category.localeCompare(b.category) || a.id.localeCompare(b.id);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function previewSummary(memory: MemorySummary) {
|
|
58
|
+
const title = memory.title.slice(0, 160);
|
|
59
|
+
const tags = memory.tags.slice(0, 8).map((tag) => tag.slice(0, 48));
|
|
60
|
+
const truncated =
|
|
61
|
+
title.length < memory.title.length ||
|
|
62
|
+
tags.length < memory.tags.length ||
|
|
63
|
+
tags.some(
|
|
64
|
+
(tag, index) => tag.length < (memory.tags[index]?.length ?? tag.length),
|
|
65
|
+
);
|
|
66
|
+
return {
|
|
67
|
+
id: memory.id,
|
|
68
|
+
category: memory.category,
|
|
69
|
+
title,
|
|
70
|
+
tags,
|
|
71
|
+
createdAt: memory.createdAt,
|
|
72
|
+
...(truncated ? { truncated: true as const } : {}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
38
76
|
export function listMemories(ctx: ToolContext) {
|
|
39
77
|
return tool({
|
|
40
78
|
description: `List saved memories, optionally filtered by category and/or tag.
|
|
41
79
|
|
|
42
|
-
Returns lightweight summaries (id, category, title, tags, createdAt)
|
|
43
|
-
most recent first. Use
|
|
44
|
-
|
|
80
|
+
Returns up to 20 lightweight summaries (id, category, title, tags, createdAt)
|
|
81
|
+
sorted by most recent first. Use nextCursor to continue until it is null;
|
|
82
|
+
count is the current total matching memories. A fresh listing includes newer
|
|
83
|
+
memories added ahead of a cursor. Titles and tags may be abbreviated in the
|
|
84
|
+
listing. Use category + id with get_memory to retrieve full content.`,
|
|
45
85
|
inputSchema: listMemoriesInputSchema,
|
|
46
86
|
execute: async (
|
|
47
|
-
{ category, tag },
|
|
87
|
+
{ category, tag, cursor },
|
|
48
88
|
{ toolCallId },
|
|
49
89
|
): Promise<ListMemoriesResult> => {
|
|
50
90
|
try {
|
|
@@ -52,11 +92,37 @@ full content.`,
|
|
|
52
92
|
{ category, tag },
|
|
53
93
|
{ sessionId: ctx.session.id, toolCallId },
|
|
54
94
|
);
|
|
95
|
+
const ordered = [...memories].sort(compareMemories);
|
|
96
|
+
let start = 0;
|
|
97
|
+
if (cursor) {
|
|
98
|
+
start = ordered.findIndex(
|
|
99
|
+
(memory) => compareMemories(memory, cursor) > 0,
|
|
100
|
+
);
|
|
101
|
+
if (start < 0) start = ordered.length;
|
|
102
|
+
}
|
|
103
|
+
const page: ListMemoriesResult["memories"] = [];
|
|
104
|
+
let pageChars = 0;
|
|
105
|
+
for (const memory of ordered.slice(start, start + PAGE_SIZE)) {
|
|
106
|
+
const summary = previewSummary(memory);
|
|
107
|
+
const chars = JSON.stringify(summary).length + 1;
|
|
108
|
+
if (page.length > 0 && pageChars + chars > PAGE_CHAR_BUDGET) break;
|
|
109
|
+
page.push(summary);
|
|
110
|
+
pageChars += chars;
|
|
111
|
+
}
|
|
112
|
+
const last = page.at(-1);
|
|
55
113
|
return {
|
|
56
114
|
success: true,
|
|
57
115
|
error: "",
|
|
58
|
-
memories,
|
|
116
|
+
memories: page,
|
|
59
117
|
count: memories.length,
|
|
118
|
+
nextCursor:
|
|
119
|
+
start + page.length < ordered.length && last
|
|
120
|
+
? {
|
|
121
|
+
createdAt: last.createdAt,
|
|
122
|
+
category: last.category,
|
|
123
|
+
id: last.id,
|
|
124
|
+
}
|
|
125
|
+
: null,
|
|
60
126
|
};
|
|
61
127
|
} catch (err: unknown) {
|
|
62
128
|
return {
|
|
@@ -64,6 +130,7 @@ full content.`,
|
|
|
64
130
|
error: err instanceof Error ? err.message : String(err),
|
|
65
131
|
memories: [],
|
|
66
132
|
count: 0,
|
|
133
|
+
nextCursor: null,
|
|
67
134
|
};
|
|
68
135
|
}
|
|
69
136
|
},
|
|
@@ -11,6 +11,12 @@ describe("report_error tool", () => {
|
|
|
11
11
|
expect(REPORT_ERROR_TOOL_NAME).toBe("report_error");
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
+
it("does not name the unavailable finding tool under external grading", () => {
|
|
15
|
+
const external = createReportErrorTool(vi.fn(), "external");
|
|
16
|
+
expect(external.description).toContain("final response");
|
|
17
|
+
expect(external.description).not.toContain("document_vulnerability");
|
|
18
|
+
});
|
|
19
|
+
|
|
14
20
|
it("forwards the reason + message to the onError callback", async () => {
|
|
15
21
|
const onError = vi.fn<(error: ReportedError) => void>();
|
|
16
22
|
const tool = createReportErrorTool(onError);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { tool } from "ai";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import type { FindingPolicy } from "../types";
|
|
3
4
|
|
|
4
5
|
export const REPORT_ERROR_TOOL_NAME = "report_error" as const;
|
|
5
6
|
|
|
@@ -22,7 +23,14 @@ export type ReportedError = {
|
|
|
22
23
|
* of a successful result it surfaces a blocking error so the caller can turn
|
|
23
24
|
* the run into a failure (e.g. mark a scan endpoint failed with this message).
|
|
24
25
|
*/
|
|
25
|
-
export function createReportErrorTool(
|
|
26
|
+
export function createReportErrorTool(
|
|
27
|
+
onError: (error: ReportedError) => void,
|
|
28
|
+
findingPolicy: FindingPolicy = "native",
|
|
29
|
+
) {
|
|
30
|
+
const vulnerabilityGuidance =
|
|
31
|
+
findingPolicy === "external"
|
|
32
|
+
? "Do NOT use this for vulnerabilities (include them in your final response)"
|
|
33
|
+
: "Do NOT use this for vulnerabilities (use document_vulnerability)";
|
|
26
34
|
return tool({
|
|
27
35
|
description: `Report a blocking error that prevents you from completing the pentest, and END your run.
|
|
28
36
|
|
|
@@ -31,7 +39,7 @@ Call this ONLY when you genuinely cannot continue, for example:
|
|
|
31
39
|
- The target is unreachable or responds in a way that makes testing impossible.
|
|
32
40
|
- Some other runtime condition blocks you from carrying out the objectives.
|
|
33
41
|
|
|
34
|
-
|
|
42
|
+
${vulnerabilityGuidance} or for normal completion (use response). Provide a clear, specific message describing exactly what blocked you — include the failing URL, status codes, or the auth step that failed where relevant — so it can be surfaced to the operator.`,
|
|
35
43
|
inputSchema: z.object({
|
|
36
44
|
reason: ReportErrorReasonSchema.describe(
|
|
37
45
|
"Coarse category of the blocking error.",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { tool } from "ai";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { getModelSettings } from "../../../ai";
|
|
3
4
|
import { createLogger } from "../../../logger/structured";
|
|
4
5
|
import { scopedLogger } from "../../../util/lazyLogger";
|
|
5
6
|
import type { AttackSurfaceResult } from "../../specialized/attackSurface/blackboxAgent";
|
|
@@ -74,13 +75,14 @@ should be passed directly to spawn_pentest_swarm for deep testing.`,
|
|
|
74
75
|
attackSurfaceRegistry: ctx.attackSurfaceRegistry,
|
|
75
76
|
},
|
|
76
77
|
runtime: {
|
|
78
|
+
findingPolicy: ctx.findingPolicy,
|
|
77
79
|
session: ctx.session,
|
|
78
|
-
|
|
79
|
-
authConfig: ctx.authConfig,
|
|
80
|
+
...getModelSettings(ctx),
|
|
80
81
|
abortSignal: ctx.abortSignal,
|
|
81
82
|
backends: ctx.backends,
|
|
82
83
|
languageModelMiddleware: ctx.languageModelMiddleware,
|
|
83
84
|
usageRecorder: ctx.usageRecorder,
|
|
85
|
+
toolExecutionContext: ctx.toolExecutionContext,
|
|
84
86
|
streamIdFactory: ctx.streamIdFactory,
|
|
85
87
|
},
|
|
86
88
|
parentBus: ctx.eventBus,
|
|
@@ -144,13 +146,14 @@ should be passed directly to spawn_pentest_swarm for deep testing.`,
|
|
|
144
146
|
attackSurfaceRegistry: ctx.attackSurfaceRegistry,
|
|
145
147
|
},
|
|
146
148
|
runtime: {
|
|
149
|
+
findingPolicy: ctx.findingPolicy,
|
|
147
150
|
session: ctx.session,
|
|
148
|
-
|
|
149
|
-
authConfig: ctx.authConfig,
|
|
151
|
+
...getModelSettings(ctx),
|
|
150
152
|
abortSignal: ctx.abortSignal,
|
|
151
153
|
backends: ctx.backends,
|
|
152
154
|
languageModelMiddleware: ctx.languageModelMiddleware,
|
|
153
155
|
usageRecorder: ctx.usageRecorder,
|
|
156
|
+
toolExecutionContext: ctx.toolExecutionContext,
|
|
154
157
|
streamIdFactory: ctx.streamIdFactory,
|
|
155
158
|
},
|
|
156
159
|
parentBus: ctx.eventBus,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
const workflow = vi.hoisted(() => vi.fn());
|
|
4
|
+
|
|
5
|
+
vi.mock("../../../workflows/pentest", () => ({
|
|
6
|
+
runPentestWorkflow: workflow,
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
import { runPentestWorkflow } from "./runPentestWorkflow";
|
|
10
|
+
import type { ToolContext } from "./types";
|
|
11
|
+
|
|
12
|
+
describe("run_pentest_workflow external finding handoff", () => {
|
|
13
|
+
it("returns reported findings to the parent instead of claiming zero were documented", async () => {
|
|
14
|
+
workflow.mockResolvedValue({
|
|
15
|
+
findings: [],
|
|
16
|
+
reportedFindings: ["Cross-tenant read at GET /private"],
|
|
17
|
+
objectiveResults: [
|
|
18
|
+
{
|
|
19
|
+
objective: "Extract the assigned flag",
|
|
20
|
+
completed: true,
|
|
21
|
+
result: "Extracted FLAG{example}",
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
findingsPath: "/session/findings",
|
|
25
|
+
pocsPath: "/session/pocs",
|
|
26
|
+
reportPath: null,
|
|
27
|
+
});
|
|
28
|
+
const ctx = {
|
|
29
|
+
findingPolicy: "external",
|
|
30
|
+
model: "test",
|
|
31
|
+
session: { id: "ses_external", rootPath: "/session" },
|
|
32
|
+
} as ToolContext;
|
|
33
|
+
const agentTool = runPentestWorkflow(ctx);
|
|
34
|
+
expect(agentTool.description).toContain("caller's evaluation");
|
|
35
|
+
const result = await agentTool.execute?.(
|
|
36
|
+
{ target: "https://example.com", toolCallDescription: "test" },
|
|
37
|
+
{ toolCallId: "tc_external", messages: [] },
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
expect(workflow).toHaveBeenCalledWith(
|
|
41
|
+
expect.objectContaining({ findingPolicy: "external" }),
|
|
42
|
+
);
|
|
43
|
+
expect(result).toMatchObject({
|
|
44
|
+
success: true,
|
|
45
|
+
totalFindings: 1,
|
|
46
|
+
reportedFindings: ["Cross-tenant read at GET /private"],
|
|
47
|
+
objectiveResults: [
|
|
48
|
+
{
|
|
49
|
+
objective: "Extract the assigned flag",
|
|
50
|
+
completed: true,
|
|
51
|
+
result: "Extracted FLAG{example}",
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
});
|
|
55
|
+
expect((result as { message: string }).message).not.toContain("documented");
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -11,13 +11,17 @@ import type { ToolContext } from "./types";
|
|
|
11
11
|
* need to orchestrate multiple tool calls manually.
|
|
12
12
|
*/
|
|
13
13
|
export function runPentestWorkflow(ctx: ToolContext) {
|
|
14
|
+
const resultStep =
|
|
15
|
+
ctx.findingPolicy === "external"
|
|
16
|
+
? "3. Return evidence-backed findings for the caller's evaluation"
|
|
17
|
+
: "3. Aggregate findings and generate the final report";
|
|
14
18
|
return tool({
|
|
15
19
|
description: `Run the full autonomous penetration test pipeline against a target.
|
|
16
20
|
|
|
17
21
|
This single tool call executes the entire pentest workflow:
|
|
18
22
|
1. Attack surface discovery (blackbox or whitebox)
|
|
19
23
|
2. Spawn a swarm of targeted pentest agents
|
|
20
|
-
|
|
24
|
+
${resultStep}
|
|
21
25
|
|
|
22
26
|
Use this when you want to run a complete, end-to-end pentest. The workflow
|
|
23
27
|
handles session creation, concurrency, persistence, and reporting automatically.
|
|
@@ -54,6 +58,7 @@ Omit \`cwd\` for blackbox mode (live target probing only).`,
|
|
|
54
58
|
|
|
55
59
|
try {
|
|
56
60
|
const result = await workflow({
|
|
61
|
+
findingPolicy: ctx.findingPolicy,
|
|
57
62
|
target,
|
|
58
63
|
cwd,
|
|
59
64
|
model: ctx.model,
|
|
@@ -64,12 +69,15 @@ Omit \`cwd\` for blackbox mode (live target probing only).`,
|
|
|
64
69
|
enableThinking: ctx.enableThinking,
|
|
65
70
|
thinkingEffort: ctx.thinkingEffort,
|
|
66
71
|
openAIReasoningEffort: ctx.openAIReasoningEffort,
|
|
72
|
+
reasoning: ctx.reasoning,
|
|
67
73
|
surfaceIntegrationEnabled: ctx.surfaceIntegrationEnabled,
|
|
68
74
|
hooks: {
|
|
75
|
+
findingPolicy: ctx.findingPolicy,
|
|
69
76
|
backends: ctx.backends,
|
|
70
77
|
subagentSpawner: ctx.subagentSpawner,
|
|
71
78
|
languageModelMiddleware: ctx.languageModelMiddleware,
|
|
72
79
|
usageRecorder: ctx.usageRecorder,
|
|
80
|
+
toolExecutionContext: ctx.toolExecutionContext,
|
|
73
81
|
streamIdFactory: ctx.streamIdFactory,
|
|
74
82
|
smsInbox: ctx.smsInbox,
|
|
75
83
|
emailAdapterFor: ctx.emailAdapterFor,
|
|
@@ -81,6 +89,18 @@ Omit \`cwd\` for blackbox mode (live target probing only).`,
|
|
|
81
89
|
const minutes = Math.floor(elapsedSeconds / 60);
|
|
82
90
|
const seconds = elapsedSeconds % 60;
|
|
83
91
|
|
|
92
|
+
if (ctx.findingPolicy === "external") {
|
|
93
|
+
const reportedFindings = result.reportedFindings ?? [];
|
|
94
|
+
return {
|
|
95
|
+
success: true,
|
|
96
|
+
totalFindings: reportedFindings.length,
|
|
97
|
+
reportedFindings,
|
|
98
|
+
objectiveResults: result.objectiveResults ?? [],
|
|
99
|
+
duration: `${minutes}m ${seconds}s`,
|
|
100
|
+
message: `Pentest complete. ${reportedFindings.length} finding(s) returned for external evaluation.`,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
84
104
|
return {
|
|
85
105
|
success: true,
|
|
86
106
|
totalFindings: result.findings.length,
|
|
@@ -22,13 +22,16 @@ const ScanKindSchema = z.enum([
|
|
|
22
22
|
]);
|
|
23
23
|
|
|
24
24
|
export function runWhiteboxScan(ctx: ToolContext) {
|
|
25
|
+
const verificationGuidance =
|
|
26
|
+
ctx.findingPolicy === "external"
|
|
27
|
+
? "as recoverable conditions. Static results are candidates only; assess them against the caller's evidence requirements."
|
|
28
|
+
: "as recoverable conditions. Static results are candidates only; verify before\ncalling document_vulnerability.";
|
|
25
29
|
return tool({
|
|
26
30
|
description: `Run installed whitebox security scanners with bounded output.
|
|
27
31
|
|
|
28
32
|
The tool detects applicable scanner adapters for the repository and normalizes
|
|
29
33
|
results into concise summaries plus artifact paths. Missing tools are reported
|
|
30
|
-
|
|
31
|
-
calling document_vulnerability.`,
|
|
34
|
+
${verificationGuidance}`,
|
|
32
35
|
inputSchema: z.object({
|
|
33
36
|
path: z
|
|
34
37
|
.string()
|