Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.vigolium.com/llms.txt

Use this file to discover all available pages before exploring further.

Piolium is Vigolium’s Pi-native multi-phase whitebox security audit harness. It runs as a foreground subcommand, vigolium agent piolium, driving a user-installed Pi extension via pi --mode json -p "/piolium-<mode>". Findings are automatically ingested into the Vigolium database alongside native scanner results.
Looking for the unified driver? vigolium agent audit runs piolium and archon back-to-back under one AgenticScan, with per-driver child rows and a post-pass findings dedup. Use --driver=piolium (or --driver=archon) to force a single driver. This page describes piolium standalone; see the audit subcommand’s --help for the unified shape.
Piolium shares its on-disk schema (audit-state.json, finding markdown, frontmatter conventions) with archon-audit — the parser, importer, and reporting tooling are shared between the two. The differences are runtime (Pi instead of Claude Code / Codex / OpenCode), folder name (piolium/ instead of archon/), env-var prefix (PIGOLIUM_* instead of ARCHON_*), and database tagging (mode=piolium instead of archon).

Why & When to Use It

Piolium and archon-audit share the same on-disk schema, finding format, and reporting tooling — they differ in which model family drives the audit. Pick based on what credentials you actually have:
  • Use archon-audit when you have access to the Claude Opus family (Claude Code, Vertex Anthropic, Bedrock Anthropic). Archon’s prompt orchestration was tuned against Opus and consistently produces the highest-quality findings on that family.
  • Use piolium when your available provider is OpenAI (GPT/Codex), Google (Gemini), or any other non-Claude model. Pi’s adapter layer abstracts the provider, and piolium’s phase prompts are designed to extract solid audit results from non-Opus models without sacrificing the adversarial-debate / cold-verify quality controls.

When to use it

Reach for vigolium agent piolium when:
  • You’re running on an OpenAI key (gpt-5.5, Codex) and want quality comparable to a Claude-Opus archon run.
  • You’re using Gemini or another Vertex/Bedrock-hosted non-Anthropic model.
  • You want a model-agnostic harness so you can swap providers (--pi-provider / --pi-model) without changing the audit pipeline.
  • You need the longshot mode (file-by-file hail-mary hunt) — it’s piolium-only and not available in archon.
Reach for archon-audit instead when:
  • You have Claude Opus access and want the best-possible audit fidelity.
  • You want to lock the swarm/autopilot in-pipeline audit to Claude/Codex/OpenCode regardless of what’s installed locally — pass --archon=<mode> to opt out of the auto-pick.
Both harnesses can run as the in-pipeline audit during vigolium agent autopilot and vigolium agent swarm. When you don’t pass --archon or --piolium, the CLI auto-picks: piolium runs if pi+the piolium extension are installed locally, otherwise it falls back to archon. The two are interoperable: findings from either land in the same findings table tagged with their source and can be reported together. To run both harnesses on the same source under one AgenticScan, use the unified vigolium agent audit driver.

Quick Start

# Default balanced audit against the current directory
vigolium agent piolium --source .

# Quick triage (lite, 4 phases) on a checkout
vigolium agent piolium --source ./backend --mode lite

# Deep audit (17 phases) on a remote repo, full clone history
vigolium agent piolium --source [email protected]:org/repo.git --mode deep

# Confirm an existing audit's findings live against a target
vigolium agent piolium --source ./backend --mode confirm

# Hail-mary file-by-file scan with a per-file budget
vigolium agent piolium --source ./backend --mode longshot --plm-longshot-limit 200

# Re-run with intensity preset (preset → mode + commit-depth)
vigolium agent piolium --source ./backend --intensity deep
vigolium agent piolium requires --source — it audits source code, not network traffic.

Prerequisites

Piolium runs as a Pi extension, so two binaries must be installed on the host:
  1. Pi runtime (@mariozechner/pi-coding-agent):
    bun install -g @mariozechner/pi-coding-agent
    pi --version
    
  2. Piolium extension (github.com/vigolium/piolium):
    pi install git:[email protected]:vigolium/piolium.git
    pi list   # verify "piolium" appears
    
Vigolium does not auto-install piolium. Before any subcommand work, vigolium agent piolium reads ~/.pi/agent/settings.json, scans the packages array, and aborts with an actionable install hint if no piolium entry is found. This avoids the agent silently writing to user settings or pulling node_modules. Optional, for richer scans (piolium falls back to grep when these aren’t on $PATH):
  • trufflehog, gitleaks — secrets scanning
  • codeql, semgrep — static analysis

How It Works

