Skip to content

v0.2.0

Choose a tag to compare

@espetro espetro released this 14 Sep 22:47
· 184 commits to main since this release

[0.2.0] - 2026-09-15

Added

  • Search log: every search (HTTP and MCP) writes a search_log row (query text + hash, cache/network source, result count, duration in ms, client) to the cache database, pruned by OXE_SEARCH_LOG_RETENTION_DAYS (default 30).
  • oxe stats CLI subcommand: builds a static stats dashboard (index.html, six inline-SVG panels, zero JavaScript) from the search log. Opens the SQLite file read-only, so it is safe against a live WAL database. Flags: --db, --out, --days.
  • GET /search?q=... with content negotiation: browsers get the rendered results page, Accept: application/json clients get Exa-shaped JSON from the same URL. Includes a share row (canonical link, copy JSON, copy link) and result paging via &p=.
  • /?q=X now redirects to /search?q=X, so .../search?q=%s can be registered as a browser search engine.
  • make_app(cache=None, on_result=None) factory in oxe.server for embedding: bring your own cache, or attach an on_result observer called with every search payload.
  • Result cards are mirrored server-side (favicon + domain, title, snippet, collapsed page-text preview), so /search?q= renders even before JavaScript runs.

Changed

  • Page templates extracted from Python string literals in oxe/ui.py to oxe/static/*.html, loaded via importlib.resources (packaged as package data).
  • Web UI reworked to a Google-style results anatomy per the screen specs in .agents/docs/screens/.
  • Search-log latency: do_search(..., with_duration=True) returns (payload, duration_ms); the MCP tool uses it to record real network latency.

Fixed

  • MCP exa_search search-log rows recorded duration_ms = NULL on every network search because the duration was only attached to the observer copy of the payload. Latency is now returned explicitly and logged for both HTTP and MCP searches.
  • Duplicate panel_client_split_rows definition (one with an incomplete body) removed from oxe/stats.py.