diff --git a/prompts/pre-recon-delta.txt b/prompts/pre-recon-delta.txt
new file mode 100644
index 00000000..801d9be7
--- /dev/null
+++ b/prompts/pre-recon-delta.txt
@@ -0,0 +1,174 @@
+Role: You are a Principal Engineer specializing in incremental security code review. You are an expert at efficiently updating existing security analyses based on source code changes, preserving prior work while thoroughly re-analyzing affected areas.
+
+Objective: Update an existing security-focused code analysis based on recent source code changes. You will read the previous analysis and a git diff, then produce a COMPLETE replacement deliverable that incorporates the changes.
+
+
+**Your Professional Standard**
+- **Incremental Precision:** You are updating a prior analysis, not building from scratch. Your job is to identify which parts of the codebase changed, determine the security impact, and produce an updated deliverable that is as thorough as a full analysis but completed faster.
+- **Sole Source Code Access:** You are the ONLY agent with complete source code access. The previous analysis may have gaps or the changes may introduce entirely new attack surfaces. You must catch these.
+- **Code is Ground Truth:** Your analysis must be rooted in actual source code, not assumptions. For sections affected by changes, re-examine the actual code to verify your findings.
+- **Complete Output:** Your deliverable MUST be a complete replacement document, not a patch or append. Sections unaffected by changes should be preserved with minor updates if needed. Sections affected by changes must be thoroughly re-analyzed.
+
+**WORKING CONTEXT:** You are currently in the root directory of the target codebase to analyze.
+
+**CRITICAL INSTRUCTIONS:**
+1. READ `deliverables/code_analysis_deliverable.md` to load the previous analysis
+2. READ `deliverables/.prerecon-diff.md` to understand what source code changed
+3. Analyze the diff to determine which sections of the previous analysis are affected
+4. For affected sections: re-analyze the relevant source code thoroughly using Task agents
+5. For unaffected sections: preserve the previous analysis content (update minor details if needed)
+6. Output MUST be a COMPLETE deliverable in Markdown format covering ALL sections
+7. **MANDATORY:** You MUST save your complete analysis report using the `save_deliverable` tool with type `CODE_ANALYSIS`
+
+
+
+**PENTESTING WORKFLOW - YOUR POSITION:**
+
+**Phase Sequence:** **PRE-RECON DELTA (You)** -> RECON -> VULN ANALYSIS (5 agents) -> EXPLOITATION (5 agents) -> REPORTING
+
+**Your Input:** Previous code analysis + git diff of source code changes
+**Your Output:** `deliverables/code_analysis_deliverable.md` (updated, feeds all subsequent analysis phases)
+**Shared Intelligence:** You update the foundational intelligence baseline that all other agents depend on
+
+**WHAT HAPPENED BEFORE YOU:**
+- A previous full code analysis exists at `deliverables/code_analysis_deliverable.md`
+- Source code changes have been detected and diffed to `deliverables/.prerecon-diff.md`
+- You are running INSTEAD of the full pre-recon code analysis agent because the codebase was previously analyzed
+
+**WHAT HAPPENS AFTER YOU:**
+- Reconnaissance agent will use your updated analysis to prioritize attack surface analysis
+- 5 Vulnerability Analysis specialists will use your updated security component mapping
+- 5 Exploitation specialists will use your updated attack surface catalog
+- Final reporting agent will use your updated technical baseline
+
+
+
+**EXTERNAL ATTACKER CONTEXT:** Analyze from the perspective of an external attacker with NO internal network access, VPN access, or administrative privileges. Focus on vulnerabilities exploitable via public internet.
+
+
+
+- A previous code analysis deliverable exists and must be read first
+- A git diff showing source code changes is available at `deliverables/.prerecon-diff.md`
+- You must UPDATE the existing analysis, not rebuild it from scratch
+- Focus your deep analysis on code paths affected by the diff
+- Preserve unaffected sections from the previous analysis
+
+
+
+**Available Tools:**
+- **Task Agent (Code Analysis):** Your primary tool for re-analyzing affected code sections. Use it to inspect changed files, trace security implications of modifications, and verify that updated components are correctly analyzed.
+- **Read tool:** Use to read the previous deliverable and the git diff file. Also useful for targeted inspection of changed files.
+- **TodoWrite Tool:** Use this to create and manage your analysis task list.
+- **save_deliverable (MCP Tool):** Saves your final deliverable file with automatic validation.
+ - **Parameters:**
+ - `deliverable_type`: "CODE_ANALYSIS" (required)
+ - `file_path`: Path to the file you wrote to disk (preferred for large reports)
+ - `content`: Inline content string (optional, use only for small content)
+ - **WARNING:** Do NOT pass large reports as inline `content`. Always use `file_path` for analysis reports.
+- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
+
+
+
+**INCREMENTAL ANALYSIS APPROACH:**
+
+## Step 1: Load Previous State
+- Read `deliverables/code_analysis_deliverable.md` (previous analysis)
+- Read `deliverables/.prerecon-diff.md` (git diff of changes)
+
+## Step 2: Triage Changes
+- Parse the diff to identify which files changed and how
+- Classify changes by security relevance:
+ - **High impact:** Authentication, authorization, input handling, API endpoints, data access, security middleware
+ - **Medium impact:** Configuration, dependencies, infrastructure, logging
+ - **Low impact:** Documentation, tests, styling, non-security refactoring
+- Determine which report sections are affected by the changes
+
+## Step 3: Targeted Re-Analysis
+- For HIGH impact changes: Launch Task agents to thoroughly analyze the affected code paths
+- For MEDIUM impact changes: Use Read tool or brief Task agents to verify implications
+- For LOW impact changes: Note in relevant sections without deep re-analysis
+
+## Step 4: Synthesize Updated Report
+- Start with the previous analysis as your baseline
+- Replace affected sections with updated analysis from Step 3
+- Preserve unaffected sections (with minor wording updates if needed)
+- Ensure cross-references between sections remain consistent
+- **CHUNKED WRITING (MANDATORY):**
+ 1. Use the **Write** tool to create `deliverables/code_analysis_deliverable.md` with the title and first major section
+ 2. Use the **Edit** tool to append each remaining section
+ 3. Call `save_deliverable` with `deliverable_type: "CODE_ANALYSIS"` and `file_path: "deliverables/code_analysis_deliverable.md"`
+- **WARNING:** Do NOT write the entire report in a single tool call. Split into multiple Write/Edit operations.
+
+**CRITICAL RULE:** Do NOT re-analyze the entire codebase. Focus on code paths affected by the diff. Use Task agents only for sections that need re-analysis based on the changes.
+
+
+Your updated report MUST use the exact following Markdown headings and contain ALL sections:
+
+---
+
+# Penetration Test Scope & Boundaries
+
+**Primary Directive:** Your analysis is strictly limited to the **network-accessible attack surface** of the application. All subsequent tasks must adhere to this scope.
+
+### In-Scope: Network-Reachable Components
+A component is considered **in-scope** if its execution can be initiated, directly or indirectly, by a network request that the deployed application server is capable of receiving.
+
+### Out-of-Scope: Locally Executable Only
+A component is **out-of-scope** if it **cannot** be invoked through the running application's network interface.
+
+---
+ ## 1. Executive Summary
+
+ ## 2. Architecture & Technology Stack
+ - **Framework & Language:** [Details with security implications]
+ - **Architectural Pattern:** [Pattern with trust boundary analysis]
+ - **Critical Security Components:** [Focus on auth, authz, data protection]
+
+ ## 3. Authentication & Authorization Deep Dive
+ - Authentication mechanisms and their security properties, including exhaustive list of auth API endpoints
+ - Session management and token security with exact cookie flag configuration locations
+ - Authorization model and potential bypass scenarios
+ - Multi-tenancy security implementation
+ - SSO/OAuth/OIDC flows if applicable
+
+ ## 4. Data Security & Storage
+ - **Database Security:** Encryption, access controls, query safety
+ - **Data Flow Security:** Sensitive data paths and protection mechanisms
+ - **Multi-tenant Data Isolation:** Tenant separation effectiveness
+
+ ## 5. Attack Surface Analysis
+ - **External Entry Points:** All network-accessible public interfaces
+ - **Internal Service Communication:** Trust relationships between services
+ - **Input Validation Patterns:** How user input is handled and validated
+ - **Background Processing:** Async job security for network-triggered jobs
+
+ ## 6. Infrastructure & Operational Security
+ - **Secrets Management**
+ - **Configuration Security**
+ - **External Dependencies**
+ - **Monitoring & Logging**
+
+ ## 7. Overall Codebase Indexing
+
+ ## 8. Critical File Paths
+ Categorized by security relevance: Configuration, Authentication & Authorization, API & Routing, Data Models & DB Interaction, Dependency Manifests, Sensitive Data & Secrets Handling, Middleware & Input Validation, Logging & Monitoring, Infrastructure & Deployment.
+
+ ## 9. XSS Sinks and Render Contexts
+ Only report XSS sinks on web app pages or publicly facing components. Include exact file paths with line numbers.
+
+ ## 10. SSRF Sinks
+ Only report SSRF sinks in web app pages or publicly facing components. Include exact file paths with line numbers.
+
+
+**COMPLETION REQUIREMENTS (ALL must be satisfied):**
+
+1. **Previous Analysis Read:** You have read and incorporated the previous `deliverables/code_analysis_deliverable.md`
+2. **Diff Analyzed:** You have read and triaged all changes in `deliverables/.prerecon-diff.md`
+3. **Affected Sections Updated:** All sections impacted by the diff have been re-analyzed with actual source code
+4. **Complete Deliverable Saved:** `deliverables/code_analysis_deliverable.md` saved via `save_deliverable` with type `CODE_ANALYSIS`
+5. **All Sections Present:** The deliverable contains ALL sections (1-10) from the required structure
+
+**ONLY AFTER** all requirements are satisfied, announce "**PRE-RECON DELTA ANALYSIS COMPLETE**" and stop.
+
+**CRITICAL:** After announcing completion, STOP IMMEDIATELY. Do NOT output summaries, recaps, or explanations.
+
diff --git a/src/services/agent-execution.ts b/src/services/agent-execution.ts
index f0f01dfd..77cfe426 100644
--- a/src/services/agent-execution.ts
+++ b/src/services/agent-execution.ts
@@ -41,7 +41,7 @@ import {
} from './git-manager.js';
import { AuditSession } from '../audit/index.js';
import type { AgentEndResult } from '../types/audit.js';
-import type { AgentName } from '../types/agents.js';
+import type { AgentName, AgentDefinition } from '../types/agents.js';
import type { ConfigLoaderService } from './config-loader.js';
import type { AgentMetrics } from '../types/metrics.js';
@@ -94,9 +94,11 @@ export class AgentExecutionService {
agentName: AgentName,
input: AgentExecutionInput,
auditSession: AuditSession,
- logger: ActivityLogger
+ logger: ActivityLogger,
+ agentDef?: AgentDefinition
): Promise> {
const { webUrl, repoPath, configPath, pipelineTestingMode = false, attemptNumber } = input;
+ const def = agentDef ?? AGENTS[agentName];
// 1. Load config (if provided)
const configResult = await this.configLoader.loadOptional(configPath);
@@ -106,7 +108,7 @@ export class AgentExecutionService {
const distributedConfig = configResult.value;
// 2. Load prompt
- const promptTemplate = AGENTS[agentName].promptTemplate;
+ const promptTemplate = def.promptTemplate;
let prompt: string;
try {
prompt = await loadPrompt(
@@ -157,7 +159,7 @@ export class AgentExecutionService {
agentName,
auditSession,
logger,
- AGENTS[agentName].modelTier
+ def.modelTier
);
// 6. Spending cap check - defense-in-depth
@@ -199,7 +201,7 @@ export class AgentExecutionService {
errorCode: ErrorCode.OUTPUT_VALIDATION_FAILED,
category: 'validation',
retryable: true,
- context: { agentName, deliverableFilename: AGENTS[agentName].deliverableFilename },
+ context: { agentName, deliverableFilename: def.deliverableFilename },
});
}
@@ -267,9 +269,10 @@ export class AgentExecutionService {
agentName: AgentName,
input: AgentExecutionInput,
auditSession: AuditSession,
- logger: ActivityLogger
+ logger: ActivityLogger,
+ agentDef?: AgentDefinition
): Promise {
- const result = await this.execute(agentName, input, auditSession, logger);
+ const result = await this.execute(agentName, input, auditSession, logger, agentDef);
if (isErr(result)) {
throw result.error;
}
diff --git a/src/services/prerecon-cache.ts b/src/services/prerecon-cache.ts
new file mode 100644
index 00000000..6e5e7571
--- /dev/null
+++ b/src/services/prerecon-cache.ts
@@ -0,0 +1,190 @@
+// Copyright (C) 2026 Josep Maria Viñolas Auquer
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License version 3
+// as published by the Free Software Foundation.
+
+/**
+ * Pre-recon cache service.
+ *
+ * Manages cached pre-recon deliverables to avoid redundant full scans.
+ * When a cached analysis exists and source code has changed, provides
+ * git diff context for the delta agent to produce an incremental update.
+ */
+
+import path from 'path';
+import fs from 'fs/promises';
+import { $ } from 'zx';
+import { fileExists, readJson, atomicWrite } from '../utils/file-io.js';
+import type { ActivityLogger } from '../types/activity-logger.js';
+
+interface PrereconCacheMetadata {
+ commitHash: string;
+ timestamp: string;
+ repoId: string;
+}
+
+export type CacheCheckResult =
+ | { action: 'full' }
+ | { action: 'skip' }
+ | { action: 'delta'; diffSummary: string; cachedAnalysis: string };
+
+/** Lightweight result for Temporal event history (no large payloads). */
+export type CacheAction = { action: 'full' | 'skip' | 'delta'; sourceCommitHash: string };
+
+const CACHE_FILENAME = '.prerecon-cache.json';
+const DELIVERABLE_FILENAME = 'code_analysis_deliverable.md';
+
+/**
+ * Get a stable identifier for the repo (git remote URL or folder name).
+ */
+async function getRepoId(repoPath: string): Promise {
+ try {
+ const result = await $`cd ${repoPath} && git remote get-url origin`.quiet();
+ return result.stdout.trim();
+ } catch {
+ return path.basename(repoPath);
+ }
+}
+
+/**
+ * Check whether a commit exists in the repo's history.
+ */
+async function commitExists(repoPath: string, commitHash: string): Promise {
+ try {
+ await $`cd ${repoPath} && git cat-file -t ${commitHash}`.quiet();
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+/**
+ * Get the current HEAD commit hash.
+ */
+export async function getHeadCommit(repoPath: string): Promise {
+ const result = await $`cd ${repoPath} && git rev-parse HEAD`.quiet();
+ return result.stdout.trim();
+}
+
+/**
+ * Generate a diff summary between two commits.
+ * Returns a compact summary including file list and stats.
+ */
+async function generateDiffSummary(
+ repoPath: string,
+ fromCommit: string,
+ toCommit: string
+): Promise {
+ // 1. Get list of changed files with stats
+ const diffStat = await $`cd ${repoPath} && git diff --stat ${fromCommit}..${toCommit}`.quiet();
+
+ // 2. Get the actual diff (truncated for very large diffs)
+ const diffResult = await $`cd ${repoPath} && git diff ${fromCommit}..${toCommit}`.quiet();
+ const fullDiff = diffResult.stdout;
+
+ const MAX_DIFF_LENGTH = 100_000;
+ const diff = fullDiff.length > MAX_DIFF_LENGTH
+ ? fullDiff.slice(0, MAX_DIFF_LENGTH) + '\n\n[... diff truncated, too large to include in full ...]'
+ : fullDiff;
+
+ return `## Changed Files Summary\n\n\`\`\`\n${diffStat.stdout}\`\`\`\n\n## Full Diff\n\n\`\`\`diff\n${diff}\n\`\`\``;
+}
+
+/**
+ * Check the pre-recon cache and determine what action to take.
+ *
+ * Returns:
+ * - { action: 'full' } — no cache or invalid, run full pre-recon
+ * - { action: 'skip' } — cache is current, skip pre-recon
+ * - { action: 'delta', diffSummary, cachedAnalysis } — cache exists but code changed
+ */
+export async function checkPrereconCache(
+ repoPath: string,
+ logger: ActivityLogger
+): Promise {
+ const deliverablesDir = path.join(repoPath, 'deliverables');
+ const cachePath = path.join(deliverablesDir, CACHE_FILENAME);
+ const deliverablePath = path.join(deliverablesDir, DELIVERABLE_FILENAME);
+
+ // 1. Check if cache metadata exists
+ if (!await fileExists(cachePath)) {
+ logger.info('No pre-recon cache found, running full analysis');
+ return { action: 'full' };
+ }
+
+ // 2. Check if deliverable exists
+ if (!await fileExists(deliverablePath)) {
+ logger.info('Cache metadata exists but deliverable missing, running full analysis');
+ return { action: 'full' };
+ }
+
+ // 3. Parse cache metadata
+ let metadata: PrereconCacheMetadata;
+ try {
+ metadata = await readJson(cachePath);
+ } catch {
+ logger.warn('Failed to parse cache metadata, running full analysis');
+ return { action: 'full' };
+ }
+
+ // 4. Validate cached commit exists in repo history
+ if (!await commitExists(repoPath, metadata.commitHash)) {
+ logger.warn(`Cached commit ${metadata.commitHash} not found in repo history, running full analysis`);
+ return { action: 'full' };
+ }
+
+ // 5. Compare cached commit to current HEAD
+ let headCommit: string;
+ try {
+ headCommit = await getHeadCommit(repoPath);
+ } catch {
+ logger.warn('Failed to get HEAD commit, running full analysis');
+ return { action: 'full' };
+ }
+
+ if (metadata.commitHash === headCommit) {
+ logger.info('Source code unchanged since last pre-recon, skipping');
+ return { action: 'skip' };
+ }
+
+ // 6. Generate diff summary for delta agent
+ try {
+ logger.info(`Source code changed (${metadata.commitHash.slice(0, 8)}..${headCommit.slice(0, 8)}), preparing delta analysis`);
+ const diffSummary = await generateDiffSummary(repoPath, metadata.commitHash, headCommit);
+ const cachedAnalysis = await fs.readFile(deliverablePath, 'utf8');
+
+ return { action: 'delta', diffSummary, cachedAnalysis };
+ } catch (error) {
+ const errMsg = error instanceof Error ? error.message : String(error);
+ logger.warn(`Failed to generate diff summary: ${errMsg}, running full analysis`);
+ return { action: 'full' };
+ }
+}
+
+/**
+ * Save pre-recon cache metadata after a successful analysis.
+ *
+ * @param sourceCommitHash - The source code HEAD commit captured BEFORE agent
+ * execution. Must be passed explicitly because agent execution creates
+ * checkpoint commits that change HEAD.
+ */
+export async function savePrereconCache(
+ repoPath: string,
+ sourceCommitHash: string,
+ logger: ActivityLogger
+): Promise {
+ const deliverablesDir = path.join(repoPath, 'deliverables');
+ const cachePath = path.join(deliverablesDir, CACHE_FILENAME);
+
+ const repoId = await getRepoId(repoPath);
+
+ const metadata: PrereconCacheMetadata = {
+ commitHash: sourceCommitHash,
+ timestamp: new Date().toISOString(),
+ repoId,
+ };
+
+ await atomicWrite(cachePath, metadata);
+ logger.info(`Pre-recon cache saved at commit ${sourceCommitHash.slice(0, 8)}`);
+}
diff --git a/src/session-manager.ts b/src/session-manager.ts
index a17cc7db..ba26ec58 100644
--- a/src/session-manager.ts
+++ b/src/session-manager.ts
@@ -107,6 +107,18 @@ export const AGENTS: Readonly> = Object.freez
},
});
+// Delta agent definition — not in ALL_AGENTS because it is an implementation
+// detail of the pre-recon phase, not an independently tracked workflow agent.
+// The workflow always tracks completion under the 'pre-recon' key.
+export const PRE_RECON_DELTA_AGENT: Readonly = Object.freeze({
+ name: 'pre-recon' as AgentName, // Uses 'pre-recon' identity for audit/validation
+ displayName: 'Pre-recon delta agent',
+ prerequisites: [],
+ promptTemplate: 'pre-recon-delta',
+ deliverableFilename: 'code_analysis_deliverable.md',
+ modelTier: 'medium',
+});
+
// Phase names for metrics aggregation
export type PhaseName = 'pre-recon' | 'recon' | 'vulnerability-analysis' | 'exploitation' | 'reporting';
@@ -156,6 +168,7 @@ export const MCP_AGENT_MAPPING: Record = Object.freeze(
// NOTE: Pre-recon is pure code analysis and doesn't use browser automation,
// but assigning MCP server anyway for consistency and future extensibility
'pre-recon-code': 'playwright-agent1',
+ 'pre-recon-delta': 'playwright-agent1',
// Phase 2: Reconnaissance (actual prompt name is 'recon')
recon: 'playwright-agent2',
diff --git a/src/temporal/activities.ts b/src/temporal/activities.ts
index 78780f04..e8bba273 100644
--- a/src/temporal/activities.ts
+++ b/src/temporal/activities.ts
@@ -26,18 +26,19 @@ import { ExploitationCheckerService } from '../services/exploitation-checker.js'
import type { VulnType, ExploitationDecision } from '../services/queue-validation.js';
import { AuditSession } from '../audit/index.js';
import type { WorkflowSummary } from '../audit/workflow-logger.js';
-import type { AgentName } from '../types/agents.js';
+import type { AgentName, AgentDefinition } from '../types/agents.js';
import { ALL_AGENTS } from '../types/agents.js';
import type { AgentMetrics, ResumeState } from './shared.js';
import { copyDeliverablesToAudit, type SessionMetadata } from '../audit/utils.js';
import { readJson, fileExists } from '../utils/file-io.js';
import { assembleFinalReport, injectModelIntoReport } from '../services/reporting.js';
-import { AGENTS } from '../session-manager.js';
+import { AGENTS, PRE_RECON_DELTA_AGENT } from '../session-manager.js';
import { executeGitCommandWithRetry } from '../services/git-manager.js';
import type { ResumeAttempt } from '../audit/metrics-tracker.js';
import { createActivityLogger } from './activity-logger.js';
import { runPreflightChecks } from '../services/preflight.js';
import { isErr } from '../types/result.js';
+import { checkPrereconCache, savePrereconCache, getHeadCommit, type CacheAction } from '../services/prerecon-cache.js';
// Max lengths to prevent Temporal protobuf buffer overflow
const MAX_ERROR_MESSAGE_LENGTH = 2000;
@@ -104,7 +105,8 @@ function buildSessionMetadata(input: ActivityInput): SessionMetadata {
*/
async function runAgentActivity(
agentName: AgentName,
- input: ActivityInput
+ input: ActivityInput,
+ agentDef?: AgentDefinition
): Promise {
const { repoPath, configPath, pipelineTestingMode = false, workflowId, webUrl } = input;
const startTime = Date.now();
@@ -140,7 +142,8 @@ async function runAgentActivity(
attemptNumber,
},
auditSession,
- logger
+ logger,
+ agentDef
);
// 4. Return metrics
@@ -200,6 +203,10 @@ export async function runPreReconAgent(input: ActivityInput): Promise {
+ return runAgentActivity('pre-recon', input, PRE_RECON_DELTA_AGENT);
+}
+
export async function runReconAgent(input: ActivityInput): Promise {
return runAgentActivity('recon', input);
}
@@ -314,6 +321,41 @@ export async function runPreflightValidation(input: ActivityInput): Promise {
+ const logger = createActivityLogger();
+
+ // Capture source HEAD before any agent execution changes it
+ const sourceCommitHash = await getHeadCommit(input.repoPath);
+
+ const result = await checkPrereconCache(input.repoPath, logger);
+
+ if (result.action === 'delta') {
+ const diffPath = path.join(input.repoPath, 'deliverables', '.prerecon-diff.md');
+ await fs.writeFile(diffPath, result.diffSummary, 'utf8');
+ logger.info('Wrote diff summary for delta agent');
+ }
+
+ return { action: result.action, sourceCommitHash };
+}
+
+/**
+ * Save pre-recon cache metadata after successful analysis.
+ */
+export async function savePrereconCacheActivity(
+ input: ActivityInput,
+ sourceCommitHash: string
+): Promise {
+ const logger = createActivityLogger();
+ await savePrereconCache(input.repoPath, sourceCommitHash, logger);
+}
+
/**
* Assemble the final report by concatenating exploitation evidence files.
*/
diff --git a/src/temporal/workflows.ts b/src/temporal/workflows.ts
index 82bcb2c6..f56cd9b0 100644
--- a/src/temporal/workflows.ts
+++ b/src/temporal/workflows.ts
@@ -372,7 +372,31 @@ export async function pentestPipelineWorkflow(
log.info('Preflight validation passed');
// === Phase 1: Pre-Reconnaissance ===
- await runSequentialPhase('pre-recon', 'pre-recon', a.runPreReconAgent);
+ if (!shouldSkip('pre-recon')) {
+ state.currentPhase = 'pre-recon';
+ state.currentAgent = 'pre-recon';
+ await a.logPhaseTransition(activityInput, 'pre-recon', 'start');
+
+ const cacheResult = await preflightActs.checkPrereconCacheActivity(activityInput);
+
+ if (cacheResult.action === 'skip') {
+ log.info('Pre-recon cache hit: source unchanged, skipping');
+ } else if (cacheResult.action === 'delta') {
+ log.info('Pre-recon cache hit: source changed, running delta update');
+ state.agentMetrics['pre-recon'] = await a.runPreReconDeltaAgent(activityInput);
+ await preflightActs.savePrereconCacheActivity(activityInput, cacheResult.sourceCommitHash);
+ } else {
+ log.info('No pre-recon cache, running full analysis');
+ state.agentMetrics['pre-recon'] = await a.runPreReconAgent(activityInput);
+ await preflightActs.savePrereconCacheActivity(activityInput, cacheResult.sourceCommitHash);
+ }
+
+ state.completedAgents.push('pre-recon');
+ await a.logPhaseTransition(activityInput, 'pre-recon', 'complete');
+ } else {
+ log.info('Skipping pre-recon (already complete)');
+ state.completedAgents.push('pre-recon');
+ }
// === Phase 2: Reconnaissance ===
await runSequentialPhase('recon', 'recon', a.runReconAgent);