When vigolium agent piolium runs:
  1. Vigolium verifies pi is on $PATH and piolium is registered in ~/.pi/agent/settings.json.
  2. The session directory is created at ~/.vigolium/agent-sessions/<scan-uuid>/.
  3. --source is resolved (local path, git URL, gs:// archive, or local archive) and the resolved tree becomes the cwd of the pi subprocess.
  4. The audit banner is printed (mode, source, session dir) so the user sees the run context before any network activity.
  5. Preflight: unless --no-preflight is passed, Vigolium runs a one-turn pi --mode json -p "..." roundtrip against the configured provider/model. The CLI prints · Pi preflight check... ok provider=… model=… in 2.3s on success or aborts with the captured upstream error (e.g. No API key found for google-vertex. Use /login to log into a provider). This catches auth/quota issues before the audit subprocess is spawned.
  6. A child AgenticScan row is created with mode=piolium.
  7. Vigolium spawns pi --mode json -p "/piolium-<mode>" [--plm-* …] with cmd.Dir = <resolved-source> and PIGOLIUM_* env vars exported.
  8. Pi loads the piolium extension, which writes its output under <source>/piolium/.
  9. Vigolium tails Pi’s --mode json stream:
    • stdout JSONL is decoded by pkg/piolium/pistream into a colored activity feed.
    • Raw lines are persisted to <sessionDir>/audit-stream.jsonl for replay (vigolium log <uuid>).
  10. Every 30 seconds, <source>/piolium/audit-state.json and <source>/piolium/findings-draft/ are synced to <sessionDir>/piolium-audit/ so progress is visible mid-run.
  11. When pi exits, the full <source>/piolium/ tree is copied into <sessionDir>/piolium-audit/, findings are parsed and imported into the database, and the source-side <source>/piolium/ directory is removed.
  12. Cost is computed from Pi’s per-cwd session transcripts and persisted on the AgenticScan row.
+-----------------------------------------------------------+
|                  vigolium agent piolium                    |
|                                                            |
|  +------------------+    +-----------------------------+  |
|  |   Foreground      |    |  Subprocess (`pi --mode    |  |
|  |   Vigolium        |    |  json -p /piolium-<mode>`)  |  |
|  |                   |    |                             |  |
|  |  resolve source   |    |  Pi loads piolium ext.      |  |
|  |  spawn pi         |--->|  Phase orchestrator runs    |  |
|  |  decode JSONL     |<---|  spawns sub-agents          |  |
|  |  sync every 30s   |<---|  writes <source>/piolium/   |  |
|  |  parse findings   |<---|  exits                      |  |
|  +-------+-----------+    +-------------+---------------+  |
|          |                                                  |
|          v                                                  |
|  +-----------------------------------------------------+  |
|  |                     Database                         |  |
|  |  findings (source: scanner modules + piolium)       |  |
|  |  agentic_scans (mode=piolium, parent_uuid=...)      |  |
|  +-----------------------------------------------------+  |
+-----------------------------------------------------------+

Audit Modes

Piolium exposes 10 modes via --mode. Operational modes (status, smoke, merge, diff, confirm, revisit) are not part of the intensity ladder — pass --mode explicitly to invoke them.
ModePhasesPhase IDsPurpose
lite4Q0–Q3Quick recon, secrets scan, fast SAST
balanced9L1–L7 (incl. L6b/L6c)Default audit path with PoCs and report
deep17P1–P17Full audit including adversarial debate, cold verification, variant hunting
revisit9R5–R11cAnti-anchored second pass over an existing audit
confirm7V1–V7Confirm existing findings live, optionally with tests
merge7M1–M7Merge and dedupe result trees from prior runs
diff1D1Scan changed files since an audited commit
longshot3Hail-mary file-by-file vulnerability hunt
statusRead-only progress check on an existing run
smokeVerify runner/provider wiring
The phase ID space is intentionally interchangeable with archon-audit (Q*, L*, P*, V*, R*, M*) so the same parser, finding format, and reporting tooling apply. For full per-phase semantics see piolium’s own docs/phase-reference.md and docs/output-structure.md.

Intensity presets

--intensity bundles the audit mode and clone depth into a single flag, matching the autopilot/swarm/archon intensity model:
IntensityModeCommit depthUse case
quicklite1 (shallow)CI/CD, routine triage
balancedbalanced1 (shallow)Default; PoCs + report
deepdeep0 (full history)Pre-release, compliance, commit archaeology
Explicit --mode and --commit-depth always override the preset.

CLI

vigolium agent piolium \
  [--mode {lite,balanced,deep,revisit,confirm,merge,diff,longshot,status,smoke}] \
  [--intensity {quick,balanced,deep}] \
  --source <path|git-url|gs://...|archive> \
  [--commit-depth N] \
  [--no-stream] \
  [--upload-results] \
  [--plm-* passthroughs]

Flag reference

FlagDescription
--sourceRequired. Local directory, git URL, gs://<project>/<key> archive, or local .zip/.tar.gz/.tar.bz2/.tar.xz.
--modeAudit mode. Overrides --intensity.
--intensityPreset. quick/balanced/deep. Defaults to balanced.
--commit-depthgit clone --depth for git URLs. 0 = full history. Overrides --intensity.
--no-streamDon’t echo to console. Stream is still persisted to runtime.log so vigolium log <uuid> works.
--upload-resultsUpload session bundle to cloud storage after completion (requires storage config).
--pi-providerOverride pi’s defaultProvider for this run (e.g. vertex-anthropic, google-vertex). Threaded through as pi --provider <name>.
--pi-modelOverride pi’s defaultModel for this run (e.g. claude-opus-4-6, gemini-3.1-pro). Threaded through as pi --model <id>.
--no-preflightSkip the pre-audit pi roundtrip (auth + model availability check).
--preflight-timeoutCap on the preflight call (default 30s).

--plm-* passthroughs

These flags map 1:1 onto piolium’s own --plm-* session flags. Empty or zero values are dropped — piolium’s defaults apply when you don’t override.
FlagPiolium scopePiolium default
--plm-scan-limit NCap commit-history scan to N commits500
--plm-scan-since <expr>git --since window (e.g. "60 days ago")"60 days ago"
--plm-phase-retries NPer-phase retry count2
--plm-command-retries NPer-command retry count3
--plm-longshot-limit NMax files hunted in longshot mode1000
--plm-longshot-timeout MSPer-file kill timer in longshot21600000 (6h)
--plm-longshot-langs <list>Comma-separated language allowlistauto-detect

Examples

# Balanced audit on a remote git URL, shallow clone
vigolium agent piolium \
  --source [email protected]:vigolium/piolium.git \
  --intensity balanced

# Deep audit with bounded commit history scan
vigolium agent piolium \
  --source ./backend \
  --mode deep \
  --plm-scan-limit 250 \
  --plm-scan-since "90 days ago"

# Longshot hunt on Python and Go files only
vigolium agent piolium \
  --source ./mono-repo \
  --mode longshot \
  --plm-longshot-langs python,go \
  --plm-longshot-limit 200

# Pinned scan UUID for cross-node sync
vigolium --scan-uuid 019aa... agent piolium --source ./backend

# Override pi's provider/model for this single run
vigolium agent piolium --source ./backend \
  --pi-provider vertex-anthropic \
  --pi-model claude-opus-4-6

vigolium agent piolium --source ./backend \
  --pi-provider google-vertex \
  --pi-model gemini-3.1-pro

Running piolium and archon together

The standalone vigolium agent piolium command runs piolium only — there is no harness fallback. If you want both archon and piolium to score the same source tree (under one AgenticScan, with per-driver child rows and a post-pass project-wide findings dedup), use the unified driver:
# Default — runs archon then piolium sequentially
vigolium agent audit --source ./backend

# Just piolium (equivalent to `vigolium agent piolium`)
vigolium agent audit --driver piolium --source ./backend --mode lite

# Just archon (equivalent to `vigolium agent archon`)
vigolium agent audit --driver archon --source ./backend --agent claude
Mode must be in the shared set (lite/balanced/deep/revisit/confirm/merge) when --driver=both. Driver-specific modes (longshot/smoke for piolium, mock for archon) require --driver=piolium or --driver=archon.

REST API

POST /api/agent/run/audit is the unified driver endpoint — it dispatches archon and/or piolium based on the driver field. To run only piolium, pass driver: "piolium". To run only archon, use driver: "archon" (or hit POST /api/agent/run/archon directly). The default driver: "both" runs archon then piolium under one AgenticScan with per-driver child rows and a post-pass findings dedup. Same lifecycle and AgenticScan row shape as /api/agent/run/archon — the existing /agent/status/:id, /agent/sessions/:id/logs, and /agent/sessions/:id/artifacts endpoints work uniformly across both harnesses. The server-side handler does not run the CLI’s preflight roundtrip (pi --mode json is started straight after request validation). Auth/quota failures surface in the SSE/runtime.log stream instead.

Request body

FieldTypeDescription
sourcestringRequired. Local path, git URL, gs://<bucket>/<key> archive (auto-downloaded + extracted server-side), or local .zip/.tar.gz/.tar.bz2/.tar.xz. With driver: "both", the source is resolved once and reused by both drivers.
targetstringOptional target URL stored on the run row for cross-referencing scans.
intensitystringquick / balanced (default) / deep. Bundles mode + timeout + commit_depth (quick → lite/1h/depth 1, balanced → balanced/6h/depth 1, deep → deep/12h/depth 0). Explicit mode/timeout/commit_depth win per-field over the preset.
modestringAudit mode override. With driver: "both", must be in the shared set: lite / balanced / deep / revisit / confirm / merge. Single-driver mode adds diff/status plus driver-specific values (longshot/smoke for piolium, mock for archon).
timeoutstringGo duration (e.g. "2h"). Overrides intensity preset. Applied per-driver under driver: "both" so an archon hang doesn’t burn piolium’s budget.
diffstringFocus on changed code: PR URL, git ref range, or HEAD~N.
last_commitsintFocus on last N commits (shorthand for diff HEAD~N).
commit_depthintgit clone --depth for git URLs. 0 = full history. Overrides intensity.
filesstring[]Specific files to focus on.
streamboolEnable SSE streaming of the agent feed. With driver: "both", events are tagged with a driver field and bracketed by driver_start/driver_end markers.
upload_resultsboolUpload session bundle to cloud storage on completion. With driver: "both", skipped when any driver failed.
project_uuidstringProject UUID for data scoping. Falls back to X-Project-UUID header.
scan_uuidstringOptional scan UUID.
driverstring"piolium" / "archon" / "both" (default). With "both", mode must be in the shared set.
no_dedupboolSkip the post-pass project-wide findings dedup that runs after driver: "both" completes. Ignored for single-driver runs (those already INSERT-time-dedup by finding_hash).
agentstringArchon platform when archon participates: claude (default) / codex / opencode. Ignored when driver: "piolium".
pi_providerstringForwarded as pi --provider <name>. Ignored when driver: "archon".
pi_modelstringForwarded as pi --model <id>.
plm_scan_limitint--plm-scan-limit passthrough.
plm_scan_sincestring--plm-scan-since passthrough.
plm_phase_retriesint--plm-phase-retries passthrough.
plm_command_retriesint--plm-command-retries passthrough.
plm_longshot_limitint--plm-longshot-limit passthrough.
plm_longshot_timeoutint--plm-longshot-timeout passthrough (ms).
plm_longshot_langsstring--plm-longshot-langs passthrough (comma-separated).
All pi_* and plm_* fields are optional and only emit their flag when populated, matching the CLI’s behavior.
gs:// requires storage config. The server resolves gs://<bucket>/<key> via pkg/storage using the configured GCS credentials (see vigolium-configs.yaml storage.gcs.*). Downloads land in a temp dir that’s removed after the run regardless of outcome. Returns 400 if the URI is reachable but not a recognized archive, or 500 if the download itself fails.

Examples

# Default — driver=both with intensity=balanced (archon then piolium,
# project-wide findings dedup after both exit)
curl -s -X POST http://localhost:9002/api/agent/run/audit \
  -H "Content-Type: application/json" \
  -d '{
    "source": "/home/user/src/my-app",
    "intensity": "balanced"
  }' | jq .

