ECHO Memory · Performance Update
Releases v1.1.0 and v1.2.0 rebuilt the vault's network layer and entity resolution. Long operations that used to exceed the agent tool timeout and drop the session now complete in under a second — and shortened entity names resolve instead of spawning duplicates.
Scripts that sweep the whole vault — sweep.py,
vault_lint.py, recall rebuild — were making hundreds of serial
requests, each opening a fresh TLS connection, and re-reading every note 2–3 times.
On the constrained agent sandbox that pushed a single pass past the ~120-second tool
timeout, so the process was killed mid-run and the session/thread was dropped. The
fix attacks the cause on three axes; the headline change is reliability:
a full-vault pass that used to fail now finishes in well under a second.
These factors compound. A full-vault pass pays 4.5× less per request, issues ~2–3× fewer of them, and runs the rest ~2× in parallel — a modeled ~18–27× reduction in total request-time, before counting the higher connection latency of the production sandbox where the old path actually failed.
One persistent connection per thread, reused across every request — instead of a fresh TCP+TLS handshake per file. The single biggest win; benefits every operation (load, capture, recall) for free.
A new read_many()
fans GETs across a thread pool (8 workers). I/O-bound work, so it parallelizes
cleanly; the win grows with vault size and network latency.
Each note is fetched
once and reused across all passes — eliminating the 2–3× re-reads and
the per-link-target re-fetch storm in sweep.
Shortened names now resolve via aliases + a fuzzy fallback, so a mention like “echo memory” finds the existing project instead of silently creating a duplicate note.
Micro-benchmark: 25 real notes, best of 3 runs, against the live endpoint.
| Mode | ms / request | vs old |
|---|---|---|
| Fresh connection per GET (old) | 30.3 | 1.0× |
| Pooled keep-alive, serial | 6.6 | 4.5× |
| Pooled + concurrent (new default) | 5.7 | 5.3× |
| Operation | serial (1 worker) | concurrent (8) | speedup |
|---|---|---|---|
| vault_lint.py | 1.77 s | 0.90 s | 2.0× |
| sweep.py (plan) | 1.64 s | 0.85 s | 1.9× |
| sweep.py --apply | — | 1.07 s | — |
| single GET · doctor | — | 0.28 s · 0.21 s | — |
Resolution was exact-match only, so a shortened or expanded name returned
nothing — and the writer would create a parallel note. The active project
slugged echo was invisible to the mention “echo memory.” Now:
| Lookup | Before | After |
|---|---|---|
| echo memory · echo plugin · echo-memory | no match | → projects/active/echo.md |
| non-aliased shortened name | no match | ranked candidates |
| update under a new name | second slug, same note | canonical slug + alias learned |
Aliases are auto-derived from titles, learned from mentions on update, and stored in note frontmatter (folded back into the index on sweep) — so the graph gets better at resolution over time, while exact-match safety prevents wrong auto-merges. The payoff is avoided duplicate entities and the cleanup/merge passes they trigger.
Methodology. Measured on the maintainer's workstation against the live
echoapi.alwisp.com endpoint (low latency). The production agent sandbox has
higher per-request connection latency, so the connection-reuse gain — and the reliability
fix — is larger there than the local figures show. The ~18–27× compound is a model of
the three measured factors (4.5× reuse × ~2× concurrency × ~2–3× fewer reads),
not a single end-to-end timing. Stdlib-only; no new dependencies.
Artifacts. echo-memory v1.2.0 — echo-memory-1.2.0.plugin.
33 automated tests pass; vault-health linter clean of regressions.