> ## 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.

# Olium Agent

> The in-process AI agent runtime that powers every agentic feature in Vigolium, interactive TUI, headless prompts, and the engine library used by autopilot, swarm, query, and vigolium-audit.

`olium` is the in-process AI agent runtime that powers every agentic feature in Vigolium. It ships as both:

* **A user-facing command**: `vigolium agent olium` (aliases: `vigolium olium`, `vigolium ol`), for interactive chat in a TUI or scripted one-shot prompts.
* **A library**: `pkg/olium/`, that the autopilot, swarm, query, vigolium-audit-prep, and source-analysis paths all dispatch through. There are **no subprocess SDK or ACP backends**; every AI call in vigolium goes through this engine.

For a higher-level comparison against the other agent subcommands, see [Agent Mode](/agentic-scan/agent-mode).

***

## What it is

A turn-based, tool-using LLM agent written in Go. Components:

| Layer              | Lives in                | Responsibility                                                                                                                                                                                                                               |
| ------------------ | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Engine**         | `pkg/olium/engine/`     | Multi-turn loop: provider stream → tool dispatch → history append → repeat                                                                                                                                                                   |
| **Provider**       | `pkg/olium/provider/`   | LLM backend, eleven drivers (openai-codex-oauth, anthropic-api-key, anthropic-oauth, openai-api-key, openai-responses, anthropic-cli, anthropic-claude-sdk-bridge, anthropic-compatible, anthropic-vertex, google-vertex, openai-compatible) |
| **Tools**          | `pkg/olium/tool/`       | The eight built-in primitives the model can call (bash, file ops, search, web fetch)                                                                                                                                                         |
| **Skills**         | `pkg/olium/skill/`      | SKILL.md workflow files (agentskills.io format) discovered from project, user, and embedded scopes                                                                                                                                           |
| **TUI**            | `pkg/olium/tui/`        | Bubble Tea front-end (inline scrollback, slash commands, live tool cards)                                                                                                                                                                    |
| **Headless**       | `pkg/olium/headless.go` | Non-interactive single-prompt runner for scripts and smoke tests                                                                                                                                                                             |
| **Autopilot**      | `pkg/olium/autopilot/`  | Long-running autonomous scan loop on top of the engine, with budgets, halt signal, and `report_finding`                                                                                                                                      |
| **Vigolium tools** | `pkg/olium/vigtool/`    | Scanner-aware extensions: `run_scan`, `run_extension`, `list_sessions`, `list_findings`, `auth_session_lookup`, etc.                                                                                                                         |
| **Auth**           | `pkg/olium/auth/`       | Codex OAuth credential loading and refresh (handles `~/.codex/auth.json`)                                                                                                                                                                    |

***

## What it does

Each invocation runs one **multi-turn loop**:

1. Append the user prompt to history.
2. Stream a single provider response (text deltas, thinking deltas, tool calls).
3. Append the assistant turn to history; emit `EventTurnDone` with token usage.
4. If there are no tool calls → emit `EventRunDone` and exit.
5. Otherwise dispatch the tool calls. If **all** calls are read-only the engine fans them out in parallel (cap = 8); otherwise it runs them strictly serially so writes can't race reads. Tool results are appended to history in the model's original order regardless.
6. Loop back to step 2, capped by `MaxTurns` (default **32** for chat / headless, **200** for autopilot).

Surrounding behavior:

* **Tool result truncation / spill**: results larger than `MaxToolResultBytes` (default 16 KiB) get head+tail truncation with an elision marker. If `SpillDir` is set (autopilot does this), the full payload spills to `<SpillDir>/tool-results/` and the model gets a head excerpt plus an on-disk path it can `read_file`.
* **Per-tool timeout**: each tool invocation gets its own deadline (default 5 minutes). A runaway `bash curl` can't hang the whole session.
* **Prompt caching**: opt-in via `EnablePromptCache`. Autopilot turns it on; the Anthropic providers write `cache_control: ephemeral` markers and the Codex OAuth provider writes `prompt_cache_key` headers, cutting repeated-prefix tokens by \~90 % across long runs. `openai-api-key`, `openai-responses`, `openai-compatible` (Ollama / OpenRouter / LM Studio / vLLM / Groq / …), `anthropic-compatible`, and `google-vertex` do not emit cache markers, so the flag is silently ignored for them. (`anthropic-claude-sdk-bridge` manages caching inside the Claude Code Agent SDK.)
* **Skills**: when a registry is loaded the engine injects an `<available_skills>` block into the system prompt at construction, and registers a `load_skill` tool the model can call to fetch a skill body on demand.