# Quick triage with intensity preset — driver=both, mode=lite,
# commit_depth=1 (resolved server-side from the preset)
curl -s -X POST http://localhost:9002/api/agent/run/audit \
  -H "Content-Type: application/json" \
  -d '{
    "source": "/home/user/src/my-app",
    "intensity": "quick",
    "driver": "both"
  }' | jq .

# Deep audit with full git history (intensity=deep → commit_depth=0)
# pinned to piolium only with a provider override
curl -s -X POST http://localhost:9002/api/agent/run/audit \
  -H "Content-Type: application/json" \
  -d '{
    "source": "[email protected]:org/repo.git",
    "intensity": "deep",
    "driver": "piolium",
    "pi_provider": "vertex-anthropic",
    "pi_model": "claude-opus-4-6"
  }' | jq .

# Source from Google Cloud Storage — server downloads + extracts the
# archive once, both drivers reuse the resolved tree (no double clone)
curl -s -X POST http://localhost:9002/api/agent/run/audit \
  -H "Content-Type: application/json" \
  -d '{
    "source": "gs://vigolium-uploads/acme/backend-2026-05-02.tar.gz",
    "intensity": "balanced",
    "project_uuid": "11111111-2222-3333-4444-555555555555"
  }' | jq .

# gs:// + driver=archon only (skip piolium even though it's installed)
curl -s -X POST http://localhost:9002/api/agent/run/audit \
  -H "Content-Type: application/json" \
  -d '{
    "source": "gs://vigolium-uploads/acme/backend-2026-05-02.zip",
    "driver": "archon",
    "agent": "claude",
    "intensity": "balanced"
  }' | jq .

