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.
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, eight drivers (openai-codex-oauth, anthropic-api-key, anthropic-oauth, openai-api-key, anthropic-cli, 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:- Append the user prompt to history.
- Stream a single provider response (text deltas, thinking deltas, tool calls).
- Append the assistant turn to history; emit
EventTurnDonewith token usage. - If there are no tool calls → emit
EventRunDoneand exit. - 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.
- Loop back to step 2, capped by
MaxTurns(default 32 for chat / headless, 200 for autopilot).
- Tool result truncation / spill: results larger than
MaxToolResultBytes(default 16 KiB) get head+tail truncation with an elision marker. IfSpillDiris 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 canread_file. - Per-tool timeout: each tool invocation gets its own deadline (default 5 minutes). A runaway
bash curlcan’t hang the whole session. - Prompt caching: opt-in via
EnablePromptCache. Autopilot turns it on; the Anthropic providers writecache_control: ephemeralmarkers and the Codex OAuth provider writesprompt_cache_keyheaders, cutting repeated-prefix tokens by ~90 % across long runs.openai-api-key,openai-compatible(Ollama / OpenRouter / LM Studio / vLLM / Groq / …), andgoogle-vertexdo not emit cache markers, so the flag is silently ignored for them. - Skills: when a registry is loaded the engine injects an
<available_skills>block into the system prompt at construction, and registers aload_skilltool the model can call to fetch a skill body on demand.
Modes
Interactive TUI (default)
/:
/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 toload_skill.
One-shot non-interactive
Passing-p / --prompt runs a single prompt non-interactively and streams to stdout, the TUI is skipped automatically.
[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
Eight drivers inpkg/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 |
anthropic-cli | (none, subprocess) | claude-opus-4-7 | --claude-bin (default claude on $PATH) |
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) |
--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 toagent.olium.*invigolium-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 acceptsaudit_auth/piolium_authfor 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. |
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 undervigolium 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 whenRepois non-nil).
Skills
Skills are Markdown workflow files with YAML frontmatter, following the agentskills.io convention so files written for Claude Code or pi work in olium verbatim. Format:name must match [a-z0-9-]+ (≤64 chars); description ≤1024 chars.
Discovery
The skill registry walks four scopes, first-found-by-name wins:- Project:
.agent/skills/and.claude/skills/in the working directory and every ancestor, closest first. - User:
~/.vigolium/skills/(only whenIncludeUserSkills=true). - Embedded: shipped in the binary under
public/presets/skills/viago:embed.
<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
-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 fullagent.olium block:
agent.sessions_dir: where per-run session directories go. Default~/.vigolium/agent-sessions/.agent.browser: togglesagent-browserintegration (the binaryweb_fetchshells out to inmode: 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 underagent.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’sSpillDiris 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).
vigolium agent session list / --full / --tail.
Stream events
The engine emits a unifiedEvent 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 |
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 |
See also
- Agent Mode, the full agent subcommand map.
- Autopilot, autonomous scan mode built on the olium engine.
- Swarm, AI-guided multi-phase scan that drives the native scanner.
- How It Works, provider list and the high-level dispatch story.
