When to Use
- Adding AI decision-making to an existing recon tool (tag selection, extension guessing, WAF classification, etc.).
For adding a whole new recon tool, use recon-tool-integration. For the setting
that toggles it, use project-settings-cascade.
Critical Rules
- NEVER let the AI helper raise. Every failure path returns the user's
current value. Recon stdout tails into the webapp's SSE recon drawer, so an
exception both breaks the scan and blanks the stream. Pattern:
recon/helpers/ai_planner/nuclei_tags.py:94
("Never raises -- returns
current_tagson any failure"). - NEVER fall back to an empty list/string. For tools where empty means "skip
the work" (nuclei tags, ffuf extensions) that silently turns detection off.
Fall back to the user's current value, not
[]/"". - NEVER call the LLM with no signal. Empty fingerprint -> return the current value; do not send an empty prompt.
- NEVER hook the AI separately in partial recon. Most tools share one entry
function (e.g.
run_vuln_scanis called by bothmain_recon_modules/andpartial_recon_modules/); hook it once and both paths inherit.grepthe function name to confirm before you edit. The feature must work in the full pipeline AND partial recon. - NEVER touch webapp/src/lib/recon-presets/presets/:
the
aiInPipelinecascade (apply_ai_pipeline_overrides, recon/project_settings.py:1968) is the single source of truth for per-tool AI flags. Presets must not hard-code them; update the Zod schema instead. - ALWAYS add a registry entry for the new
Projectcolumn{tool}Ai{Feature}in recon_settings/registry.yaml, besideffufAiExtensions,nucleiAiTags,nucleiAiResponseFilterandwafAiClassifier. A test walkingPrisma.ProjectScalarFieldEnumfails until every column has one. An AI hook is an ordinary boolean toggle:mcp: settable,traffic: none(the hook itself sends no traffic; the tool it advises does), and ameaningthat says which decision it moves from the operator to the model. - ALWAYS cache a per-target hook keyed by tech fingerprint (Server, X-Powered-By, ...) so N targets behind one stack collapse to one LLM call (ffuf_extensions.py). A per-scan hook (nuclei_tags.py) runs once and needs no cache.
- ALWAYS put the toggle in two places bound to the same field
data.{tool}Ai{Feature}: the master AI-in-Pipeline panel (TargetSection.tsx:362) and the tool's own section (e.g. NucleiSection.tsx). Read AND write the same field; no copy-on-flip (they stay in sync because they share the field).
The pieces
| Piece | File | Note |
|---|---|---|
| Helper | recon/helpers/ai_planner/{tool}_{feature}.py | POSTs to the agent; never raises; logs [*][{Tool}-AI] / [!][{Tool}-AI] to stdout |
| Agent endpoint | agentic/api.py (e.g. /llm/nuclei-tags at :641, /llm/ffuf-extensions at :543) | Pydantic model; returns 422 (bad body) / 503 (no key), never 500 |
| Setting | recon/project_settings.py DEFAULT_SETTINGS + fetch_project_settings + both branches of apply_ai_pipeline_overrides | see project-settings-cascade |
| Zod | webapp/src/lib/recon-preset-schema.ts | so AI-generated presets see the field |
| UI | TargetSection.tsx + the tool's section | two toggles, one field |
Commands
bashdocker compose build agent && docker compose up -d agent # the /llm endpoint lives in agentic/ (baked) # recon/*.py is volume-mounted at spawn - no rebuild # verify: a minimal POST returns 422/503, never 500; and a live scan logs # [*][{Tool}-AI] in BOTH a full run and a partial recon run; stop the agent -> scan still completes.
Resources
- docs/readmes/coding_agent_prompts/PROMPT.ADD_AI_IN_RECON.md - full walkthrough, per-target vs per-scan, verify steps
- Related skills:
recon-tool-integration,project-settings-cascade