# Explicit mode wins over intensity — longshot is piolium-only, so
# driver must be set to piolium (driver=both would 400)
curl -s -X POST http://localhost:9002/api/agent/run/audit \
  -H "Content-Type: application/json" \
  -d '{
    "source": "/home/user/src/mono-repo",
    "driver": "piolium",
    "mode": "longshot",
    "plm_longshot_langs": "python,go",
    "plm_longshot_limit": 200
  }' | jq .

# Skip the post-pass dedup (driver=both only — single-driver runs
# already INSERT-time-dedup by finding_hash)
curl -s -X POST http://localhost:9002/api/agent/run/audit \
  -H "Content-Type: application/json" \
  -d '{
    "source": "/home/user/src/my-app",
    "intensity": "balanced",
    "no_dedup": true
  }' | jq .

Response

The endpoint returns 202 Accepted with the parent run UUID; tail progress via /api/agent/sessions/<uuid>/logs (SSE-capable) or poll /api/agent/status/<uuid> for phase counters. With driver: "both", child rows are exposed under the parent via /api/agent/sessions/<uuid> (child_runs[]).
{
  "agentic_scan_uuid": "b1e100e5-1131-4b41-995d-f0991534ac14",
  "status": "running",
  "message": "audit (driver=both) started"
}
Single-driver runs (driver: "piolium" or "archon") return "message": "audit run started". Pass "stream": true in the request to keep the connection open and receive an SSE stream of chunk / error / done events instead of the 202.

