Releases: agentic-box/memora
Release list
0.3.3
Memora 0.3.3
Search accuracy and hardening release.
Search
- Full-text search now queries the FTS index correctly, improving keyword relevance; substring matching remains only as an explicit fallback.
limitis honored onmemory_hybrid_searchandmemory_semantic_search(top_kstill accepted).- Searches and lists with lineage filtering (
follow=active/latest) fill the requested result count even when top-ranked candidates are superseded, scanning beyond the previous 5,000-row window with a loud error at the safety bound instead of silent truncation.
Absorb & lineage
- Absorb updates supersede the current version of a memory, resolving through the supersession chain to the leaf.
- New classifier measurement harness: labeled fixture pairs, per-class precision/recall and confusion matrix, dry-run safe, with a
--min-macro-f1gate for regression testing. - All LLM calls are bounded by an explicit timeout (
MEMORA_LLM_TIMEOUT, default 60s). Measurement mode fails loud; runtime absorb degrades gracefully.
Tag policy
- The Cloudflare graph app validates tag writes (memory edit and chat) against a versioned policy stored per database, failing closed when the policy is unavailable.
- Wildcards support slash namespaces (
memora/*) alongside dot namespaces; tags are capped at 100 characters, counted identically (Unicode code points) in Python and TypeScript and guarded by a shared conformance fixture.
Graph UI
- The WebGL canvas tracks its container through drawer transitions via ResizeObserver, fixing a sizing race under load.
CI
- New
clean-installworkflow: builds the wheel, installs into an empty environment, and runs the suite — on push, tags, and a daily schedule. - New
graph-uiworkflow: browser tests for the graph UI (drawers, top bar, render-idle power behavior, database selector), tag-policy write tests, and lineage logic tests against a seeded local D1.
Docs
- Install instructions lead with PyPI (
pip install memora-mcp); absorb, supersession lineage, and digest documented in Features;MEMORA_TAG_FILEformat corrected (JSON array).
v0.3.2
memora 0.3.2
Consolidated 0.3.x release. Notes for the earlier 0.3.x tags remain in the repo as
RELEASE_NOTES_0.3.0.md and RELEASE_NOTES_0.3.1.md.
Fresh installs work again
mcp 2.0.0 removed mcp.server.fastmcp, and our dependency was an unbounded mcp>=1.0.0, so every
fresh install resolved to the new major and the server died at import with
ModuleNotFoundError. Now constrained to mcp>=1.0.0,<2 (the 1.x line is maintained in parallel).
The failure was invisible from both sides: a dead stdio MCP server looks identical to one exposing no
tools, and existing environments had mcp pinned to a working 1.x, so every test suite passed.
Reported and fixed by @BillyBunn in
#44.
Breaking
Embeddings. memora could be configured — following its own installer and README — into a state
where no embedding was ever computed and nothing said so. install.sh generated
openai/text-embedding-3-small while the README recommended OpenRouter as the base URL, and
OpenRouter serves no embeddings endpoint. Every call 404'd, one warning went to a log nobody reads,
and stores silently filled with TF-IDF keyword bags. In the store where this was found, 756 memories
had been keyword vectors for months.
- Every store needs one embedding rebuild. The model fingerprint now records backend, model,
endpoint host and representation, so the old bare"openai"stamp no longer matches. - Dense backends no longer fall back to TF-IDF. A provider failure raises instead of persisting a
wrong vector. Settfidfexplicitly if you want it. MEMORA_EMBEDDING_API_KEY/MEMORA_EMBEDDING_BASE_URLare an atomic pair — set both or
neither. A partial pair is rejected rather than borrowing the missing half fromOPENAI_*, which
could previously send one provider's secret to another provider's host.
Issues are no longer inferred. memory_create_issue and memory_create_todo are now the only
ways a memory becomes typed; memory_absorb and plain creates stay untyped knowledge. A keyword
classifier had mislabelled 130 knowledge memories as open issues. Existing typed memories are
untouched.
Added
memory_verify_integrity— read-only doctor reporting representation counts, coverage and
offending ids, with concrete remediation.MEMORA_EMBEDDING_STRICT=1(recommended) turns a broken endpoint into a named error instead of
silent degradation.- Integrity derived from SQL and invalidated by a DB-owned change epoch, so an external writer cannot
leave a stale "healthy" verdict behind.
Fixed
- Batch embedding responses are validated for cardinality, index coverage and uniform dimensions, and
reconstructed by index — a reordered response could previously attach a vector to the wrong
memory. - Coverage uses indexed anti-joins in both directions; counting by subtraction let one orphan cancel
one missing embedding. absorbno longer leaves untracked partial rows.- Concurrent
ensure_schemano longer raises on duplicate columns. install.shand the README no longer generate or document the broken configuration.
memora-graph
- Supersession is visible. Superseded memories render dimmed amber with a
SUPERSEDEDbadge and
directed lineage arrows; a toggle collapses to current-state only. Onlysupersedesedges count as
lineage —references/contradicts/implementswere previously drawn as supersession. Half-written
crossrefs are detected rather than shown as current, and the page reports
LINEAGE UNAVAILABLEinstead of a confident zero when it cannot tell. - The 3D view no longer burns the CPU. Its render loop repainted 60×/sec forever, whether or not
anything changed — measured at ~227% CPU on an idle page. The loop now stops once the layout
settles and wakes on interaction: ~227% → 0.4%. Kill switch:
localStorage.setItem("memora-graph.noIdle","1"). - Resizable timeline and detail drawers with independently persisted widths; per-tab panel widths in
the default view. - The database selector lists every configured database in both views (it was hardcoded in one).
- The top bar no longer hides behind open drawers.
Known issues
- Epoch time-of-check window — a query can use data that changed mid-call; the next call detects
it. Transient, self-healing. memories_embedding_repairsis unbounded with no foreign key to memory lifetime; explicit id
reuse can mark an unrelated row as recurring.- D1 ownership recovery after a lost response lacks bounded retry and a unique operation record.
- External writers must populate the new columns. A writer emitting a thresholded/sparse encoding
of a dense vector is reported as an encoding fault by id, and auto-rebuild is skipped deliberately. - The default
index.htmlview lacks force-graph's database-switch protection — after a switch or
live refresh an already-rendered list can briefly show another store's authority state. Memory ids
overlap across databases, so this is worth knowing.
Upgrading
- Upgrade, then run
memory_verify_integritybefore anything else. - If it reports a repairable mismatch, run an embedding rebuild.
- Set
MEMORA_EMBEDDING_STRICT=1. - Verify by looking at a stored vector, not at your config — it should have your model's
dimension count with numeric keys covering0..N-1. Configuration that looks correct is what hid
the original problem.
v0.2.29
v0.2.28
Notable changes since v0.2.27
Fixes
- MCP schema sanitizer (
memora/server.py) — strips Pydantic-generated nullableanyOfcombinators from tool input schemas before servingtools/list. Anthropic's tool validator rejected these, which 400'd the entire MCP handshake and made Claude Code disconnect the server. Reviewed end-to-end by an inter-agent bus loop (claude-B audit + codex review). 19 of 41 memora tools were affected; all clean after sanitize. - Embedding fallback observability (#457) — stops silent fallback to TF-IDF when the OpenAI embedding endpoint fails; warns visibly instead of degrading silently. Also adds explicit warn-on-fallback log path.
- API issue filter tightening —
/api/memoriesissue filter now requires strictmetadata.typematch instead of fuzzy heuristic. - Graph spin fixes (5 commits) —
vis-networkphysics now disables after stabilization, force-disables 1.5s after drag, re-enables on dragStart for elasticity, explicitly starts simulation on drag events, and stops endless spinning aftermemory_updaterealtime reload. - Favorites filter (#544) — fetches the full memory set from the server instead of filtering only the loaded page.
Features
- Issues tab in web UI — inline status / category editing for memories of
type: issue.
Reverts
- Reverted Block 4 of duplicate-review UI action-buttons wiring (held back pending follow-up work).
v0.2.27
What's New
Structured Document Storage
memory_store_document— Store markdown documents as a root memory + searchable typed fragments (claims, plan items, references, risks, section chunks)memory_get_document— Retrieve documents by key with optional fragment type filteringmemory_delete_document— Cascade delete a document and all its fragments- Structure-aware parser (
document.py) — splits by headings, tables, numbered lists, URL lists, and risk sections instead of blind token chunking - Fragment integrity guards — protects document fragments from accidental delete, merge, absorb, duplicate detection, and supersession detection
- Graph UI — document fragments hidden from visualization (local + cloud); only the document root node is visible
Auto-categorization Improvements
- Auto-assign
metadata.sectionfrom detected project at write time - Normalize generic tags to project-prefixed tags automatically
- Backfill tag detection now catches subsection changes
- Derive subsections from bare tags when no prefixed tags exist
- Broader project detection with tag-based fallback and more content patterns
Bug Fixes
- Fix
find_duplicate_candidatestype error when crossref entries have mixed ID types - Fix crossref computation to exclude document fragments from similarity graph
- Skip typed link entries (supersedes, extends, etc.) in duplicate candidate scanning
v0.2.26
What's New
memory_detect_supersessions — Retroactive supersession detection
New MCP tool that scans existing memories for pairs where one updates/replaces another, then creates supersedes edges between them. Complements memory_absorb which only catches supersessions at write time.
- Neutral LLM classification with 6-way relation enum (a_supersedes_b, b_supersedes_a, duplicate, related, contradicts, neither)
- Conservative defaults:
dry_run=True,min_confidence=0.75 - 30s rate limit cooldown
- No schema changes required
Bug fixes
- Fix three bugs in
memory_absorbthat broke LLM classification and fact grouping - Consolidate related new facts into single richer memories via LLM synthesis
- Bump graph cluster thresholds back to 0.4
Other improvements
- Lineage-aware retrieval:
followparameter for supersession chain walking - Context-efficient retrieval: compact defaults, fields projection, hybrid filter fix
- Add
--versionflag to memora-server CLI - Add install script for server, skill, and MCP config guidance
v0.2.25 — D1 latency collapse
Headline
`memory_create` / `memory_update` drop from 10s+ to ~2s per call on the D1 backend — 5× faster.
Three independent fixes, all landing together:
- Cached `ensure_schema()` per backend instance — previously ran 7–9 D1 round-trips on every MCP tool call (~4–8s wasted each). Now runs once per backend per process. [`memora/schema.py`]
- Single paginated LEFT JOIN for crossref/vector scan — replaces the old `list_memories(...) + _get_embeddings_for_ids(...)` two-step that cost ~10 sequential D1 round-trips on stores with many memories. [`memora/storage.py`]
- Per-instance D1 session token + backend-level keep-max bookmark mirror — the old class-level token could be stomped by cross-thread code (`cloud_sync`'s `threading.Timer`), corrupting read-your-writes. Now safe under concurrency. [`memora/backends.py`]
Also in this release
- Durable Object request reduction — WebSocket keepalive ping removed, broadcasts skipped when no clients connected, disconnect on hidden tab. Direct Cloudflare cost savings.
- Schema cache correctness — `CloudSQLiteBackend.sync_before_use()` can `shutil.move` a fresh DB file over `cache_path`; the cache now invalidates via inode-based signature so long-lived processes don't run against a post-replacement DB with stale schema assumptions.
- Security (merged from main) — XSS attribute injection fix in graph UI (#36), stale draft cancellation (#37).
- Docs — eventual-consistency model of the `related` graph documented in `memory_related` and `memory_rebuild_crossrefs` MCP tool docstrings.
Upgrade notes
- No schema migration required.
- No public API signature changes.
- No configuration changes required.
- New optional env var `MEMORA_VECTOR_SCAN_PAGE_SIZE` (default: 1000) — rarely needs tuning.
- 39/39 tests passing.
Verified performance
Measured against live D1 (steady-state, medium-length memory):
| Operation | Before | After |
|---|---|---|
| `storage.connect()` 1st | — | ~1.9s |
| `storage.connect()` 2nd+ | ~4–8s (schema probe) | ~0ms (cache hit) |
| `add_memory` | 10s+ | ~1.8s |
| `update_memory` | 10s+ | ~1.1s |
v0.2.24 — Security Hardening
Security Hardening Release
Comprehensive security audit and fixes across critical, high, and medium severity findings.
R2 Proxy Lockdown
images/prefix restriction on both cloud and local proxies- Path traversal (
..) blocking - Image extension allowlist + Content-Type validation from R2 metadata
MCP Tool Rate Limiting
- Single-flight + cooldown on 7 expensive operations (rebuild, export, import, insights, duplicates, migrate)
- Prevents resource exhaustion via repeated calls
Prompt Injection Mitigation
- Memory content moved from system prompt to user/context message across all 4 LLM callsites
- Defensive markers on all memory-to-LLM paths
Upload Hardening
- Symlink chain rejection, Pillow content verification
- MIME derived from image content (not filename), no path disclosure in responses
- Extension allowlist aligned with storage backend
Additional Fixes
- Origin validation (urlparse-based) on SSE and chat endpoints
- Chat rate limiting: 30 req/min per IP
- Secure dedup cache:
~/.cache/memora/, SHA-256 filenames, fcntl locking - SQL hardening: alias-aware ORDER BY whitelist, LIMIT/OFFSET clamping
- Error sanitization: generic messages for infrastructure errors
- Dependencies: Pillow >= 10.4.0, openai >= 1.6.0, zod 3.24.2
- Lock files: requirements.lock, requirements-local.lock, requirements-dev.lock
- Security Model documentation in README
Codebase Health (from v0.2.23)
- Full XSS sweep in graph UI (escapeHtmlAttr, CSS.escape, data-* delegation)
- PATCH metadata shallow merge with null-delete semantics
- FTS/embedding reindex on tag/metadata changes
- Module extraction: embeddings.py, schema.py split from storage.py
- Duplicate detection aligned to 0.85 threshold across all paths
- 39 tests passing
Full Changelog: v0.2.23...v0.2.24
v0.2.23
Changes
- Chat embeddings: Compute embeddings on memory create/update via chat UI, ensuring new memories appear in semantic search immediately
- Robust search: Run semantic + keyword search in parallel with isolated error handling; recent memories fallback preserved
- UI improvements: Pencil edit icon, hide complex metadata section by default
- Sync:
sync-to-d1.pynow syncsmemories_embeddingstable with transaction wrapping - Query rewriting: Add RAG query rewriting for better retrieval
v0.2.22
What's Changed
- Fix focused node centering — nodes no longer appear shifted left when the detail panel opens
- Memora favicon — tab icon now uses the memora logo
- Wider detail panel — default width increased from 450px to 560px
- Scrubbed private data — removed hardcoded Cloudflare DB IDs from example scripts