***

## Modes

### Interactive TUI (default)

```bash theme={null}
vigolium olium                     # chat
vigolium ol                        # alias
vigolium agent olium               # full path
vigolium ol "audit this repo"      # auto-submitted first prompt
echo "summarise" | vigolium ol     # stdin auto-detected when piped
```

Bubble Tea inline mode (no alt-screen, output appends to scrollback as it streams). Live partial line, fenced code-block highlighting via chroma, and a one-line "tool exec" card while a tool runs. Slash chooser opens on `/`:

* `/clear`: clear conversation history.
* `/skill:<name> [args]`: inline expansion of a loaded skill; the body is pasted into the prompt so the model doesn't have to spend a tool call to `load_skill`.

The model id, provider, and reasoning effort are shown in the banner header.

### One-shot non-interactive

Passing `-p` / `--prompt` runs a single prompt non-interactively and streams to stdout, the TUI is skipped automatically.

```bash theme={null}
vigolium ol -p "list every route in this repo"
```

Prints assistant text to **stdout**; thinking deltas, tool start/end cards, and per-turn `[turn done in= out= cached=]` summaries go to **stderr**. Exits non-zero on engine error.

### Library use (autopilot, swarm, query)

`pkg/agent/olium_adapter.go` is the single dispatch path every other agent feature funnels through:

* `runOliumPrompt(ctx, cfg, prompt, streamWriter, sourcePath)`: fresh engine per call.
* `runOliumOnEngine(ctx, cfg, eng, prompt, streamWriter)`: reuses an engine so the conversation prefix stays warm (used by source-analysis to fork an explore phase into 3 parallel format calls).
* `acquireProviderSlot(ctx, cfg)`: global semaphore (size = `agent.olium.max_concurrent`, default 4) that bounds in-flight provider calls process-wide so swarm phase fan-out can't trigger 429s on tier-1 plans.
* `EffectiveCallTimeout()`: default 10 min per provider call; 0 → default, negative → no timeout.

***

## Providers

Eleven drivers in `pkg/olium/provider/`. The provider ID is vendor-first so it's obvious which credential field applies:

| Provider                        | Auth                             | Default / typical model        | Source of credential                                                                                                                                                                                                           |
| ------------------------------- | -------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `openai-codex-oauth`            | OAuth credential file            | `gpt-5.5`                      | `--oauth-cred` → `agent.olium.oauth_cred_path` → `~/.codex/auth.json` (produced by `codex login`)                                                                                                                              |
| `anthropic-api-key`             | `x-api-key` header               | `claude-opus-4-7`              | `--llm-api-key` → `agent.olium.llm_api_key` → `$ANTHROPIC_API_KEY`                                                                                                                                                             |
| `anthropic-oauth`               | Bearer token (Claude Code OAuth) | `claude-opus-4-7`              | `--oauth-token` → `agent.olium.oauth_token` → `$ANTHROPIC_API_KEY` (produced by `claude setup-token`)                                                                                                                          |
| `openai-api-key`                | `x-api-key` header               | `gpt-5.5`                      | `--llm-api-key` → `agent.olium.llm_api_key` → `$OPENAI_API_KEY`                                                                                                                                                                |
| `openai-responses`              | `x-api-key` header               | `gpt-5.5`                      | `--llm-api-key` → `agent.olium.llm_api_key` → `$OPENAI_API_KEY`; public OpenAI Responses API (`/v1/responses`) instead of Chat Completions                                                                                     |
| `anthropic-cli`                 | (none, subprocess)               | `claude-opus-4-7`              | `--claude-bin` (default `claude` on `$PATH`); alias `anthropic-claude-cli`                                                                                                                                                     |
| `anthropic-claude-sdk-bridge`   | (none, subscription)             | Claude Code default            | Drives Claude Code via the `vigolium-audit bridge` sidecar using your logged-in subscription; `--bridge-bin` → `agent.olium.bridge_binary` → embedded blob → `vigolium-audit` on `$PATH`                                       |
| `anthropic-compatible`          | optional bearer                  | via `custom_provider.model_id` | `custom_provider.base_url` (Anthropic Messages `/v1/messages` gateway or proxy), `custom_provider.api_key`, `custom_provider.model_id`, `custom_provider.extra_headers`                                                        |
| `anthropic-vertex`              | GCP SA JSON                      | `claude-opus-4-6`              | `agent.olium.oauth_cred_path` (or `$GOOGLE_APPLICATION_CREDENTIALS`) + `google_cloud_project` / `google_cloud_location`; routes `claude-*` models to publishers/anthropic                                                      |
| `google-vertex`                 | GCP SA JSON                      | `gemini-2.5-pro`               | Same GCP creds as `anthropic-vertex`; routes `gemini-*` models to publishers/google                                                                                                                                            |
| `openai-compatible` *(default)* | optional bearer                  | `gemma4:latest` (Ollama)       | `custom_provider.base_url` (required), `custom_provider.api_key` (optional), `custom_provider.model_id`, `custom_provider.extra_headers` (Ollama / OpenRouter / LM Studio / vLLM / Together / Groq / LocalAI / custom proxies) |