Errors

StatusCause
400Missing source; invalid mode/intensity/driver; a driver-specific mode (longshot/smoke/mock) under driver: "both"; or invalid archon agent (claude/codex/opencode) when archon participates.
429Heavy-agent semaphore is full — retry after the in-flight audit completes.
503The single requested driver’s runtime is unavailable: driver: "piolium" returns 503 when pi isn’t on PATH or the piolium extension isn’t registered in ~/.pi/agent/settings.json; driver: "archon" returns 503 when the configured platform binary (claude/codex/opencode) isn’t on PATH. driver: "both" never returns 503 for missing runtimes — a missing pi or platform binary becomes a server warning log, the available driver still runs, and the failed driver surfaces as a per-driver error on the child run (parent ends completed_with_errors). The request only fails if both drivers turn out to be unavailable, which still produces a 202 Accepted followed by a completed_with_errors parent row that names both.

Combined-driver SSE events

When driver: "both" and stream: true, archon-then-piolium output is multiplexed into one SSE stream. Each event includes a driver field ("archon" or "piolium"); driver_start and driver_end events bracket each driver’s slot so clients can render per-driver progress. The error field on driver_end carries the per-driver failure (if any), and a final done event fires only when both drivers completed without errors.
# Run both drivers, multiplexed SSE
curl -N -s -X POST http://localhost:9002/api/agent/run/audit \
  -H "Content-Type: application/json" \
  -d '{
    "source": "/home/user/src/my-app",
    "mode": "balanced",
    "driver": "both",
    "stream": true
  }'

In-Pipeline Audit (autopilot / swarm)

vigolium agent autopilot and vigolium agent swarm can drive piolium as the in-pipeline audit that runs alongside (and feeds findings into) the operator agent. The same CLI that historically defaulted to archon now auto-picks piolium when it’s locally available.

Auto-pick

When --source is set and neither --archon nor --piolium is passed:
  • If pi is on $PATH and the piolium extension is registered in ~/.pi/agent/settings.jsonpiolium runs (mode = whatever the intensity preset chose, default lite).
  • Otherwise → archon runs (existing default).
This preserves the prior behavior on machines without Pi while picking up piolium automatically once a user installs it.

Explicit override

Flag combinationResult
--piolium (explicit, any mode)Piolium runs at that mode; archon turns off.
--archon (explicit, any mode)Archon runs at that mode; piolium stays off.
--archon=off --piolium=offNo audit.
Only one harness runs per scan — there’s no “run both” mode under autopilot/swarm. If you want both harnesses on the same source, use vigolium agent audit instead (it produces one parent AgenticScan with per-driver child rows).

REST API equivalents

Both POST /api/agent/run/autopilot and POST /api/agent/run/swarm accept a piolium field that mirrors the CLI flag exactly. The same auto-pick rule applies server-side: omitting both archon and piolium triggers piolium when the server process has pi+piolium installed, archon otherwise.
# Autopilot — explicit piolium audit
curl -s -X POST http://localhost:9002/api/agent/run/autopilot \
  -H "Content-Type: application/json" \
  -d '{
    "target": "https://example.com",
    "source": "/home/user/src/my-app",
    "piolium": "balanced"
  }' | jq .

# Swarm — opt-in piolium audit during a targeted scan
curl -s -X POST http://localhost:9002/api/agent/run/swarm \
  -H "Content-Type: application/json" \
  -d '{
    "input": "https://example.com/api/users?id=1",
    "source": "/home/user/src/my-app",
    "discover": true,
    "piolium": "lite"
  }' | jq .

