leviath-cli 0.1.1

Command-line interface for Leviath agent framework
Documentation
[agent]
name = "log-analyzer"
version = "0.0.1"
description = "Analyzes log files - identifies anomalies, trends, and error patterns via a scripted analyze⇄script loop, maintaining a severity-ranked findings index"
entry_stage = "ingest"

[tool_permissions]
read_file = "allow"
list_dir  = "allow"
bash      = "ask"
write_file = "ask"

# ─── Stage 1: Ingest ──────────────────────────────────────────────────────────
[stages.ingest]
mode = "autonomous"
model = { models = [{ provider = "anthropic", model = "claude-sonnet-5" }, { provider = "openai", model = "gpt-5.4-mini" }, { provider = "ollama", model = "qwen3.5:9b" }] }
description = "Read log files, identify format and structure"
available_tools = ["read_file", "list_dir", "bash", "context_write"]
max_iterations = 15
system_prompt = """
Ingest the log files named in `task`. Determine:
1. Log format (JSON, syslog, Apache, custom, etc.)
2. Time range covered and total volume
3. Available fields/columns
4. Obvious structural issues (corrupted lines, encoding problems)

Use bash (head/wc/file) to sample rather than reading whole huge files. Write a
short format/structure summary to `findings` (context_write) so the analyze stage
starts oriented. Raw sampled content lands in `logs`.
"""

[stages.ingest.tool_routing]
default_region = "conversation"
[stages.ingest.tool_routing.overrides]
read_file = "logs"
bash      = "logs"

[stages.ingest.transitions.analyze]
hint = "Log format identified, ready to analyze"
transform = "direct"

[stages.ingest.transitions.error_recovery]
condition = "error"
transform = "direct"

# ─── Stage 2: Analyze ─────────────────────────────────────────────────────────
[stages.analyze]
mode = "autonomous"
model = { models = [{ provider = "anthropic", model = "claude-opus-5" }, { provider = "openai", model = "gpt-5.5" }, { provider = "ollama", model = "qwen3.6:27b" }] }
description = "Identify anomalies, trends, and error patterns"
available_tools = ["read_file", "bash", "context_write", "context_append"]
max_iterations = 25
max_revisits = 3
transition_prompt = """
Decide what happens next:
- If you need a script to parse/filter/aggregate the data more precisely, respond with: script
- If you have enough findings to produce a report, respond with: report
"""
system_prompt = """
Analyze the log data for:
1. Error patterns - recurring errors, error spikes, cascading failures
2. Anomalies - unusual request rates, latency outliers, unexpected status codes
3. Trends - gradual degradation, growing queue depths, memory creep
4. Correlations - errors that co-occur, time-based patterns
5. Security signals - auth failures, unusual access, injection attempts

Quantify everything (timestamps, counts, rates). For each finding, append to
`findings` (context_append): `<SEVERITY> | <finding> | <evidence: counts/timestamps>`
with SEVERITY in {critical, warning, info}. Keep a running tally in `severity_index`
(context_write), e.g. "critical: 2, warning: 5, info: 3". Use the `script` stage
when you need precise parsing/aggregation you can't eyeball.
"""

[stages.analyze.tool_routing]
default_region = "conversation"
[stages.analyze.tool_routing.overrides]
read_file = "logs"
bash      = "scratch"

[stages.analyze.transitions.script]
hint = "Need a script to parse/filter/aggregate log data"
transform = "compact"

[stages.analyze.transitions.report]
hint = "Analysis complete - ready to produce report"
transform = "compact"

[stages.analyze.transitions.error_recovery]
condition = "error"
transform = "direct"

