Skip to content

Releases: ekalb81/agent-odometer

Odometer v0.8.19

Choose a tag to compare

@github-actions github-actions released this 10 Sep 16:42
Immutable release. Only release title and notes can be modified.
v0.8.19
6b02edd

What's new

  • Project controls: Move an individual session to an existing project, make it standalone, or restore its detected project from session details. Saved assignments refresh after startup scans, and collapsing a parent no longer hides sessions moved to another project. Headless reports use destination labels, and project budgets count usage under the assigned project.
  • Gemini CLI turn receipts: An optional AfterAgent hook shows completed-turn tokens and estimated cost. Enable Gemini separately in Settings; it remains off by default. Setup is reversible and reports hook health. Receipts require JSONL sessions from Gemini CLI 0.39 or later.
  • Updated model catalog: Added GPT-6 Astra, GPT-5.6 Cyber, Terra and Luna, Claude Fable 5.1, and Claude Mythos 5/5.1, plus verified aliases. Corrected bundled GPT-5.6 and Sonnet 5 pricing. Astra fast mode now applies the appropriate multiplier separately to Codex credits and API estimates.

Saved custom rate rows remain preserved. To replace those rows with the revised bundled rates, use Reset to shipped defaults in Settings.

Closes #40 and #41. See #229 for implementation and validation details.

Odometer v0.8.18

Choose a tag to compare

@github-actions github-actions released this 10 Sep 13:09
Immutable release. Only release title and notes can be modified.
v0.8.18
3832769

Installation

Download the installer for your platform:

  • Windows: the .msi (recommended) or .exe.
  • macOS (Apple Silicon): the .dmg. Right-click → Open on first launch (the app is not notarized yet).
  • Linux: the .AppImage, .deb, or .rpm.

Installed copies check for new releases on launch and offer a one-click update.

Consistent pricing across desktop and local reports