# Autopilot — let auto-pick decide; pi+piolium on this host means piolium wins
curl -s -X POST http://localhost:9002/api/agent/run/autopilot \
  -H "Content-Type: application/json" \
  -d '{
    "target": "https://example.com",
    "source": "/home/user/src/my-app"
  }' | jq .

Findings flow

Whichever harness runs:
  • The audit’s audit-state.json and findings/ tree are synced into <sessionDir>/<harness.SessionSubdir>/ (archon-audit/ or piolium-audit/).
  • The autopilot pipeline blocks on the audit before launching the operator agent, then folds the findings into the operator’s frozen context bundle (same flow that archon has used).
  • Findings land in the database tagged with finding_source = "archon" or "piolium". Mix and match in queries:
    vigolium finding list --source piolium,archon
    

Caveats

  • Pi-specific knobs (--pi-provider, --pi-model, --plm-*) are not exposed on the autopilot/swarm surface. If you need those, run vigolium agent piolium (or POST /api/agent/run/audit) directly.
  • Auto-pick is host-local. If your CLI machine has pi installed but the server running autopilot doesn’t (or vice versa), the auto-pick decision happens where the request handler runs.

Streaming

Pi’s --mode json flag emits newline-delimited AgentSessionEvent objects to stdout. Vigolium’s pkg/piolium/pistream decoder consumes them and renders a compact, colored activity feed.

Event types rendered

EventRendering
sessionOne-line header with session ID and cwd
agent_start / agent_endRun lifecycle markers; agent_end includes elapsed duration
message_end (assistant)Final assistant text, plus error messages on auth/quota failures
tool_execution_start→ tool_name (args)
tool_execution_end← tool_name result ( on error)
auto_retry_start / auto_retry_endRetry attempts with backoff and cause
compaction_start / compaction_endContext compaction cycles
turn_start, turn_end, message_start, message_update, tool_execution_update, queue_update, and session_info_changed are intentionally suppressed — they’re either redundant with the *_end events or UI-state-only. The raw JSONL is mirrored to <sessionDir>/audit-stream.jsonl regardless of --no-stream, so vigolium log <uuid> can replay the exact event stream after the fact.

Cost Tracking

Pi pre-prices every assistant turn and writes the result into its session transcript. Vigolium’s pkg/piolium/picost extracts that data after the run.

How cost is extracted

  1. After pi exits, picost.BuildSummary locates ~/.pi/agent/sessions/<encoded-cwd>/, where <encoded-cwd> is --<path>-- with / replaced by -. Example: /Users/alice/Desktop/repo--Users-alice-Desktop-repo--
  2. Every <timestamp>_<sessionid>.jsonl transcript with a session header timestamp inside [startedAt - 30s, startedAt + 24h] is scanned.
  3. Each assistant message event carries:
    "usage": {
      "input": 4254, "output": 14, "cacheRead": 0, "cacheWrite": 0, "totalTokens": 4268,
      "cost": { "input": 0.02127, "output": 0.00042, ..., "total": 0.02169 }
    }
    
    Tokens and cost.total are summed across all attributed transcripts.
  4. Transcripts whose only assistant turn was a 401-error (auth failure) are dropped — they have zero cost and would skew the model attribution.

Output

The CLI summary appends:
ℹ Cost: ~$0.02 (model gpt-5.5)
Multi-session runs (deep mode, longshot) annotate as (model gpt-5.5, 23 sessions). The AgenticScan DB row is populated with:
FieldSource
total_input_tokensSum of usage.input
total_output_tokensSum of usage.output
estimated_cost_usdSum of usage.cost.total
token_usageFull picost.Summary JSON (incl. per-session breakdown)
Unlike claudecost and codexcost, picost has no local pricing table — Pi has already computed prices against the active provider’s rates by the time the message lands in the transcript.

Configuration

YAML config

Piolium reuses the existing agent.sessions_dir and storage settings. There is no piolium-specific block in vigolium-configs.yaml — the harness ships with Pi, not Vigolium.
agent:
  sessions_dir: ~/.vigolium/agent-sessions/   # session artifacts root
Pi-side configuration (provider, model, auth) lives in ~/.pi/agent/settings.json and is set via pi itself:
pi config set defaultProvider openai-codex
pi config set defaultModel gpt-5.5
The piolium extension also reads its own --plm-* flags from the session, which Vigolium passes through verbatim from the matching --plm-* flags on vigolium agent piolium.

Environment variables exported to pi

Vigolium replicates piolium’s expected env contract before launching:
VariableSource
PIGOLIUM_REPOSITORYResolved repo identity (git remote URL canonicalized to owner/repo, else dir basename)
PIGOLIUM_GIT_AVAILABLEtrue when <source> is a git work tree
PIGOLIUM_SESSION_UUIDVigolium’s AgenticScan UUID for this run
PIGOLIUM_COMMIT_SCAN_LIMITSet when --plm-scan-limit > 0
PIGOLIUM_COMMIT_SCAN_SINCESet when --plm-scan-since is non-empty