With no `--provider` flag and no YAML override, vigolium defaults to **`openai-compatible`** with `gemma4:latest` against a local Ollama endpoint, so a freshly initialized config works out of the box without any cloud credentials. The `anthropic-oauth` provider also prepends a Claude Code preamble to the system prompt and adds the `oauth-2025-04-20` beta header so it's accepted on the same endpoint as `anthropic-api-key`.

Codex auth refreshes itself: it parses the JWT, checks expiry with a 60 s skew, and posts to `/oauth/token` with the stored refresh token, rewriting `~/.codex/auth.json` (mode `0o600`).

> **Note:** the REST API falls back to `agent.olium.*` in `vigolium-configs.yaml` (which keeps warm sessions and prompt caches stable across requests), but every agent run endpoint also accepts per-request BYOK credentials (`api_key`, `oauth_token`, `oauth_cred_file`, `oauth_cred_json`). The audit dispatcher additionally accepts `audit_auth` / `piolium_auth` for per-driver overrides.

***

## Tools

Built-in tool registry, eight tools registered in this order:

| Name         | Read-only? | What it does                                                                                                                                                                                                       |
| ------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `bash`       | no         | `bash -lc <cmd>` with hard-rejects for catastrophic patterns (`rm -rf /`, `dd` to block devices, fork bombs, `mkfs` against real devices). Default timeout = engine `ToolTimeout` (5 min).                         |
| `read_file`  | yes        | Read file with line-number prefix. Params: `path`, `offset`, `limit` (default 2000).                                                                                                                               |
| `write_file` | no         | Create or overwrite a file.                                                                                                                                                                                        |
| `edit_file`  | no         | Find-and-replace edit on a file.                                                                                                                                                                                   |
| `ls`         | yes        | List a directory.                                                                                                                                                                                                  |
| `grep`       | yes        | Regex search, uses ripgrep when available, else native Go regex. Params: `pattern`, `path`, `glob`, `max_matches` (200), `ignore_case`.                                                                            |
| `glob`       | yes        | Glob pattern → paths.                                                                                                                                                                                              |
| `web_fetch`  | yes        | Fetch a URL. Two modes: `http` (default, fast) and `browser` (delegates to `agent-browser` for SPA / JS-heavy pages). Params: `url`, `method`, `headers`, `body`, `max_bytes`, `mode`, `wait_selector`, `wait_ms`. |

The `IsReadOnly()` flag is what the engine uses to decide whether to fan out a turn's tool calls in parallel. `bash` runs **without an approval prompt** (yolo mode), only the catastrophic-pattern guard prevents disasters.

### Autopilot adds more

When the engine runs under `vigolium agent autopilot`, the registry also gets:

* `halt_scan`: model-driven exit. Sets a halt signal; the run loop exits after the current turn.
* `report_finding`: persists a finding to the database (title, severity, description, remediation, CWE, evidence, confidence, status). Soft-warns at 50 calls, hard-caps at 200.
* `load_skill`: fetch a skill body by name (registered whenever the skill registry is non-empty).
* **Vigtool**: `run_scan`, `run_extension`, `list_sessions`, `get_session`, `list_findings`, `list_auth_sessions`, `auth_session_lookup` (registered when `Repo` is non-nil).

