Changelog¶
Only user-visible changes are listed here. For the full commit log see the gitea repo (internal) or the GitHub mirror for public releases.
v0.26.0 — Silent behaviour made visible; verified provider layer¶
The theme is telling you what happened. An import that discarded 74% of command
outputs said nothing; an auto-export reached you only if the model chose to
mention it; olav doctor passed 9/9 on an install whose every query path was
dead.
Features¶
olav doctorgrew from 9 to 11 checks.authnames the mode and the provider that will serve it — a clean install could pass 9/9 with every query path dead, becauseinithad writtenauth.mode=tokenwhose provider ships in olav-ent.contextcompares the agent's context budget against the window your server actually advertises, which is what turns intoagent error, code 400on a long run.olav doctor --verifyproves a service works rather than merely answers, and each registered service contributes its own readiness check.- Import report. Ingest writes
exports/import_reports/<snapshot>.mdnaming how much parsed, what did not, and why —raw_only,no_parser_registered,parser_no_match, or a parser error. On a 339-device dataset, 8,761 command outputs produced 2,278 structured rows; that 74% was previously invisible. Where thelearnercould close a gap the report says so and stops — it never runs it, because that would turn a two-minute import into a long one. - Query exports are announced. When
execute_sqlcaps rows to fit the model's context and writes the full result to a file, the CLI states the row count, the path, and how much you were actually shown. - Provider support is now measured, not assumed. Each provider has a first-class driver with its own function-call determinism defaults, pinned by an offline check on the exact request it receives plus an opt-in live call.
Fixes¶
- Queries selecting stored config text failed with
'str' object has no attribute 'items'. A per-field size budget was being applied to whole rows, so any wide row stopped being a row. olav initno longer overwrites an auth mode you chose. It only selects a mode on a config it created in that run.- Claude rejected requests with a 400 when temperature exceeded 1.0, which is its documented ceiling. Temperature is now clamped per provider.
- Memory capture ran twice per query, costing a second LLM round-trip and writing duplicate memories.
- A repeated query left duplicate CSVs. Exports are content-addressed, so an identical result set reuses its file.
devices.rolestayed empty when no inventory supplied it; it is now derived from the hostname where the name says so.- Two co-located deployments could adopt each other's containers — compose project names are namespaced.
Removed¶
- Mistral provider support and its
[mistral]extra. The setup wizard was offering a provider whose driver a plain install did not ship.
v0.25.1 — Packaging fix: authored workspace files now ship¶
Fixes¶
- 16 authored workspace reference files were never packaged. Guides and references that shape agent behaviour existed in the repo but not in the wheel, so a pip install behaved differently from a source checkout.
render_reportinitialised an LLM unconditionally, so a report that needed no model still required one to be reachable.
Changed¶
- Developer-facing text in the workspace prompt surface is English-only, so a mixed-language prompt no longer reaches the model.
v0.25.0 — Tiering: team features move to olav-ent¶
Changed¶
- The web API and WebGUI moved to olav-ent. The open-source package is the CLI, runtime and agents; team features (multi-user auth, the web surface) are the enterprise extension.
auth.mode=nonegrants the local OS user admin rights. A single-operator install needs no login at all.- A plain install is smaller —
fastapiand theldap3extra are gone from the OSS dependency set.
Features¶
- One write path to DuckDB (
open_write_connection), so concurrent writers no longer corrupt state or race each other.
v0.24.1 — Audit and export fixes¶
Fixes¶
- Audit postcheck prompts suggested a command that does not exist
(
olav -a ops); they now name a real agent. - Batfish export falls back to built-in per-platform backup commands when a device has no stored config, instead of exporting nothing.
v0.24.0 — Loop protection, declarative workflows, honest doctor¶
Features¶
- Multi-step tasks can be declared rather than improvised: known-shape work routes across sub-agents by declaration.
- A zero-LLM circuit breaker on every agent. A model repeating the same tool call is stopped by Python, not by asking the model to notice.
olav doctornames the backends it found — model and endpoint for the LLM, the same for embeddings — so a misconfiguration is visible without a run.- Per-script timeouts can be declared in
SKILL.md.
Fixes¶
- The first-run wizard rejected the numbered choice it had just printed in the embedding menu.
- API-mode embeddings read a config key that did not exist, so an endpoint you configured was silently ignored.
deploy_servicereported healthy when a container had already exited —docker compose pswas being read without-a.- A single broken audit view failed the whole audit; jobs now degrade individually.
v0.23.1 — Metadata¶
Changed¶
- Project URLs point at
https://olavai.comand the documentation site; author and copyright metadata unified.
v0.23.0 — Self-reflection, scheduled jobs, recall benchmark¶
Also carries the changes internally tagged 0.22.2, which was never published.
Features¶
admin/reflector— a daily self-reflection pass that turns recurring error signatures into constraints the agents recall later.olav cron—enable/disable/listfor declared scheduled jobs, opt-in rather than implicit.olav kb bench— measures whether the knowledge base can actually recall what was stored in it, without domain assumptions.olav doctorgrew from 3 to 8 zero-LLM checks, adding agents, tools and scaffolding.draw_topology— a one-shot draw.io or Mermaid topology from the writer agent.execute_sqlcaps per-cell text, so one 70 KB config column can no longer consume the model's whole context.- Batfish self-service — the simulator diagnoses a missing Batfish and offers to deploy it instead of failing.
Fixes¶
- Scheduled jobs silently did nothing — their log redirect pointed at a directory that did not exist.
register_servicewrote auth keys the reader never looked for, so a registered credential was ignored.- NetBox 4.5 — Bearer
nbt_tokens and the/apiprefix are supported. olav doctor --jsonemitted invalid JSON, because rich wrapped a value mid-line.- A slow local model was reported as unreachable rather than slow.
v0.22.1 — Local embedding endpoints¶
Fixes¶
- Ollama, llama.cpp and vLLM embedding endpoints work as configured; the client no longer assumes an OpenAI-shaped response.
v0.22.0 — Zero-ritual onboarding¶
Features¶
- Bare
olavin a fresh directory sets itself up — no init ceremony before the first question. olav doctorand the TUI/doctor— a deterministic, zero-LLM health check you can run before blaming the model.- A welcome screen that reports real findings from your workspace instead of rotating tips.
- Conversational self-configuration — the admin agent can edit its own configuration under approval.
- Undo for workspace writes and cron changes.
v0.21.1 — Per-skill LLM overrides, KB source-tier lifecycle, NetBox CSV sync¶
Status: in progress (post-rc4, prepping for tag).
Features¶
- Per-skill LLM overrides. Each
SKILL.md/AGENT.mdmay now carry an optionalllm:block with any subset ofmodel,temperature,max_tokens,base_url,model_provider,num_ctx,num_predict. Missing keys fall through to the globalapi.json.llm.*default. No profile indirection — single fall-through chain. Used in-tree by: sim,investigate,learner→temperature: 0.0(deterministic output)core/memory_curator→max_tokens: 4096(R102 HITL is short)analyzer(no override) — inherits global 32K- Global
max_tokensraised 16000 → 32768. gemma4:31b nothink can produce long structured tables/reports without truncation. Override per-skill if needed (memory_curator already does the opposite). - Source-tier KB lifecycle (
dev_docs/79). Every*.guide.yamlcarries asource_tierfield (vendor/platform/team/user). Priority + weight derived from the tier, not hand-set. - R102 conversational commits land at
source_tier: user(priority 3, weight 0.83) — operator-spoken rules can't outrank architectural invariants by accident - Schema v1 YAML still loads (default =
usertier with backward-compat for explicitpriority:field) - New CLI:
olav kb remove <intent> --reason "..."(tombstone YAML + LanceDB delete +kb_audit/<ts>_remove_<intent>.yamlrow),olav kb show <intent>,olav kb list-guides --tier <name> kb_audit/git-trackable audit chain. Every KB-mutating action (R102 commit,kb remove, NetBox push) drops onekb_audit/<iso-ts>_<verb>_<intent>.yamlrow withaction / intent / actor / timestamp / body_sha256+ outcome counters. Distinct fromaudit.duckdb— git-tracked so the change record survives independent of the runtime DB.olav userCLI. Friendly aliases forolav admin add-user/....olav user create <name>requires the Linux user to exist (pwd.getpwnam(name)) by default — OLAV's user model is aligned to the OS, no parallel identity. Token hash + salt stored, plaintext shown once. Override Linux check with--no-verifyfor containers.- Collection-time credential redaction (
olav.core.redaction.scrub, ADR-0008). Two-layer pipeline scrubs passwords / SNMP communities / BGP MD5 keys / IPSec PSK / TACACS / RADIUS shared secrets out of collected network config before it lands inaudit.duckdbor exported datasets, while keeping IP / hostname / ASN / topology intact so diff and diagnostic logic still work. - Collection layer:
olav.core.redaction.scrubwraps netconan (Batfish team, ~50 KB pure Python; 55 built-in sensitive-item regex + 5213 reserved words; per-workspace.redaction_saltchmod 600). Wired intonetops_init/run.py:_collect_cmd(collection boundary) andAuditEventRecorder.record_message(defense-in-depth for chat/LLM text). Replacement marker:netconanRemovedN. - Enterprise gate layer:
audit_recorder.redact_sensitive(5-pattern regex producing[REDACTED]) drives Layer 1 ofaudit_dataset_export.redact_audit_runand runs again at gate-check time as a final escape detector. - Configuration:
api.json.redaction.{enabled, anon_ip, extra_sensitive_words, extra_reserved_words}+OLAV_REDACTION=0env-var kill-switch. Install viapip install 'olav[redaction]'to pullnetconan>=0.13; without it OLAV still operates but Layer 1 fails open with a WARNING. - 16 governance tests in
tests/governance/test_redaction.pycover Cisco / Junos / salt management / fail-open semantics / idempotency. - NetBox CSV sync chain (
dev_docs/71Ch10). Three-phase workflow decoupling read (netops) from write (services) via a CSV intermediate: olav --agent netops "/export_netbox_csv"— produces NetBox-shaped CSV fromnetops.devices(column mapping innetops/guides/netbox_csv_export.guide.yaml)olav --agent services "/import_netbox_csv"— dry-run validates shape, writesexports/reports/netbox_import_dry_run.mdolav --agent services "/import_netbox_csv --write --endpoint ... --token ..."— actual POST to a running NetBox, 5-step idempotent lookup-or-create chain (site → manufacturer → device-type → device-role → platform → device); writeskb_audit/row per push
Fixes¶
- Audit tables populate from the CLI again. v0.20.3 flipped
OLAV_MIDDLEWARE_MODEdefault tomiddleware; the CLI invoke path never bound anOlavRunContexttoastream_events, soAuditMiddleware._get_context()returned None and silently no-op'd every tool call. Plusrecord_run_startwas called withoutthread_idso thesessionsUPSERT never fired. Both fixed: CLI now mirrors the API server's context-binding pattern, and athread_id=session_id or run_idis threaded through every turn. Net effect:audit_runs / sessions / audit_events / audit_messages / audit_tool_callsall populate after a single tool-using turn. reasoning_effort: minimalno longer sent. Ollama OpenAI-compat strictly validates this field and accepts onlyhigh / medium / low / max / none. Dropped — the siblingchat_template_kwargs.enable_thinking=falsealready handles the qwen3 / gemma4 nothink path.olav skill installrefuses to overwrite platform-owned workspaces. Domain-package installs (e.g.olav-netops) that carried a dev-mirror ofaudit/would silently clobber the platform's bundled audit/ onolav skill install. Now guarded by_PLATFORM_OWNED = {core, audit, services}skip list with a--force-overwriteescape./netops_initingest gate. Stage 4 now distinguishes "SSH collected" from "DB ingest" and prints❌ FAILED at the ingest stagewith non-zero exit when the staging JSON loaded 0 rows despite successful SSH collection. Previously printed✅ Network initialization completeeven when ingest erred.- 5 SQL view schema guides shipped (
netops/guides/sql_schema_*.guide.yaml) documenting BGP / OSPF / interface / route / device-and-topology view column names + worked examples. AutoRecall surfaces them on schema-discovery queries; gemma4 nothink lands first-try SQL instead of column-name trial-and-error. olav usersubparser added. Previously CLI had only the more verboseolav admin add-userpath;olav user create / list / token / revokeare the canonical user-facing form.- Platform audit workspace bundled into the wheel. Pre-v0.21.1
pip install olav==0.19.0 && olav initproduced only 2 agents (core+services); the audit/ workspace existed in dev but wasn't in the wheel's data path. Fresh install now ships 3 platform agents (audit / core / services).
Migration¶
- No flag-day. Schema v1
*.guide.yamlfiles continue to load, treated assource_tier: userwith a deprecation warning at prime time. Migrate at your leisure; the v2 form isschema_version: 2+ an explicitsource_tier:field. - R102 commits are demoted to
usertier. If you were relying on operator-spoken rules outranking platform guides, that no longer happens. Promote via git PR (edit the YAML tosource_tier: teamand commit through the normal review path). - Lab / CAB content moved out.
lab/sub-agent + CAB workflow + TCF schema demo + fault-injection scripts now live inolav-ent. The platform-base runsheet (dev_docs/71) drops former Ch7 + Ch8; the enterprise runsheet picks them up.
v0.21.0 — WebUI agent routing + service lifecycle hardening¶
Status: in progress (rc4 latest at time of writing).
Fixes¶
- Web GUI: agent dropdown now actually switches the running graph.
Pre-rc2 the FastAPI server kept a single
_agent_instancesingleton; clicking "ops" in the dropdown only re-tagged audit events but still executed the core graph.get_agent()is now a per-assistant_idcache; five agents coexist without cross-contamination. POST /reloadreturns 200 again. rc2 replaced the agent singleton with a cache dict but forgot to update the/reloadhandler; every reload raisedNameError. Handler now iterates the cache, best-effort- closes each agent, and clears state.- Audit in middleware mode sees run_id. v0.20.3 flipped
OLAV_MIDDLEWARE_MODEdefault tomiddleware; the web server never attached anOlavRunContexttoastream_events, soAuditMiddlewarehad norun_idto record against — every web request ran without audit. Fixed by wiring context through in both modes. - Thread + agent binding persists across page refresh. WebUI now
writes
currentThreadIdand each thread'sagentIdtolocalStorage; F5 restores state fully. corepinned first in/agents. Installing olav-netops adds 4 workspaces that all sort beforecorealphabetically, so the WebUI dropdown was auto-selectingauditas the default. Server now forcescoreto index 0.- Service lifecycle pins spawned processes to the workspace.
Pre-rc4
uvicorn/ the agent daemon / the syslog receiver derived their PID + log paths from a module-levelPROJECT_ROOTfrozen at module-import time. When olav was launched from inside another repo with apyproject.toml, the spawned processes silently inherited the wrong cwd and read the wrongusers.duckdb— every login 401'd. Resolution is now cwd-based, anchored on the.olav/marker, and re-resolved on every call;Popen(..., cwd=str(workspace_root))is passed explicitly. olav service start --allworks on a fresh wheel install. Not a bug — arg order isolav service <action> --all, which was always correct — but the demo runsheet examples have been re-verified.olav skill install <path>still works via a v0.21.0-rc4 deprecation shim. The singularskilldispatcher was removed in v0.20.3; the shim forwards verbatim toolav agent installand prints a one-line warning. See CLI reference for the migration note.
New in rc4¶
scripts/sync_netops_workspace.py— sync the platform-vendored.olav/workspace/ops/copy with the authoritativeolav-netops/.olav/workspace/ops/tree. Default direction: root → olav-netops;--reversepulls the other way;--checkis the CI drift guard.
v0.20.3 — deepagents migration complete (P7 final)¶
olav skill installsubcommand removed. Useolav agent install. (v0.21.0-rc4 adds a deprecation shim for back-compat; see above.)OLAV_MIDDLEWARE_MODEdefault flipped tomiddleware. The legacy callback plugins are still installable viaOLAV_MIDDLEWARE_MODE=callbackfor one release cycle whileAuditMiddlewareproves itself.parse_subcommand_argssweeping into 7 verbs. Shared helper for quoted/empty/--argparse edge cases.workspace_discovery.pyconsolidated. Three duplicate scanners merged into one; all consumers (CLI, web/agents, TUI) go through the same code path.
v0.20.2 — P1 + P2 + P6 cutover¶
- Native TUI (via
langgraph devsubprocess) is now default, replacing the in-process overlay.OLAV_TUI_MODE=overlayrolls back to the v0.19.x path for emergency cases. - Tool-loader auto-discovers
.deepagents/agents/<name>/tools/*.pyand.deepagents/agents/<name>/skills/<skill>/tools/*.pyat startup. - v0.20 workspace layout migration available via
olav migrate v0_20_layout(idempotent +--dry-run).
v0.20.1 — P3 middleware migration¶
AgentMiddlewaresubclasses for audit / guardrails / memory-capture / memory-recall / uks-capture, parallel with the callback plugin path.OLAV_MIDDLEWARE_MODEenv var selects.
v0.20.0 — P5 graph factory + olav agent install¶
olav agent install <path>introduced as the public install verb.olav skill installstill works (same dispatcher under the hood); this release is the start of the rename.graph_factory.build_graph()internal API for constructing the LangGraph perassistant_id— foundation for the per-id cache that landed in v0.21.0-rc2.
v0.19.0 — last pre-deepagents release¶
See DEMO_RUNSHEET (historical).