Session Artifacts

Source directory (transient, removed after import)

<source_path>/
└── piolium/
    ├── audit-state.json               # Phase progress (snake_case keys, archon-compatible)
    ├── attack-surface/                # Recon, KB, SAST, probe summaries
    │   ├── lite-recon.md
    │   ├── knowledge-base-report.md
    │   ├── sast-merged.sarif
    │   ├── source-sink-flows-all-severities.md
    │   ├── public-routes-authz-matrix.md
    │   └── ...
    ├── findings-draft/                # Candidate findings (filed during a run)
    │   ├── q2-001-sql-injection-user-lookup.md
    │   └── ...
    ├── findings/                      # Final consolidated findings
    │   ├── p10-001-direct-git-url-ref-…/
    │   │   ├── draft.md               # Frontmatter metadata
    │   │   ├── report.md              # Polished, structured analysis
    │   │   ├── poc.{sh,ts,py,…}       # Executable proof
    │   │   └── evidence/              # Supporting files
    │   └── ...
    ├── final-audit-report.md          # Top-level report
    ├── confirmation-report.md         # confirm-mode output
    ├── chamber-workspace/             # Adversarial debate transcripts (deep mode)
    ├── codeql-artifacts/              # CodeQL DBs, SARIF
    ├── semgrep-rules/
    └── tmp/                           # Per-sub-agent run transcripts (cleaned up)

Vigolium session directory (persistent)

~/.vigolium/agent-sessions/<uuid>/
├── piolium-audit/                     # Synced from <source>/piolium/
│   ├── audit-state.json
│   ├── attack-surface/
│   ├── findings/
│   ├── final-audit-report.md
│   └── ...
├── audit-stream.jsonl                 # Raw Pi --mode json events
├── piolium-audit-output.md            # Captured stdout (fallback when stream is empty)
└── runtime.log                        # Replayable feed via `vigolium log <uuid>`

Finding Format

Piolium’s finding format is identical to archon’s — same YAML frontmatter, same body conventions, same cold-verify overlay pattern. The only naming difference is the directory layout: piolium keeps the source phase ID on its promoted findings (p10-001-<slug>/) instead of renumbering to severity-letter format (C1-<slug>/ in archon). Vigolium’s parser handles both.

Frontmatter (lowercase YAML, piolium-style)

---
id: p10-001
phase: P10
source-draft: p4-001
slug: direct-git-url-ref-reaches-simple-git-clone
severity: high
verdict: VALID
debate: piolium/chamber-workspace/c01-git-and-lock-command/debate.md
---
PoC-Status: executed
Protocol: local
Auth-Required: no

# Direct git URL/ref reaches vulnerable simple-git clone boundary

## Summary


## Location
- Source: `src/add.ts:895-942`
- Sink:   `src/git.ts:25-62`

## Impact


## Evidence

The frontmatter parser is case-insensitive on field names: archon’s Phase/Severity-Final and piolium’s phase/severity both work. Piolium’s single severity field is mapped onto archon’s SeverityFinal slot so the existing “prefer final over original” resolution still applies. For a full schema see archon-audit’s Finding Format section — both harnesses share it.

Finding Ingestion

When the audit completes, findings are automatically parsed and stored in the Vigolium database.

Database fields

Piolium fieldDatabase fieldExample
Finding IDmodule_idpiolium:p10-001
Title (from H1 or slug)module_nameDirect git URL reaches simple-git clone
Slugmodule_shortdirect-git-url-ref-reaches-…
Severity (final)severityhigh (normalized)
Verdictconfidencefirm (CONFIRMED/VALID) or tentative
CWEcwe_idCWE-918
Full bodydescriptionMarkdown with evidence
First locationsource_filesrc/add.ts
All locationsmatched_atsrc/add.ts:895-942
Metadatatags["piolium", "phase-P10", "valid", "poc-executed", "CWE-918"]
All findings are stored with:
  • finding_source: piolium
  • module_type: whitebox
  • finding_hash: MD5(auditID + moduleID + findingID) for deduplication
The associated AgenticScan row carries:
  • mode: piolium
  • agent_name: pigolium
  • protocol: pi-sdk
  • input_type: piolium

Querying piolium findings

# Via CLI
vigolium finding list --source piolium

# Via API
GET /api/findings?source=piolium

# Combined with archon
vigolium finding list --source piolium,archon

Manual import