# ─── Stage 3: Script ──────────────────────────────────────────────────────────
[stages.script]
mode = "autonomous"
model = { models = [{ provider = "anthropic", model = "claude-sonnet-5" }, { provider = "openai", model = "gpt-5.4-mini" }, { provider = "ollama", model = "qwen3.5:9b" }] }
description = "Write and execute scripts to parse, filter, and aggregate log data"
available_tools = ["read_file", "bash"]
max_iterations = 20
max_revisits = 5
transition_prompt = """
Your script has produced results. Decide what happens next:
- If you need to refine this script or write another, respond with: script
- If the results answer the analysis questions, respond with: analyze
"""
system_prompt = """
Write scripts (awk/grep/python via bash) to process the logs:
- Parse structured fields, filter by time/severity/pattern
- Aggregate counts, rates, percentiles
- Detect anomalies via statistical thresholds
- Correlate events across sources

Write small, focused scripts; test each before building on it. Script code and
output land in `scripts`. Iterate until the output answers the analysis questions,
then return to analyze.
"""

[stages.script.tool_routing]
default_region = "scripts"
[stages.script.tool_routing.overrides]
read_file = "logs"

[stages.script.transitions.script]
hint = "Refine or write another script"
transform = "direct"

[stages.script.transitions.analyze]
hint = "Script results ready - return to analysis"
transform = "compact"

[stages.script.transitions.error_recovery]
condition = "error"
transform = "direct"

# ─── Stage 4: Report (terminal) ───────────────────────────────────────────────
[stages.report]
mode = "autonomous"
model = { models = [{ provider = "anthropic", model = "claude-opus-5" }, { provider = "openai", model = "gpt-5.5" }, { provider = "ollama", model = "qwen3.6:27b" }] }
description = "Produce a structured analysis report with findings"
available_tools = ["write_file", "read_file"]
max_iterations = 10
# Generating the final report - don't inject mid-run user messages into it.
accepts_messages = false
system_prompt = """
Write a structured log analysis report (write_file) from the `findings` region,
structured by the `severity_index` tally. Include:
- Executive summary (top findings in 3-5 bullets)
- Log source overview (files, format, time range, volume)
- Findings by severity (🔴 critical / 🟡 warning / 🔵 info)
  - For each: description, evidence (counts/timestamps), impact
- Trends and patterns observed
- Recommendations (what to fix, what to monitor)
- Appendix: key log excerpts and script outputs

Be precise - timestamps, counts, percentages, specific log lines. The report's
severity counts must match `severity_index`.
"""

# Explicit empty transitions = terminal.
[stages.report.transitions]

# ─── Stage 5: Error recovery ──────────────────────────────────────────────────
[stages.error_recovery]
mode = "autonomous"
model = { models = [{ provider = "anthropic", model = "claude-sonnet-5" }, { provider = "openai", model = "gpt-5.4-mini" }, { provider = "ollama", model = "qwen3.5:9b" }] }
description = "Recover from a failed read or script execution"
available_tools = ["read_file", "bash"]
max_iterations = 10
max_revisits = 1
system_prompt = """
A read or script execution failed. Read the error from context, diagnose it
(a malformed log line, a script bug, a missing file), note a workaround, then
return to analyze to continue with the data available.
"""

[stages.error_recovery.transitions.analyze]
hint = "Recovered - resume analysis"
transform = "compact"

[compaction]
provider = "anthropic"
model = "claude-sonnet-5"

# ─── Context layout ───────────────────────────────────────────────────────────
[context.regions]
task            = { kind = "pinned", budget = "2%", max_tokens = 3000, required = true, seed = "task", required_message = "Name the log file(s)/dir and what to look for via --task." }
severity_index  = { kind = "pinned", budget = "2%", max_tokens = 2000 }
findings        = { kind = "sliding_window", max_items = 30, budget = "12%", max_tokens = 15000, strategy = "bulk", overflow = 10 }

logs            = { kind = "temporary",       budget = "30%", max_tokens = 50000 }
scripts         = { kind = "compacting",      budget = "20%", compact_at = "80%", threshold_tokens = 20000, max_tokens = 30000 }
scripts_history = { kind = "compact_history", source_region = "scripts", budget = "3%", max_tokens = 10000 }

conversation    = { kind = "sliding_window", max_items = 30, budget = "12%", max_tokens = 15000, strategy = "bulk", overflow = 10 }
scratch         = { kind = "clearable", budget = "6%", max_tokens = 6000 }