The desktop now uses the shared Rust pricing service for session, date-range, detail, and correlation costs. CLI reports and browser test fixtures use the same service. Frozen pricing cases cover models, service tiers, cache traffic, fallback rates, and unpriced usage (#217, #218, #219, #225).

Saved rate changes refresh prices in batches. Raw usage stays visible while replacement prices load, and older responses cannot overwrite newer results. This release also fixes two pricing defects found by conformance tests and preserves per-event accounting for detailed usage.

Local reports and MCP tools

New read-only commands cover sessions, models, projects, activity, workflow metrics, categories, tools, context, findings, diagnostics, quota observations, mirrored history, verification, and statusline output. Versioned exports support JSON, CSV, and Markdown. Reports share the desktop's query service, apply explicit limits and deadlines, redact paths by default, and report unavailable data honestly (#200, #202, #204, #206#212, #225).

A local MCP stdio server exposes the same reporting service with bounded requests, cancellation, and concurrency limits. It does not add network access or credential access. Availability tools remain usable when the local ledger is missing or needs preparation (#209, #225).

Settings and history improvements

  • Manage project aliases and merge related projects in Settings (#197).
  • Configure Gemini CLI session roots in Settings (#198).
  • Detect mirrored histories across providers and preserve thread names separately from session snapshots (#192, #207).
  • Reduce copying and write-batch size during scans, prevent history rebuilds from overlapping bulk scans, and rebuild only affected rollups (#183, #188, #193, #216).
  • Improve performance recordings for history rebuilds, scan locks, and rendered session rows (#190, #194, #195, #213).
  • Reject invalid rates during refresh and retain dated provenance for floating provider aliases (#191, #203).

This release also updates TypeScript, dependencies, and CI tooling.

Full Changelog: v0.8.17...v0.8.18

Odometer v0.8.17

Choose a tag to compare

@github-actions github-actions released this 15 Aug 21:05
Immutable release. Only release title and notes can be modified.
v0.8.17
f587f92

Installation

Download the installer for your platform:

  • Windows: the .msi (recommended) or .exe.
  • macOS (Apple Silicon): the .dmg. Right-click → Open on first launch (the app is not notarized yet).
  • Linux: the .AppImage (single file, no install) or .deb (Debian/Ubuntu).

Installed copies check for new releases on launch and offer a one-click update.

The startup memory spike is gone

For several releases Odometer briefly used around three gigabytes while starting, on top of the ~350 MB it settles at. Each attempt to find it looked in the wrong place, because every attempt assumed the memory was Odometer's own — the data it loads, the structures it keeps.

It never was. Loading history asks the database for sessions in a particular order, and nothing in the database was arranged to answer that quickly, so the database sorted the whole archive itself before handing back the first row. That sorting happened inside SQLite, in memory Odometer never allocated and therefore never saw when it looked.

This release adds the missing arrangement — one index. Measured on a 1.13 GB test archive:

  • peak memory during that phase: +287 MB → +0 MB
  • time to load history: 8.57 s → 3.79 s

The first row now arrives immediately rather than after the entire archive has been sorted, which is also why the loading phase is roughly twice as fast.

Your archive is upgraded once on first launch to add the index. On a realistic archive that took under four milliseconds — the table being indexed is a narrow one, and the bulky session contents live in a separate table the index never touches.

Rebuilding history now sticks

The "Rebuild history" action added two releases ago worked, and then the very next startup quietly undid it.

Rebuilding rewrote the archive but not the separate cache Odometer keeps of already-scanned transcripts. The scan that runs on the same startup then found that cache still holding the old, larger versions and wrote them straight back. On a real archive the reduction — 40% fewer bytes to load, and hydration nearly three times faster — survived exactly one launch before the archive re-inflated to where it started.

The rebuild now updates that cache as it goes, so the benefit persists. If you ran a rebuild on an earlier release and wondered why it seemed not to help, this is why.

A new model was being priced by fallback

Codex began reporting usage under gpt-daybreak-blue-latest, which Odometer did not recognise, so it showed a warning and used a fallback rate.

That alias currently points at a model Odometer already prices correctly, so the number was right the whole time — only its provenance was wrong. It is now mapped explicitly and the warning is gone.

One caveat worth knowing: OpenAI repoints these aliases as new models ship, and pricing follows whatever they point at. The mapping is accurate as of this release and will need revisiting when that happens.

Two things not yet proven in the field

The WAL cleanup after compaction, shipped last release, has still never actually run in any recording. Same for this release's archive upgrade against a genuinely large archive — it is measured on a test corpus, not yet on a multi-gigabyte one.

Both are expected to work. Neither has been watched doing so.

Full Changelog: v0.8.16...v0.8.17

Odometer v0.8.16

Choose a tag to compare

@github-actions github-actions released this 11 Aug 13:57
Immutable release. Only release title and notes can be modified.
v0.8.16
e4a43e0

Installation

Download the installer for your platform:

  • Windows: the .msi (recommended) or .exe.
  • macOS (Apple Silicon): the .dmg. Right-click → Open on first launch (the app is not notarized yet).
  • Linux: the .AppImage (single file, no install) or .deb (Debian/Ubuntu).

Installed copies check for new releases on launch and offer a one-click update.

Two bugs, both found by using the previous release

Rebuild history was leaving a huge temporary file behind

The rebuild added in v0.8.15 did shrink the archive — on a real 3.4 GB history it came down to 2.5 GB, and every future startup reads about a quarter less. But it also left behind a sidecar file the same size as the archive itself, so the space on disk went up by more than a gigabyte, and the app reported success.

The compaction step rewrites the database through that sidecar and then has to fold it back in. It never did. This release folds it in, and puts a ceiling on how large that file can get in ordinary use.

The reason nobody noticed is worth stating: the rebuild reported its result by measuring the database file alone, so the extra gigabyte was invisible to the very screen that was supposed to show you what happened. It now reports the total, sidecar included.

If you already ran a rebuild on v0.8.15, quitting and reopening the app reclaims the space — the fold-in happens on close. This release makes that automatic rather than something you have to know.

The heap number was wrong, sometimes wildly

Turning on heap tracking after the app had started produced a meaningless figure — either far too small, or occasionally an absurd one around eighteen quintillion bytes. The counter began at zero but still subtracted memory released from before it started counting.

It now counts allocations and releases separately and never subtracts below zero, so the impossible value cannot occur. More usefully, when the reading cannot be trusted — which is always the case if you switch tracking on mid-session — it now says so instead of showing a confident number.

For a reading you can actually rely on, turn tracking on and then restart, so counting begins with the app.

Nothing else changed

No change to how your history is stored, no migration, and no automatic rebuild.

Full Changelog: v0.8.15...v0.8.16

Odometer v0.8.15

Choose a tag to compare

@github-actions github-actions released this 11 Aug 04:05
Immutable release. Only release title and notes can be modified.
v0.8.15
e80866f

Installation

Download the installer for your platform:

  • Windows: the .msi (recommended) or .exe.
  • macOS (Apple Silicon): the .dmg. Right-click → Open on first launch (the app is not notarized yet).
  • Linux: the .AppImage (single file, no install) or .deb (Debian/Ubuntu).

Installed copies check for new releases on launch and offer a one-click update.

Rebuild history — a new action in Settings

The previous release taught Odometer to stop storing four out of every five quota snapshots, because they repeat the one before them and say nothing new. It worked, and on an established install it changed nothing at all: sessions already stored are never read again, so they keep the old bulky form indefinitely. On a large history that was 4,438 sessions out of 4,530 left untouched.

Settings → Rebuild history does the missing half. It re-reads every session from its original transcript, stores the compact form, then compacts the archive file itself — that second step matters, because a database does not shrink on its own when its contents do.

It asks first, and it is not automatic. It takes several minutes on a large history and it rewrites the whole archive, so it should be something you choose rather than something an update does to you.

Interrupting it is safe. Anything already rewritten stays rewritten, and a session whose transcript is no longer on disk keeps the copy Odometer already has rather than being touched — for those, the archive is the only remaining record. Both properties are covered by tests, including one that kills the process mid-rebuild.

When it finishes it reports what changed: stored snapshots, bytes, and the file size on disk. If it did nothing, you will be able to see that it did nothing.

Honestly stated: the parsing cost was measured, but compacting a multi-gigabyte archive and re-reading every transcript were not, and either could take longer than the parsing does. The first run is the measurement.

Startup now shows its work as it happens

Startup timings previously recorded one number per stage. That is enough to notice a stage got slower and useless for telling where the time went — a stage that runs slowly throughout and one that stalls in the middle look identical.

Odometer now samples itself continuously while those long stages run, and Diagnostics → Show live view displays what it finds while it is happening: memory in use, the current stage and how long it has been running, database sizes, and free disk space.

The sampling costs nothing measurable — the difference was smaller than the variation between repeated runs — and none of it exists unless performance tracking is on.

The live view is closed by default and does no work while closed. That is deliberate: a diagnostic panel that constantly asks the app how it is doing becomes part of the problem it was built to find.

Nothing else changed

No automatic rescan, no change to how your history is stored, and no migration on upgrade. Both features are things you turn on or click.

Full Changelog: v0.8.14...v0.8.15

Odometer v0.8.14

Choose a tag to compare

@github-actions github-actions released this 11 Aug 00:43
Immutable release. Only release title and notes can be modified.
v0.8.14
8068390

Installation

Download the installer for your platform:

  • Windows: the .msi (recommended) or .exe.
  • macOS (Apple Silicon): the .dmg. Right-click → Open on first launch (the app is not notarized yet).
  • Linux: the .AppImage (single file, no install) or .deb (Debian/Ubuntu).

Installed copies check for new releases on launch and offer a one-click update.

Four in five stored quota snapshots said nothing new

Odometer records a rate-limit snapshot every time Codex reports one, which is on essentially every model response. Measured across the real transcripts on a large machine, 81.6% of them are byte-identical to the one before: the same window, the same percentage used, nothing changed.

They are now collapsed. A run of identical observations is stored once, carrying when the run started, when it ended, and how many observations it stands in for. On a corpus shaped like a real history that is 88% fewer stored points and 87% fewer bytes for that part of a session.

This is the same data that caused v0.8.13's memory problem, seen from the other side. That release stopped keeping millions of copies in memory. This one stops writing most of them down in the first place.

Nothing about your numbers changes

The collapse is designed to be invisible, and that took more care than dropping duplicates would have.

Quota pace is calculated from the first and last observation in a window, so simply deleting the ones in between would not have changed any pace figure — but forecasts are only produced once a window has at least five observations, and deleting duplicates would have pushed some windows below that line. The forecast would have quietly stopped appearing, with nothing to indicate why. Keeping the observation count alongside each collapsed run means every threshold sees exactly the number it saw before.

One more consumer turned up during the work: per-turn quota receipts look up snapshots by turn, not by value. So a run is never merged across a turn boundary, and those receipts are unaffected.

Both properties are checked by tests that run the same data through the old and new paths and compare the complete result, not by inspection.

Startup now reports how big your data has got

Startup time on a large history has been erratic for several releases — the same two gigabytes of stored sessions has taken twelve seconds to read in one run and well over a minute in another. The time spent turning those bytes into data is steady; the time spent getting them off disk is not.

The likeliest explanation is simply size: a database that has grown to several gigabytes over weeks, on a disk with little room left. Nothing Odometer recorded could confirm that, so it now reports each database's size and how much space is free on the drive holding it, alongside the existing timings.

That is a measurement, not a fix. It exists so the next decision is made on evidence — the same approach that ended the memory investigation after three releases of confident guessing had not.

Your existing history is untouched

Sessions already stored keep their uncollapsed snapshots until they happen to be re-read. Only new and re-read sessions are collapsed.

Forcing everything to be redone would mean re-reading every transcript from scratch — far more data than the stored copy — and a single very slow startup. Whether that is worth it depends on how large your database actually is, which is exactly what this release starts reporting. It seemed better to measure first.

Full Changelog: v0.8.13...v0.8.14

Odometer v0.8.13

Choose a tag to compare

@github-actions github-actions released this 10 Aug 21:05
Immutable release. Only release title and notes can be modified.
v0.8.13
2b9ae3d

Installation

Download the installer for your platform:

  • Windows: the .msi (recommended) or .exe.
  • macOS (Apple Silicon): the .dmg. Right-click → Open on first launch (the app is not notarized yet).
  • Linux: the .AppImage (single file, no install) or .deb (Debian/Ubuntu).

Installed copies check for new releases on launch and offer a one-click update.

The memory problem is found and fixed

Three previous releases tried to reduce Odometer's memory use and none of them worked. v0.8.12 added the missing measurement instead of guessing again. This release acts on what it found.

The cause was never the size of your sessions, which is what all three earlier attempts targeted. It was rate-limit snapshots — the small records Codex attaches to its transcripts saying how much of your quota is used.

Why they added up to gigabytes

Odometer records one snapshot per Codex event that carries quota information. A comment in the code described this as "typically a handful per session." Counted across the real transcripts on this machine, it averages 1,393 per session, and the largest single session holds 22,395. Nearly four million in total.

Each one was then kept three separate times in memory.

Meanwhile the code that reads them uses the most recent 2,000 per provider and discards the rest. The limit was deliberate and had been there all along — but it was applied when reading, and nothing ever limited what was kept. So the app held roughly eleven million copies of something it would only ever look at six thousand of.

What changes

Odometer now applies that limit when storing, not just when reading, with a generous margin so nothing the quota forecasts depend on can be lost. It also stops keeping a second full copy of every snapshot alongside each session, since the only two things that used it needed just the newest one and a count.

Measured against a corpus built to match the real distribution — same heavy skew, same 22,395-point extreme — memory held after loading history drops from roughly 2,900 MB to about 32 MB. Loading takes the same time as before; an earlier version of this fix made it twice as slow, which is why the test now tracks speed alongside memory.

Nothing about your stored history changes. No migration, no rescan, no re-reading your transcripts.

Not called fixed yet

Three releases have shown a large improvement in testing and no improvement in practice, so the issue stays open until a recording from a real machine says otherwise. The difference this time is that the test corpus is now known to reproduce the actual problem: run against the previous version, it predicts 3,375 MB where the real recording measured 2,839 MB. None of the earlier probes could make that claim — every one of them was built on a corpus that turned out not to resemble a real history.

If you export a performance recording after using this build, the number to watch is the memory reading after "hydrate history."

Also known, not fixed here

The rollup tables are recomputed in full on every launch — 17 seconds in the last recording, to account for 96 changed files out of 4,495. Tracked separately, along with the fact that 82% of those rate-limit snapshots are exact duplicates of the one before them, which is worth roughly a third of the startup read once addressed.

Full Changelog: v0.8.12...v0.8.13

Odometer v0.8.12

Choose a tag to compare

@github-actions github-actions released this 10 Aug 06:01
Immutable release. Only release title and notes can be modified.
v0.8.12
3c14008

Installation

Download the installer for your platform:

  • Windows: the .msi (recommended) or .exe.
  • macOS (Apple Silicon): the .dmg. Right-click → Open on first launch (the app is not notarized yet).
  • Linux: the .AppImage (single file, no install) or .deb (Debian/Ubuntu).

Installed copies check for new releases on launch and offer a one-click update.

This release measures memory. It does not try to reduce it.

Three releases in a row attempted to cut Odometer's memory use on large histories. Each one had a sound-looking explanation and a test that showed a large improvement. None of them changed the real number, and one of them made startup slower.

The reason is straightforward in hindsight: Odometer measures time in detail and memory not at all. Every attempt was reasoning about where the memory probably went, checked against artificial test data that turned out not to resemble a real history. So this release stops guessing and adds the missing measurement instead.

What it records

When performance tracking is enabled, Odometer now reports its own memory at each stage of startup — opening the history database, loading it, scanning, and once more after startup settles. Each sample includes the memory the operating system attributes to the process, the highest point it reached, and optionally how much the program itself has allocated.

The peak matters as much as the current value: a large temporary spike during startup can disappear before any single reading catches it, while still being the reason the number in Task Manager stays high.

One explanation already ruled out

A likely suspect was the database's own page cache — Odometer keeps two databases, one of which is several gigabytes. Checking took a moment and eliminated it: both use the default cache of about 2 MB and neither memory-maps its file. Two small caches cannot account for gigabytes.

That halves the remaining possibilities before a single measurement is taken.

To help, if you would like to

The heap portion is off by default. Settings → enable "Track allocator heap", alongside the existing performance tracking, then use Odometer normally and export the log.

The distinction that toggle provides is the useful one: if the operating system reports high memory while the program's own allocations are low, the cause lies outside Odometer's direct control. If both are high, it is memory the program requested and has not returned. Those need different fixes, and right now there is no way to tell which is happening.

The measurement costs nothing — enabling it changed performance by less than the noise in repeated timing runs.

Nothing else changed

No memory optimisation, and no revert of the previous release's slower startup. Both wait on the measurement rather than another guess.

Full Changelog: v0.8.11...v0.8.12

Odometer v0.8.11

Choose a tag to compare

@github-actions github-actions released this 10 Aug 04:36
Immutable release. Only release title and notes can be modified.
v0.8.11
55df9e0

Installation

Download the installer for your platform:

  • Windows: the .msi (recommended) or .exe.
  • macOS (Apple Silicon): the .dmg. Right-click → Open on first launch (the app is not notarized yet).
  • Linux: the .AppImage (single file, no install) or .deb (Debian/Ubuntu).

Installed copies check for new releases on launch and offer a one-click update.

v0.8.10 was supposed to reduce memory use. It did not.

That release changed what Odometer keeps in memory, and the change was real — but it left untouched how much it allocates at once while starting up, which is what actually determines the number in Task Manager. Memory went from 4,433 MB to 4,109 MB, about 7%, when a much larger drop was expected.

This release fixes the part that was missed.

Loading history no longer builds the whole corpus in memory first

Startup read every session's stored contents into memory, then decoded all of them into a second complete copy, and only then reduced them to the compact summaries it actually keeps. Two full copies of your entire history existed simultaneously before a single one could be released.

It now processes one session at a time — read, reduce to a summary, discard — so the amount held at any instant is one session rather than all of them.

Measured on sessions sized to match a real history: the peak went from +175.5 MB to +2.8 MB for a 300-session pass, and the memory still held afterward went from +174 MB to zero.

Applying thread names stops loading sessions it does not need

Odometer checked whether a thread name is stored anywhere other than inside the session record itself. It is not — so rewriting the record is unavoidable without changing the database format, and that part stays.

What was avoidable: it had been loading each session's complete contents first, only to satisfy a general-purpose write path that then re-read the same record to check for conflicting edits — a check that cannot apply here, because the content comes straight from the database rather than from something that might be out of date.

Same measurement scale: peak +88.5 MB → +3.0 MB, and the pass runs about twice as fast.

Why the previous release got this wrong

Both of v0.8.10's fixes were validated against artificial sessions roughly fifteen times smaller than real ones. At that size, the dominant cost is per-item overhead — exactly what those fixes reduced — so both looked like large improvements and neither transferred.

The measurements in this release use sessions sized from a real recording, and they report peak memory rather than totals.

Not yet claimed as fixed

Nothing here is marked resolved. Two releases in a row have shown improvements in testing that did not appear in practice, so these stay open until a recording from a real machine confirms them.

No migration

The history database format is unchanged. Upgrading does not re-run any migration.

Full Changelog: v0.8.10...v0.8.11

Odometer v0.8.10

Choose a tag to compare

@github-actions github-actions released this 10 Aug 02:18
Immutable release. Only release title and notes can be modified.
v0.8.10
b1d9116

Installation

Download the installer for your platform:

  • Windows: the .msi (recommended) or .exe.
  • macOS (Apple Silicon): the .dmg. Right-click → Open on first launch (the app is not notarized yet).
  • Linux: the .AppImage (single file, no install) or .deb (Debian/Ubuntu).

Installed copies check for new releases on launch and offer a one-click update.

Memory use should drop substantially

Odometer had been keeping every session's complete contents in memory for as long as it ran — every turn, every message, every token reading, every tool call — even though almost nothing needed them. On a large history that was several gigabytes, and it grew with every session you added.

It now keeps a compact summary of each session and reads the full contents from its local database only when something actually needs them, such as opening a session's detail pane.

Measured on 4,400 realistically-shaped sessions: 31,458 bytes per session down to 2,499, about 12.6x smaller. That figure comes from measuring the data structures directly rather than the whole application, and the synthetic sessions used are smaller than typical real ones — so treat the ratio as the meaningful number, not the byte counts. If anything the real-world saving should be larger, because a summary barely grows as a session gets longer while the full contents do.

If you notice anything slower, the most likely candidate is opening a session's detail pane, which now reads from disk instead of memory. That path reports its own timing, so a performance export will show it plainly.

Two of the heavier analytics views — tool impact comparison and configuration-change correlation — had no way to narrow which sessions they needed, and would have ended up reading the entire history on every request. They now narrow by date overlap first, which is safe because a session whose activity falls entirely outside the window being examined cannot affect the result.

Startup does less redundant work

Applying thread names after a scan performed one database read and one database write per changed session, roughly 370 of each on a large history. Both are now done in single batched operations: 2.62 s → 0.63 s in a like-for-like measurement.

Still being worked on

  • Warm startup is faster than a few releases ago but not yet back to where it was before it regressed.
  • Loading history at startup still reads each session in full before reducing it to a summary. That is about a third faster than before, but properly fixing it needs a change to how the database stores sessions.

Both are measured rather than suspected, and both are tracked.

No migration

The history database format is unchanged. Upgrading does not re-run any migration.

Full Changelog: v0.8.9...v0.8.10