Piolium output from external runs imports through the same path as archon — the parser detects the findings/p<phase>-<seq>-<slug>/ directory layout and tags rows correctly:
vigolium import /path/to/piolium-output/
The folder must contain audit-state.json and either findings/ or findings-draft/.

Comparison: piolium vs archon

Aspectvigolium agent archonvigolium agent piolium
RuntimeClaude Code, Codex, or OpenCodePi (pi CLI + piolium extension)
Install modelEmbedded harness, auto-extracted at runtimeUser installs piolium via pi install
Slash command/archon-audit:archon:<mode>/piolium-<mode>
Output folder<source>/archon/<sessionDir>/archon-audit/<source>/piolium/<sessionDir>/piolium-audit/
Env prefixARCHON_*PIGOLIUM_*
Session-flag prefixn/a--plm-* (passthrough on the pi argv)
Modeslite/balanced/deep/revisit/confirm/merge/diff/status/mocklite/balanced/deep/revisit/confirm/merge/diff/longshot/status/smoke
Cost sourceclaudecost (audit-stream.jsonl), codexcost (~/.codex/sessions)picost (~/.pi/agent/sessions)
DB taggingmode=archon, module_id=archon:…mode=piolium, module_id=piolium:…
Audit-state.jsonSame schema (snake_case keys, interchangeable)Same schema
Finding formatSame (YAML frontmatter + cold-verify overlays)Same
Promoted-findings layoutfindings/C1-<slug>/ (severity-prefixed)findings/p<phase>-<seq>-<slug>/ (phase-prefixed)
Background-during-scanYes — --archon flag on swarm/autopilotNo — foreground subcommand only
The two are intentionally interoperable. You can run a piolium audit and import the output into a project that has archon-tagged findings; the parser, finding deduplication, and reporting tooling all apply uniformly.

Comparison with Native Scanning

AspectVigolium Native (Swarm/Autopilot)Piolium
FocusNetwork vulnerabilities (injection, XSS, SSRF, etc.)Source code vulnerabilities (logic flaws, auth gaps, spec violations)
MethodLive HTTP scanning with payloadsStatic analysis + AI reasoning + adversarial validation
False positive handlingAI triage phaseMulti-layer: adversarial debate chambers + cold verification (deep mode)
Finding richnessStandard severity/confidenceAdversarial verdicts, cold-verify overlays, CWE, PoC status
SpeedMinutes to hoursMinutes (lite, ~1h) to hours (deep, ~6h)
RequiresTarget URLSource code path
Runs asForeground (main pipeline)Foreground subcommand
The two approaches are complementary. Network scanning finds vulnerabilities that manifest in HTTP responses; piolium finds vulnerabilities that require understanding code semantics, business logic, and specification compliance. Running both — vigolium agent swarm -t https://… against a deployed instance, then vigolium agent piolium --source ./repo against the source — provides the most comprehensive assessment.

End-to-End Tests

The piolium audit path has an opt-in e2e test at test/e2e/piolium_audit_e2e_test.go that runs pi --mode json -p "/piolium-lite" against a tiny synthetic Python fixture and asserts that the session artifacts, DB row, and JSONL stream all materialize.
# Default (uses whatever pi has configured as defaultProvider/defaultModel)
make test-e2e-piolium

# Override the provider/model for the run
VIGOLIUM_E2E_PI_PROVIDER=vertex-anthropic \
VIGOLIUM_E2E_PI_MODEL=claude-opus-4-6 \
  make test-e2e-piolium

VIGOLIUM_E2E_PI_PROVIDER=google-vertex \
VIGOLIUM_E2E_PI_MODEL=gemini-3.1-pro \
  make test-e2e-piolium

# Keep the session dir for inspection
VIGOLIUM_E2E_PI_KEEP=1 \
  make test-e2e-piolium

# Custom timeout (defaults to 5m)
VIGOLIUM_E2E_PI_TIMEOUT=10m make test-e2e-piolium
Environment variables consumed:
VariableDefaultEffect
VIGOLIUM_E2E_PI_PROVIDER(pi’s defaultProvider)Sets --pi-provider for the test run
VIGOLIUM_E2E_PI_MODEL(pi’s defaultModel)Sets --pi-model for the test run
VIGOLIUM_E2E_PI_TIMEOUT5mAudit hard timeout (Go duration syntax)
VIGOLIUM_E2E_PI_KEEPunsetWhen 1, keeps the session dir on disk after the test exits
The test skips automatically when pi isn’t on $PATH or piolium isn’t registered in ~/.pi/agent/settings.json. There’s also a non-skipping companion subtest, TestE2EPioliumAudit_PreArgsRoundtrip, that proves the argv builder produces the exact pi --provider X --model Y --mode json -p /piolium-<mode> shape for each provider — useful for catching flag-plumbing regressions in CI without needing live credentials.