***

## Skills

Skills are Markdown workflow files with YAML frontmatter, following the [agentskills.io](https://agentskills.io) convention so files written for Claude Code or pi work in olium verbatim. Format:

```markdown theme={null}
---
name: triage-finding
description: Walk a candidate finding from suspicious response → root cause → PoC.
license: optional
allowed-tools: optional list
---

# Body
Instructional prose the model reads after calling load_skill.
```

`name` must match `[a-z0-9-]+` (≤64 chars); `description` ≤1024 chars.

### Discovery

The skill registry walks four scopes, first-found-by-name wins:

1. **Project**: `.agent/skills/` and `.claude/skills/` in the working directory and every ancestor, closest first.
2. **User**: `~/.vigolium/skills/` (only when `IncludeUserSkills=true`).
3. **Embedded**: shipped in the binary under `public/presets/skills/` via `go:embed`.

Two on-disk layouts are accepted: `<root>/<name>/SKILL.md` (directory skill, the agentskills.io standard) or `<root>/<name>.md` (single-file shorthand; frontmatter `name` must match the filename stem).

Generic chat (`vigolium agent olium`, headless) loads scopes 1 + 3 only. Autopilot and swarm load all three so security-specific workflows in `~/.vigolium/skills/` don't pollute casual chat.

### Use

The engine writes an `<available_skills>` block into the system prompt listing every skill's name + description + location. The model fetches bodies on demand via the `load_skill` tool, progressive disclosure, so unused skills don't burn tokens.

In the TUI, type `/skill:<name> [args]` to inline-expand a skill body into your prompt directly, no tool call needed.

***

## CLI flags

```text theme={null}
--provider          openai-codex-oauth | anthropic-api-key | anthropic-oauth | openai-api-key | openai-responses | anthropic-cli | anthropic-claude-sdk-bridge | anthropic-compatible | anthropic-vertex | google-vertex | openai-compatible
--model             provider-specific (empty = provider default)
--oauth-cred        OAuth credential file (openai-codex-oauth; default ~/.codex/auth.json). Also the GCP SA JSON for the Vertex providers.
--oauth-token       Claude Code OAuth bearer token (anthropic-oauth)
--llm-api-key       API key for anthropic-api-key / openai-api-key / openai-responses
--claude-bin        Path to the `claude` binary (anthropic-cli)
--bridge-bin        Path to the vigolium-audit binary hosting the SDK bridge (anthropic-claude-sdk-bridge; empty = embedded blob, then PATH)
--base-url          Override custom_provider.base_url (openai-compatible / anthropic-compatible)
--system            Override the built-in system prompt
-p, --prompt        Initial prompt (alternative to a positional arg). Forces non-interactive mode.
--stdin             Force reading the prompt from stdin
```

Precedence for the initial prompt: positional args → `-p/--prompt` → stdin (auto-detected when piped, or forced with `--stdin`). Values flow CLI → YAML → env: every CLI flag falls back to its `agent.olium.*` YAML field, which in turn falls back to the documented default or env var.

***

## Configuration

The full `agent.olium` block:

```yaml theme={null}
agent:
  olium:
    provider: openai-compatible    # openai-codex-oauth | anthropic-api-key | anthropic-oauth | openai-api-key | openai-responses | anthropic-cli | anthropic-claude-sdk-bridge | anthropic-compatible | anthropic-vertex | google-vertex | openai-compatible
    model: gemma4:latest           # empty = provider default
    oauth_cred_path: ~/.codex/auth.json  # also GCP SA JSON for anthropic-vertex / google-vertex
    bridge_binary: ""              # anthropic-claude-sdk-bridge; empty = embedded blob, then PATH
    oauth_token: ""                # anthropic-oauth; supports ${ENV_VAR}; falls back to $ANTHROPIC_API_KEY
    llm_api_key: ""                # supports ${ENV_VAR}; falls back to $ANTHROPIC_API_KEY / $OPENAI_API_KEY
    google_cloud_project: ""       # Vertex providers; $GOOGLE_CLOUD_PROJECT wins
    google_cloud_location: ""      # Vertex providers; default us-central1
    reasoning_effort: medium       # minimal|low|medium|high|xhigh (codex)
    system_prompt: ""              # empty = built-in olium prompt
    custom_provider:               # used when provider == openai-compatible or anthropic-compatible
      base_url: http://localhost:11434/v1   # full chat-completions URL also works
      model_id: gemma4:latest               # fallback for olium.model
      api_key: ""                           # optional
      extra_headers: []                     # curl-style "Key: Value" entries
    max_tokens: 1000000
    temperature: 0.0
    max_turns: 32                  # short non-autopilot uses; autopilot uses its own cap (DefaultAutopilotMaxTurns=200)
    cache_size: 1024               # LRU; 0 disables
    max_concurrent: 4              # global cap on simultaneous provider calls; 0 = unbounded
    call_timeout_sec: 600          # per-call deadline; negative = no timeout (parent ctx only)
```

Adjacent config blocks worth knowing:

* `agent.sessions_dir`: where per-run session directories go. Default `~/.vigolium/agent-sessions/`.
* `agent.browser`: toggles `agent-browser` integration (the binary `web_fetch` shells out to in `mode: browser`).
* `agent.audit`: controls the optional vigolium-audit / piolium prep step that autopilot/swarm can stack ahead of the olium loop.

***

## Sessions and on-disk state

Every agent run gets a session directory under `agent.sessions_dir` (default `~/.vigolium/agent-sessions/<run-uuid>/`). Bare `vigolium agent olium` chat doesn't write a session, it's only autopilot/swarm/query that materialise one.

Inside a session dir you may find:

* `runtime.log`: per-turn event log (text deltas, tool start/end, turn-done summaries).
* `tool-results/<tool>-<call-id>.txt`: spilled oversized tool outputs (when the engine's `SpillDir` is set).
* `session-config.json`: run metadata (project / scan UUIDs, options).
* `swarm-plan.json`, `master-output.md`, `audit-stream.jsonl`, `checkpoint.json`, produced by the higher-level modes that wrap olium (swarm, vigolium-audit, autopilot).

Browse past runs with `vigolium agent session list` / `--full` / `--tail`.

***

## Stream events

The engine emits a unified `Event` channel regardless of provider:

| Event                                                               | Carries                                                                  |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `EventTextDelta`                                                    | `Delta`, assistant text increment                                        |
| `EventThinkingDelta`                                                | `Delta`, reasoning content (Anthropic thinking, codex reasoning)         |
| `EventToolCallStart`                                                | `ToolName`, `ToolArgs`, the model decided to call a tool                 |
| `EventToolExecStart` / `EventToolExecProgress` / `EventToolExecEnd` | tool invocation lifecycle, `ToolResult`, `ToolIsErr`                     |
| `EventTurnDone`                                                     | `StopReason`, `Usage` (input / output / cache-read / cache-write tokens) |
| `EventRunDone`                                                      | terminal usage                                                           |
| `EventError`                                                        | `Err`, provider failure, ctx cancellation, max-turns exceeded            |

Token counts on `EventTurnDone` are accumulated by every higher-level caller (autopilot for budget enforcement, the adapter for `agenttypes.TokenUsage`, the swarm for cost reporting).

***

## When to use what

| You want to...                                            | Use                                                                                   |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Chat / debug / explore interactively                      | `vigolium ol`                                                                         |
| Run one prompt from a script and parse stdout             | `vigolium ol -p "..."`                                                                |
| Hand the agent the wheel for an autonomous pentest        | `vigolium agent autopilot` (uses olium under the hood with budgets + report\_finding) |
| AI-direct the native scanner (plan → modules → triage)    | `vigolium agent swarm`                                                                |
| Single-shot template-driven prompt with structured output | `vigolium agent query`                                                                |

Olium itself is the **general-purpose** chat / dev surface and the engine every other mode reuses, it is not a security scan on its own.

***

## See also

* [Agent Mode](/agentic-scan/agent-mode), the full agent subcommand map.
* [Autopilot](/agentic-scan/autopilot), autonomous scan mode built on the olium engine.
* [Swarm](/agentic-scan/swarm), AI-guided multi-phase scan that drives the native scanner.
* [How It Works](/agentic-scan/how-it-works), provider list and the high-level dispatch story.
