# PentestGPT Agent

Small, durable penetration-testing loop built on the external
[`unified-agent`](https://github.com/PentestGPT-Project/UnifedAgentWrapper) package. It is the
maintained autonomous framework; the root `pentestgpt_legacy` package remains the human-driven
USEN-2024 workflow.

The supported deployment boundary is an isolated, disposable environment containing only
authorized targets and the required provider credentials.

## Runtime shape

```text
SQLite snapshot
  -> recover an active lease or invoke a fresh Supervisor
  -> validate one typed decision and atomically lease one task
  -> recover its trace or invoke a fresh Executor
  -> validate trace identity and exact receipt evidence
  -> atomically commit attempt, observation, and transition
  -> repeat, finish, safely retry, or fail closed
```

- The Supervisor and Executor both receive all provider tools and `FULL_ACCESS` filesystem/process
  permissions. PentestGPT relies on deployment isolation rather than a second in-process sandbox.
- The Supervisor proposes at most one new task and selects exactly one ready task or completion.
- The Executor receives one leased task with an explicit task kind and bounded provider turn budget.
- Every episode is fresh (`resume = null`); provider conversation history is not memory.
- Deterministic code owns scope, dependencies, leases, receipt provenance, retries, completion
  bases, and canonical state transitions.
- Provider actions and file writes are retained in traces and audit totals, but are allowed.
- There is no speculative backlog, parallel scheduler, RAG service, or always-on judge.

See [`CONTEXT.md`](CONTEXT.md) for the exact domain language and invariants, and
[`../docs/architecture.md`](../docs/architecture.md) for repository-level decisions.

## Memory and traces

SQLite stores runs, typed tasks, attempts, bounded exact observations, and one transition per
revision. An observation must resolve to one exact contiguous slice of one eligible command or tool
receipt. Completed nonzero commands may ground negative findings; provider/tool transport failures
cannot. CRLF/LF normalization is the only accepted textual transport normalization.

An oversized receipt contributes at most an exact 4,000-character suffix and can commit only
`progress`. A no-action attempt may reuse exact evidence from an earlier attempt of the same task;
unsupported paraphrases degrade to `progress` and never create evidence.

The Supervisor receives a bounded projection: open work, four recent closed tasks, required
dependency/basis context, selected observations, aggregate history, and recent diagnostics. The
Executor receives one task, its explicit basis, up to two same-task observations, and one retry
diagnostic. Future retrieval may select canonical IDs, but SQLite remains authoritative.

Each episode directory contains:

```text
input.json     exact role input, prompt/schema hashes, provider policy
events.jsonl   chronological normalized tool, command, file, and terminal events
output.json    normalized result, usage, cost, duration, and failure
```

Traces contain sensitive target output and provider session identifiers. They are mode-restricted,
not encrypted or tamper-evident.

## Failure and restart behavior

- Operational failures retry only when no external-action receipt exists.
- Actionful failures are never replayed automatically.
- Existing terminal traces are compiled and committed before any provider reinvocation.
- A terminal event can reconstruct a missing `output.json`.
- Supervisor decisions are recovered by state revision; invalid plans receive one bounded retry.
- Attempt and decision budgets survive restart.
- `--resume` requires an exact `trial-config.json` match, including goal, targets, provider, model,
  effort, prompt/schema hashes, budgets, both full-access role policies, environment, and
  `unified-agent` version.

## Install and verify

Run from this directory so the repository-root compatibility copy cannot shadow the dependency.

```bash
uv sync --extra claude       # or: codex / all
uv run python -m pytest -q
uv run ruff check src tests
uv run ruff format --check src tests
uv run mypy src
uv lock --check
uv build
```

The normal suite uses scripted provider adapters and a loopback target. The opt-in live smoke spends
provider tokens:

```bash
PENTESTGPT_AGENT_LIVE=1 \
PENTESTGPT_AGENT_BACKEND=claude \
PENTESTGPT_AGENT_MODEL=claude-opus-4-8 \
uv run python -m pytest tests/live/test_local_model.py -q -s
```

## Run and resume

```bash
uv run pentestgpt-agent \
  --goal "Capture the flag from this authorized target." \
  --target http://127.0.0.1:8080 \
  --backend claude \
  --model claude-opus-4-8 \
  --effort xhigh \
  --run-id example

uv run pentestgpt-agent \
  --goal "Capture the flag from this authorized target." \
  --target http://127.0.0.1:8080 \
  --backend claude \
  --model claude-opus-4-8 \
  --effort xhigh \
  --run-id example \
  --resume
```

Audit a held-out oracle:

```bash
uv run pentestgpt-agent-audit \
  --run-dir runs/example \
  --expected-flag 'FLAG{...}' \
  --output runs/example-audit.json
```

Audit schema 2 checks structural completion, canonical oracle evidence, settled leases/episodes,
exact receipt grounding, identity, dependency/basis integrity, current TEST provenance for EXPLOIT,
completion basis, revision continuity, and settled failures. Supervisor actions and provider-memory
writes are reported as diagnostics rather than failures.

## UnifiedAgent dependency

`unified-agent==0.3.0` is pinned to the merged inspector commit in `pyproject.toml`. It is the provider seam
for Claude Code and Codex: task invocation, model/effort configuration, structured output, and
normalized events. PentestGPT keeps task policy, memory, evidence, and completion outside that
dependency.

`tests/test_dependency.py` verifies that imports resolve to the installed external package, not the
obsolete repository-root `unified_agent/` copy.

### Live process inspector

UnifiedAgent 0.3 adds a read-only local web inspector. PentestGPT gives every monitored provider
episode a stable `<pentest-run-id>--<episode-id>` identity and attaches the role, state revision,
task, and attempt as metadata. The framework's trace journal remains canonical; the inspector
database is an additional real-time diagnostic projection.

To inspect local wrapper changes before publishing a new immutable dependency revision, explicitly
opt into the sibling checkout:

```bash
export PYTHONPATH="$(cd ../../UnifedAgentWrapper && pwd)"
export UNIFIED_AGENT_MONITOR_DB="$(pwd)/runs/inspector.sqlite3"
uv run --no-sync pentestgpt-agent \
  --goal "Assess this explicitly authorized target." \
  --target http://127.0.0.1:8080 \
  --backend codex \
  --model gpt-5.5 \
  --run-id inspector-example

# In another terminal, from ../../UnifedAgentWrapper:
PYTHONPATH=. .venv/bin/python -m unified_agent.monitor_web \
  --database "$(cd ../PentestGPT/pentestgpt_agent && pwd)/runs/inspector.sqlite3" \
  --open
```

The pinned 0.3 dependency provides the same monitor behavior in normal synchronized environments;
the sibling override is only for wrapper development.

## External evaluation references

The sibling `../../xbow-benchmark` checkout is retained only as a historical/reference corpus.
There is no supported XBOW command, runner, or CI path in `pentestgpt-agent`, and benchmark-specific
orchestration must stay outside this product repository. Historical engineering records may still
name earlier XBEN qualifications; they are not current regression guarantees.

The later HTB Enigma qualification did not solve the target; see
[`HTB_ENIGMA_QUALIFICATION_20260712.md`](HTB_ENIGMA_QUALIFICATION_20260712.md).

## Current limits

- `FULL_ACCESS` is intentional. Either role may read or alter mounted state and credentials; the
  surrounding container/VM must be treated as the blast radius.
- Provider `max_turns` is not a portable command/action limit, especially on Codex. It bounds the
  provider interaction where supported, not external side effects.
- Controller convergence is the main demonstrated performance problem: older coverage can fall out
  of the Supervisor projection and trigger repeated discovery.
- `COMPLETED` proves structural evidence citation, not arbitrary natural-language entailment. CTF
  audits use a held-out oracle; general goals need a goal-specific verifier.
- SQLite has no versioned migration framework. Trace storage is sensitive and not power-loss or
  tamper hardened.
- Structured findings, report generation, and a PentestGPT product UI remain future work. The
  wrapper-level process inspector is available for live diagnostics.
