All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to SemVer starting from v3.1.2.
-
BEAM initialization status is now available through the additive public Python
BeamInitResult. It reports the configured embedding dimension, any dimension mismatch, and immutable stored dimensions for each vector table. -
Multimodal memory: images, video and audio can become recallable memories (RFCs 0002, 0003, 0004).
BeamMemory.remember_media(ref)takes a reference to a piece of media, registers it, describes it through a configured modality provider, and writes the description back as an ordinary memory that hybrid recall already understands. Nothing about text recall changes.The stack is additive throughout. Two sidecar tables,
media_assetsandmedia_moments, are createdIF NOT EXISTSby their own store when a bank is opened, so existing databases acquire them with no migration step and no change to any existing table. No new package dependency is introduced.It is off unless configured.
modality_enableddefaults tofalseand every endpoint and model key defaults to empty, so an installation that does not opt in behaves exactly as before. The provider seam is named after the protocol rather than a vendor:MNEMOSYNE_MODALITY_BASE_URL,MNEMOSYNE_MODALITY_API_KEY,MNEMOSYNE_MODALITY_VISION_MODEL,MNEMOSYNE_MODALITY_VIDEO_MODEL,MNEMOSYNE_MODALITY_AUDIO_MODELandMNEMOSYNE_MODALITY_TIMEOUTpoint it at any OpenAI-compatible endpoint, and a second backend can be added without inheriting the first one's name.remember_media()returns aMediaIngestResultrather than a bare id, because the ingest path degrades in stages and the caller needs to see which one it landed on:ok,partial,unavailableorrefused.unavailableis a success, not an error. It means the asset was registered and can be described later once a provider is configured.Supporting pieces:
ContentResolverwith aBlobResolverimplementation gives the blob store a reader, so a stored reference can be turned back into bytes;remember()accepts an explicitmemory_typethat overrides the content classifier anddedupe=Falsefor callers that must write a row per call, both defaulting to current behaviour, with an unrecognizedmemory_typelogging a warning and falling back to classification rather than writing a bad value; andmnemosyne doctorgrows a media orphan check that counts both orphan kinds while treating only one of them as a warning, reporting reference columns only and never user content. -
Native MCP Streamable HTTP transport for
mnemosyne mcp.--transport streamable-http(aliashttp) serves the modern MCPhttptransport on a single configurable endpoint (--path, default/mcp) that handles GET, POST, and DELETE, so clients POST JSON-RPC directly to it with no/messagesroute to proxy. Responses stream via SSE upgrade by default or are JSON-only with--json-response. Auth policy matches SSE: loopback binds need no token; non-loopback binds requireMNEMOSYNE_MCP_TOKENbearer auth. A non-loopbackstreamable-httpbind exposes the selected local SQLite-backed memory bank to network clients and additionally requiresMNEMOSYNE_MCP_ALLOWED_HOSTS, withMNEMOSYNE_MCP_ALLOWED_ORIGINSoptionally restricting browser origins. Tracks #598 (this PR: #749); thanks @ekinnee for filing the issue and for the implementation (PR #599) shipped in the same window. -
MNEMOSYNE_JOURNAL_MODEoverrides the SQLite journal mode for store connections. WAL readback on Linux containers over macOS virtiofs intermittently surfaces asdatabase disk image is malformedat every open; deployments on such filesystems can now setMNEMOSYNE_JOURNAL_MODE=delete(or any sqlite journal mode) and the sync client (it rides the beam connection) and every connection that sets a journal mode (memory, beam, query cache, veracity consolidator) honors it. Onlywalpersists in the database file; every other mode is per-connection and reverts to SQLite's default (delete) on reopen, so each connection re-applies the mode rather than relying on persistence. WAL remains the default; the value is trimmed and lower-cased, unset or blank falls back towal, and non-blank invalid values warn and fall back towal.memoryandoffremove disk-backed rollback protection and can corrupt the database after a crash. -
MCP clients can now retire canonical facts with
mnemosyne_forget_canonical(#723). The tool is discoverable and callable by default over MCP; retirement removes the current slot from active recall while preserving it as history. -
MNEMOSYNE_MODEL_CACHE_DIRrelocates the local GGUF cache (#708). The ~656 MB consolidation model was pinned to~/.hermes/mnemosyne/models, so the only way off a small home partition was a symlink. The variable is environment-only and read at import, matchingMNEMOSYNE_LLM_REPO/MNEMOSYNE_LLM_FILE; unset or blank keeps the historical path,~is expanded, and the value is used for the cached-file lookup, the directory creation andhf_hub_downloadalike. Existing models are never moved, copied or deleted. An explicitly set path is authoritative: when it cannot be created or written to, the local GGUF attempt fails with an error naming both the variable and the selected path rather than silently falling back to the default, which would reinstate the location the user moved away from. The error is logged as well as raised, because the download path degrades to AAAK on any exception and a raised message alone would never reach the user. -
CLI version reporting (#642).
mnemosyne --version/mnemosyne versionandmnemosyne-hermes --version/mnemosyne-hermes versionreport installed distribution versions without initializing Mnemosyne data.hermes mnemosyne versionnow reports both core and Hermes-provider versions. -
Embedding dimension in doctor diagnostics.
collect_runtime_diagnostics(surfaced bymnemosyne doctor) now reports the resolvedembeddings_dimalongsideembeddings_model, so operators can confirm theirMNEMOSYNE_EMBEDDING_DIM/ model-table resolution without inspecting a traceback. Complements the fail-loud unknown-model resolver (#521); the version bump is deferred to that PR to avoid a duplicate bump.
-
Entity extraction no longer stores whole quoted spans as entities (#891). The
"..."and'...'patterns in_ENTITY_PATTERNScaptured any quoted span of 2-50 characters, so conversational and roleplay text wrote dialogue into thementionsvocabulary:'Okay,','Talia pauses.','the light is fading.'. Punctuation-bearing values also slip past the stop-word filter, which compares exact strings ('okay,' != 'okay'). Measured on one production store: 589 of 1,270 distinctmentionsvalues (46%) carried punctuation or spaces, and of 9,831referencesedges written by proactive linking, 127 connected pairs sharing such a fragment, 103 of them on nothing else, so junk vocabulary became graph topology that recall reads back. Both patterns are removed. A real name inside quotes is unaffected because quotes do not block\b, so it still extracts from the capitalized single-word and multi-word patterns; the values that disappear are exactly the spans no other pattern can produce, which are the lowercase and punctuation-bearing ones. A quoted lowercase single word was already dropped by the existing lowercase filter. Existing annotation rows are not cleaned retroactively. -
Unknown embedding models now fail loud at startup instead of silently assuming 384 dimensions (#518, #521).
_get_embedding_dimresolves an explicitMNEMOSYNE_EMBEDDING_DIMfirst (must be a positive integer), then the built-in model table, and raisesValueErrorfor an unknown model with no explicit dimension rather than falling back to 384 (bge-small's dimension). A vec0 table is dimensioned at creation, so a silent 384 guess baked the wrong dimension into a fresh database and corrupted vector search for anyone using a model absent from the table (e.g.mxbai-embed-largevia a custom endpoint). Dimension resolution is centralized inembeddings._get_embedding_dim; Beam delegates to it, removing a duplicate resolver that could drift. Embeddings-disabled invocations keep the 384 fallback (the dimension is unused there).Breaking: pointing
MNEMOSYNE_EMBEDDING_API_URLat a custom endpoint with a model not in the built-in table now requiresMNEMOSYNE_EMBEDDING_DIM=<N>, otherwise direct core/MCP-provider startup exits at import with an actionable error (themnemosyne-hermeswrapper catches this and reports the provider unavailable instead of exiting). Blank/emptyMNEMOSYNE_EMBEDDING_DIMandMNEMOSYNE_EMBEDDING_MODEL(common in Docker Compose and.envfiles) are normalized to unset/default rather than treated as explicit invalid values.Upgrade note for stores created under the old silent-384 fallback: setting the model's true dimension can trigger the existing dimension-mismatch guard. Use the documented reindex/recovery path rather than treating the override as a one-step fix. See docs/migration-4.0.md.
-
CI no longer hangs silently on an
mcprelease (#871).mcp2.1.0 deadlocks the streamable-http test teardown, so every matrix job burned its full time budget with zeroFAILEDlines and no commit to blame. The dependency now excludes 2.1.0, and the CI pytest invocations run underpytest-timeout(900 s per test, thread method) so a future hang surfaces as a named failure instead of a bare red job. -
Fact extraction no longer persists truncated or value-free objects (#837). The rule-based
EpisodicGraph.extract_factsregexes matched their optional article inside the next word, so"Alice is already ready"stored(Alice, is, lready); and nothing guarded the object side, so"Bob is different"stored(Bob, is, different)and"Carol uses an extremely reliable editor"stored(Carol, uses, extremely). Those rows reachedfacts,graph_edgesandconsolidated_factsthroughremember, its dedup-update branch,remember_batchandconsolidate_to_episodic, andfact_recallsurfaced them. Because every such triple shares(subject, predicate)with the real facts about that subject, the veracity consolidator also read each one as a contradiction. The article group is now anchored as a whole word, and a new_is_low_quality_objectrejects a lone lowercase object that is a function word, a transient-state adjective, a filler, or a stance/degree adverb. The guard is a closed word list, not a suffix or shape rule, so names and nouns such asSally,Italy,familyanddevelopercannot be rejected, and a capitalised token (Rust,ComfyUI) always passes. The patterns capture one object token and still do, so an adjective phrase reaches the guard as its leading modifier and the rule is about that word alone; widening the capture would change every object row and is deliberately not part of this fix. Article-led subjects are rejected when the article opens a common-noun phrase ("The silence is different"), and kept when it opens a name ("The Matrix is a film","A New Hope has a sequel"), which the word after the article decides. Existing junk rows are not cleaned retroactively. Restores, in a narrower shape, the fix from #248, whose commits are no longer reachable frommain(#862); thanks @ekinnee for the independent report. -
Optional
embeddingsandallinstalls caponnxruntimebelow 1.29. This avoidsblkidstderr on minimal Linux/aarch64 systems. -
CI stopped being able to verify anything, because an unpinned dependency changed ASGI behaviour (#860).
tests/test_mcp_streamable_http.pydrives the authenticated SSE GET by hand through the TestClient portal, and itsreceive()never delivered the initialhttp.requestmessage. That violates the ASGI contract, but mcp 2.0.0 answered without waiting for it, so the driver passed. mcp 2.1.0 reads the request body to enforcemax_request_body_size(SDK #3336), so the handler now blocks beforehttp.response.start, the test's wait fails, andTestClient.__exit__then blocks forever draining a task group that still holds the wedged ASGI task. The job ran to its six-hour ceiling and reported nothing.The dependency is declared
mcp>=2.0.0with no upper bound, so every run resolves the newest release at install time. mcp 2.1.0 was published on 2026-08-24 at 19:04 UTC; every green run predates it and every hung run follows it. This was not intermittent and not a race: 0 hangs in 22 consecutive runs on 2.0.0, then 4 hangs in 4 runs on 2.1.x, and the same boundary reproduces locally on the unmodified test.The server itself is unaffected. Driven over a real socket, mcp 2.0.0 and 2.1.1 both answer the session GET with 200 and
text/event-streamimmediately, so no released version of Mnemosyne is affected and the requirement stays unbounded. Only the hand-written scope could omit a message a real server always sends.Three changes:
receive()now delivershttp.requestbefore blocking; the ASGI call runs as a portal task whose future is cancelled in afinally, so a stuck stream or any failing assertion reports instead of wedging teardown; andpytest-timeoutcaps any single test at 300 seconds, roughly a hundred times the slowest test in the suite, so the next surprise of this shape costs five minutes and names itself instead of costing six silent hours. -
Native Windows no longer defaults to an install mode that cannot succeed (#857).
mnemosyne-hermes installdefaulted tosymlinkon every platform, but Windows only permits creating a symbolic link with Developer Mode enabled or an elevated shell. Without one, the install failed withWinError 1314, so it worked for some users and not others depending on a privilege nobody thinks to check. On native Windows the default is now persistent wrapper mode, which writes a real plugin directory and needs no privilege;--mode symlinkstill works for anyone who holds it, and nothing changes on Linux, macOS or WSL. An omitted--moderesolves to wrapper before installation begins rather than switching after a failure, and an explicit--mode symlinkthat hitsWinError 1314is never switched automatically: it fails with recovery guidance, and the message says so plainly. -
CLI failure boundaries now emit stable sanitized error codes.
-
The Core wheel no longer ships
examples/as an installed top-level package. #729 excluded the repository-onlyintegrationstree from root package discovery, but the same greedy finder still sweptexamples, so installingmnemosyne-memoryplaced a top-levelexamplespackage intosite-packages, where it can collide with or shadow any other distribution'sexamplesmodule and a user's ownimport examples.examples*is now excluded. The wheel regression suite asserts the entire top-level surface rather than individual leaked directories, so the next repository-root directory cannot reachsite-packagesunnoticed. -
Portable JSON exports now disclose partial data (#602). The additive completeness manifest lists populated persisted surfaces omitted entirely and exported sections that omit populated fields; import reports the source artifact's evidence instead of implying a lossless restore. Older export files remain importable with unknown completeness.
-
Hermes plugin tools no longer talk to a second, never-initialized provider.
register()constructed oneMnemosyneMemoryProviderfor MemoryManager and a second for PluginManager tool handlers. Desktop/tool_callhit the empty instance and returnedMnemosyne not initializedwhile the CLI andhermes memory statusused the live DB. Both paths now share one instance, and a primary-context tool call lazy-initializes if Hermes never calledinitialize(). -
Native Windows Hermes venv discovery now finds
Scripts/python.exe(#809). Implicitmnemosyne-hermes installdiscovery now supports validated native Windows virtual-environment layouts through launcher siblings, known Hermes roots, the active prefix, andVIRTUAL_ENV; explicit--pythonremains authoritative. -
Windows Hermes symlink installs now explain WinError 1314 recovery (#807). When Windows denies symbolic-link creation because Developer Mode or the symbolic-link privilege is unavailable, the installer fails closed and prints a command-safe persistent wrapper retry using the resolved Hermes Python; it does not switch modes automatically.
-
CJK-labelled secrets are now detected, flagged and redacted (#806). A secret introduced by a Chinese/Japanese/Korean label with a fullwidth separator (
数据库密码:s3cr3t_...) previously bypassed the write classifier, hygiene secret flagging and doctor preview redaction.detect_secretsnow recognizes a curated set of CJK labels (密码/密钥/令牌/口令/私钥,パスワード/秘密鍵/トークン,비밀번호/키) followed by an ASCII or fullwidth separator, with a credential-value predicate that requires a non-CJK, token-like value (8+ chars, at least one ASCII letter or digit) so ordinary Chinese policy prose such as密码:建议每90天更换一次is never classified as a secret. The write classifier and hygiene consume this throughdetect_secrets; doctor preview compiles the same canonical patterns for redaction. -
Hermes wrapper validation timeout is configurable (#804).
mnemosyne-hermes install --mode wrappernow accepts--import-timeout SECONDS(default: 60) for both selected-Python validation probes, rejects non-positive/non-finite values, and gives a retry command when validation times out. -
Committed memory invalidations no longer report failure when enhanced-recall cache eviction fails (#594). The mutation remains successful and the cache error is logged for reconciliation.
-
Hermes providers no longer clear the shared host LLM backend while another primary provider remains active (#551).
-
The OpenAI-compatible modality retry test is deterministic under load (#798). Its localhost stub handles one request at a time and records response statuses, so the 401/no-retry contract is checked against the response actually served.
-
Raw dialog no longer starves distilled facts out of the dense recall voice (#696). Conversational capture (
source='conversation', and legacyhoncho_*imports) is topically identical to the queries that retrieve it, so those rows saturated the nearest-N working-memory vector pool and pushed distilled facts beyond it. An affected fact surfaced withdense_score=0.0or did not surface at all. Dialog sources are now excluded from the working-memory dense candidate pool while remaining fully reachable through FTS. #608 widened the candidate neighbourhood, which helps a shallow flood; this is what makes that capacity effective against the flood itself. -
hermes mnemosyne exporthonors the resolved bank instead of leaking the default (#690). Explicit and profile-resolved bank selections are now passed to the export-sideMnemosyneinstance. A selected bank is validated through a read-only SQLite preflight before any Beam, Mnemosyne or output initialization, and a missing, directory-incomplete, table-incomplete or column-incomplete bank is rejected without creating an output artifact or mutating the bank. Validation failures do not expose filesystem paths. Export with no selected bank is unchanged. -
An uncached local model download now warns before it starts (#703). The first use of the local GGUF path could spend a long time fetching roughly 656 MB with nothing said. A single warning now names the model file, the HuggingFace repository and the destination cache path, states the size for the built-in default artifact, and explains both the pre-cache option and the AAAK-only opt-out via
MNEMOSYNE_LLM_ENABLED=false. Default, cache, download, retry and fallback behavior are unchanged, and nothing is written to CLI or MCP stdout. -
Hermes wrapper installs are no longer clobbered by a forced symlink install. Wrapper mode is the Docker-safe integration path, and a generic forced symlink install could remove its import bootstrap and leave profile links resolving to the package directory. A wrapper-to-symlink downgrade now requires an explicit request, and wrapper refreshes are validated and staged before they replace a working install. Legacy fresh symlink installs, opted-in profile links and the
upgradepath are unchanged. -
Forgetting a working memory now removes its associated gists (#782). Direct and batch forget paths previously left derived gist rows behind, allowing stale context to survive deletion. Cleanup is atomic and preserves the existing session authorization boundary.
-
The
mnemosyne-stats.pytest suite now runs against a hermetic pytest-owned database instead of the developer's real one (#783).tests/test_mnemosyne_stats.pyshelled out to the stats CLI without an environment override, so on a developer machine it resolved the ambientMNEMOSYNE_DATA_DIR/HERMES_HOME/HOME, read and reported on the real Mnemosyne database, and wrote snapshots into real home directories;test_rapid_fireflaked when a live database had concurrent writers, and the tests exposed the developer's stored memories. An autouse module fixture now points the subprocess at a seededtmp_pathbank plus tmp home/wiki dirs and re-points the assertion helpers at the same locations, making the tests hermetic and ordering-independent. -
Automatic working-memory consolidation no longer calls
sleep_all_sessions(), andauto_sleep_enabled: falseis honored (#771). The Hermes provider's_maybe_auto_sleep()previously selectedsleep_all_sessions()by capability probing, which could sweep unrelated sessions. Its worker now callssleep()on theBeamMemoryinstance bound to the triggering session. The provider also reads the coreauto_sleep_enabledconfig key (via the Mnemosyne config bridge, matching the root provider) in addition to the Hermesauto_sleepkey, somnemosyne config set auto_sleep_enabled falsedisables automatic consolidation. -
The Core wheel no longer ships the repository-only Hermes provider source/test tree (#729). The root setuptools package finder did not exclude the nested
integrations/tree, somnemosyne-memorywheels bundled the standalone Hermes provider and its tests even though it is published separately.integrations*is now excluded from Core package discovery, while the standalonemnemosyne-hermespackage remains separate; regression tests build both wheels and assert their contents. -
valid_untiltimestamps are now aware UTC everywhere (#525).invalidate()wrote a naive local wall-clock ISO value while SQLite-side surfaces (doctor, repair, MCP validate) compare against UTCjulianday('now')/CURRENT_TIMESTAMP, so expiry checks disagreed by the host's UTC offset and shifted with DST. The write path and every Python-sidevalid_until > ?comparison now usedatetime.now(timezone.utc). All read filters compare stored values chronologically (julianday) rather than by ISO string ordering, so offset-bearing and space-separated legacy rows are judged by their actual instant; offset-bearing values are canonicalized to UTC at every supported persistence boundary (remember,consolidate_to_episodic,import_from_dict, Hindsight import, sync-apply). Legacy rows written without an offset are interpreted as UTC (the same interpretation SQLite already applies), and only an exactYYYY-MM-DDvalid_untilinput keeps pass-through semantics (any other parseable form, including lowercasetseparators, is normalized; unparseable values pass through unchanged). -
SHMR clustering no longer crashes with a dimension mismatch (#762).
harmonize()'s_embed()passed astrtoembeddings.embed(), which expectsList[str]; the string was iterated per character, so each embedding's dimension scaled with the text length and_cluster_by_similarity()failed whenever two candidates had different lengths._embed()now wraps the text in a list, returns a fixed-dimension vector, and degrades to zeros when embeddings are unavailable. Theharmonize()facts query also drops a filter on astatuscolumn that thefactstable does not have, so the candidate step no longer raisesOperationalError. -
MCP
tools/listno longer advertises tools that cannot be called (#728). Eight schemas (mnemosyne_triple_end,mnemosyne_sync_push/pull/status,mnemosyne_persona_promote/demote/list/reinforce) were published over MCP without a dispatch handler, so everytools/callfor them failed withUnknown tool. The advertised surface is now filtered to the handler registry, and a parity test asserts the advertised set matches it exactly. -
The Hermes provider's failure diagnostic missed two virtualenvs over one base interpreter (#709).
register_memory_provider()compared_hp.resolve()againstPath(sys.executable).resolve(). A venv'sbin/pythonis a symlink to the interpreter it was created from, so resolving collapsed two distinct environments onto that one binary and skipped the diagnostic in exactly the case it exists to report; on macOS it also rewrote/tmpto/private/tmp. It now uses the_hermes_python_mismatch()helper added for #736, which compares environment roots, so the provider diagnostic andmnemosyne-hermes statusanswer the question the same way. That helper now normalises both sides withos.path.normpathbefore deriving the root: without it a path spelled<venv>/bin/../bin/pythonyielded<venv>/bin/.., which names<venv>but does not compare equal to it, so one environment was reported as two. Normalising is lexical and does not follow symlinks, so venv identity is preserved. -
Recall no longer silently misses leading-hyphen and symbolic query fragments (#744). Queries containing leading-hyphen fragments such as
rm -rfor--forcecould produce invalid FTS5 queries or no usable FTS terms (a token must start with a word character, and FTS5 treats a leading-as the NOT / column-exclusion operator), and symbolic code names such asC++orC#were dropped by the three-character meaningful-token gate, so recall returned an empty list without an error. Leading-hyphen fragments are now split into their components and matched through the FTS5 and lexical paths (-v-style single-character flags are included while stopwords and digits stay excluded); literal flag queries reject bare-component-only candidates regardless of configurable scoring weights. Symbolic code names are admitted as exact lexical tokens on both sides, soC++recalls memories containingC++without admittingc-token distractors. -
mnemosyne-hermes statusnow reports the real interpreter mismatch (#736). The warning compared interpreter paths but claimed a Python version mismatch and printed a bare version number instead of a runnable fix; it now compares the Hermes and installer environments and emits a shell-quoted→ Run: <python> -m pip install -U 'mnemosyne-hermes[all]'command. -
A query embedding whose dimension disagreed with the store's
vec0tables crashedrecall()(#753, fixed in #754)._vec_search(the episodic KNN overvec_episodes) executed its MATCH without exception handling, sosqlite3.OperationalError: Dimension mismatch for query vectorpropagated straight out ofrecall()and took down the calling process — while the write path (_wm_vec_upsert) logged and dropped the mismatched vector, and the working-memory KNN (_wm_vec_search_sqlite) already returned[]. Most often hit when a process resolves a differentMNEMOSYNE_EMBEDDING_DIMthan the one that dimensioned the store._vec_searchnow degrades the same way: vector recall is disabled for that call,recall()falls back to its other voices, and the log carries actionable guidance: the existing_dim_mismatch_message()self-heal steps when the configured dimension disagrees with the store, or a pointer at the embedding endpoint (explicitly not a reindex) when the endpoint serves a differently-dimensioned query vector while store and configuration agree. -
MCP SSE authentication rejects malformed non-ASCII bearer tokens with
401instead of returning a server error (#739). -
Thread-local SQLite connection churn no longer accumulates file descriptors. Connection creation now periodically runs process-wide cyclic-garbage collection, reclaiming unreachable SQLite handles without closing connections still referenced by live objects. Because collection scans all unreachable cycles, its occasional tail latency depends on process heap size.
-
API embedding failures now leave a redacted diagnostic trace (#735). Final HTTP, network, and invalid-response failures still degrade to keyword-only retrieval, but now log the endpoint and safe error class or status without request content, API keys, URL userinfo, query strings, or fragments.
-
Hermes tool discovery now honors
memory.mnemosyne.tools(#725). Tools outside the configured allowlist are no longer advertised through Hermes provider schemas before provider initialization. -
Truncated LLM reasoning traces no longer reach memory persistence (#734). Malformed or unbalanced
<think>output is rejected before fact extraction, model-refresh parsing, or sleep consolidation; sleep falls back to AAAK rather than persisting a partial LLM summary. -
Episodic degradation preserves atomic vector refreshes (#691). Refreshing sqlite-vec embeddings no longer commits inside a degradation savepoint, so a failed refresh rolls back its content and vector update together.
-
Hermes interpreter discovery accepted an unvalidated candidate (follow-up to #618/#620). #620 taught
_find_hermes_python()to follow a shell-wrapper launcher through itsexectarget, which fixed the reported case. Two paths still returned the wrong interpreter: a launcher that is neither a symlink nor anexecwrapper resolves to itself, so a siblingpythonin a shim directory such as~/.local/bin(commonly a Homebrew or system symlink) was still returned as "Hermes' Python"; and the known-install-root branches returnedcandidate.resolve(), which follows a venv'sbin/pythonsymlink to its base interpreter and discards the venv. An implicitly discovered candidate is now returned only when its directory is a real virtualenv (pyvenv.cfg) and its interpreter is executable, from the launcher, the install roots,sys.prefixandVIRTUAL_ENValike, and no branch resolves the interpreter symlink. An explicit non-empty--pythonstays authoritative and deliberately bypasses that validation; an empty one is rejected rather than falling through to discovery.--pythonis now authoritative and reaches symlink-mode discovery and--dry-run, where it previously affected only wrapper installs. Behavior change: a symlink install fails closed when no validated interpreter is found, naming--python, where it previously proceeded;--no-bootstrapcontinues without dependency validation, since it already installs nothing into Hermes' environment. -
Windows Git Bash/MSYS backup destinations no longer silently land on a drive-relative path (#659).
mnemosyne backup /c/...now writes to the intendedC:/...destination. Ambiguous POSIX-rooted destinations are rejected before backup creation instead of reporting success for a different location; native Windows, UNC, and relative paths remain supported. -
The built wheel now ships
hermes_memory_provider/plugin.yaml(#656).pyproject.tomldeclared nopackage-dataforhermes_memory_provider, so a normalpip install mnemosyne-memory(unlike an editable install) omitted the manifest Hermes' plugin loader requires, leaving the documentedhermes_memory_providersymlink install pointed at a directory with noplugin.yaml. -
Invalidation replacement links now require an accessible memory (#676).
mnemosyne_invalidaterejects an unknown or out-of-scope non-emptyreplacement_idbefore changing the target, so rejected replacements do not create links at invalidation time. -
bge-m3embedding alias resolves its 1024-dimensional vectors (#666). The unqualified model name now resolves identically toBAAI/bge-m3, avoiding an unknown-model startup error when no explicit dimension override is set. -
MCP invalidate now reports scope-safe failure (#660).
mnemosyne_invalidatereturnsmemory_not_foundinstead of claiming success when its target is outside the current scope or cannot be mutated, preserving scope isolation. -
Recall diagnostics were dead under
MNEMOSYNE_POLYPHONIC_RECALL=1. The polyphonic branch ofBeamMemory.recall()returned before the C4 recording block, so every recall that ran through the polyphonic engine (vector/graph/fact/temporal voices) never incrementedmnemosyne_recall_diagnosticscounters — the tool reportedcalls: 0under the flag that production deployments use. The polyphonic branch now records tier hits and call counts itself, mapping engine voices to the existing diagnostic tiers (vector→wm_vec,graph→em_vec,fact→em_fts). Recording is read-only signal and never alters recall behavior. Documented indocs/benchmarking.md. -
fallback_ratewas dead underMNEMOSYNE_POLYPHONIC_RECALL=1. The polyphonic diagnostics block (added in #668) recorded tier hits and call counts but neverrecord_fallback_used(), somnemosyne_recall_diagnosticsreportedwm_fallback_rate/em_fallback_rateas0on every polyphonic recall — including when the vector voice degraded from the sqlite-vec fast path to a numpy full-scan (sqlite-vec absent, failing, or its top-K ANN hits all dropped in the superseded/valid_until JOIN). The engine now exposes a per-call degraded-path flag and the polyphonic block records it asem_fallback_used.wm_fallback_ratestays0by design: the polyphonic engine has no substring-scoring tier for working memory. Recording is read-only signal and never alters recall behavior. Documented indocs/benchmarking.md. -
Persisted Enhanced Recall cache was stale after fresh
remember()writes (#556).BeamMemory.remember()now uses the established persisted-cache invalidation helper after successful new-memory and dedup-update writes, so a fresh writer evicts results warmed by another instance before the next fresh enhanced-recall request. Live peer in-memory coherence remains tracked separately in #552. -
Hermes wrapper runtime compatibility guard (#625). The legacy provider and newly registered persistent wrappers reject selected Mnemosyne site-packages whose virtualenv targets a different Python major/minor, or has an unreadable version, before activation/import; the error directs operators to recreate the Mnemosyne environment using Hermes' Python. Existing persistent wrapper artifacts must be force-refreshed or re-registered from a compatible Hermes-Python venv to receive this guard.
-
Vector rebuild failures are now reported explicitly (#603). Reindexing fails on incomplete embedding batches or derived-vector write failures.
vec_workingrepair also fails when its final coverage check remains incomplete.mnemosyne diagnose --repair-vec-workingreturns a non-zero exit code when a requested repair fails. -
Persona token-cap truncation (#621).
render_persona_markdownnow skips oversized topic sections and continues evaluating later sections, so smaller persona sections that still fit within the approximate token cap are retained. -
Silent hermes_plugin import failure in legacy provider (#649).
hermes_memory_provider/__init__.pyregister()replaced bareexcept Exception: passwithlogger.warning(...)so that failures to import the legacyhermes_plugin/directory are visible in logs. Previously, a missing__init__.py(or stale.pycfiles) silently prevented hook registration (pre_llm_call memory injection, tools) with no diagnostic output. -
degrade_batchnow honorsconfig.yamlat the BEAM consumer (#482). Episodic degradation resolvesdegrade_batchasconfig.yaml > MNEMOSYNE_DEGRADE_BATCH > 100once per complete degradation pass. Reloaded YAML applies to the next pass without changing the candidate limits of a running pass. -
BEAM recall weights now honor
config.yamlat runtime (#482).vec_weight,fts_weight, andimportance_weightnow resolve asconfig.yaml > MNEMOSYNE_*_WEIGHT > defaultsin direct and Hermes-provider recall paths. Reloaded weights apply to the next request, and enhanced recall cache entries are isolated by the effective weight snapshot. -
Packaged Hermes plugin manifests match the released package version (#588).
-
Hermes provider discovery and registration work through the provider
register()bridge (#565). -
Invalidating a nonexistent memory explicitly reports
memory_not_found(#542). -
sqlite-vec candidate retrieval is widened before working-memory filters (#608). Matching results are no longer excluded prematurely.
-
File-import dry run. File-import
--dry-runnow passes through the core, MCP, both Hermes providers, and CLI surfaces to clone-based validation without changing the active database or audit data. Dry-run responses report"status": "dry_run"so clients cannot mistake simulated import statistics for a completed import. -
Repaired
hygiene audit --json→hygiene cleanworkflow (#606).hygiene cleannow unwraps the audit envelope produced byhygiene audit --jsonand validates each candidate before cleanup. Raw candidate arrays remain supported, and candidates with persistedimportancevalues outside[0, 1]are accepted so the audit-to-clean pipeline completes without manual editing. -
Model-refresh confidence: NaN cleared every gate and legacy text crashed sleep mid-batch. JSON round-trips NaN and Infinity, and
parse_model_update_proposalsclamped NaN to 1.0 (minandmaxkeep their first argument when a NaN comparison is False), so a NaN-confidence proposal became a top-importance memory; the auto-apply gate'sconfidence < minimumcheck is also False for NaN, so the same proposal reached the canonical store regardless of threshold. Separately,apply_model_refresh_proposaland sleep()'s proposal-remember call site converted stored confidence with a barefloat(), so a legacy bank's text value (for example"high") raised ValueError. The sleep call site runs after the claim commit, so that raise stranded the group'sconsolidation_claimed_atand orphaned every later group's claimed rows. Non-numeric and non-finite confidence now degrades per site: skipped at parse, 0.0 at the auto-apply gate, 0.5 at apply and at proposal importance. Finite values outside [0.0, 1.0] clamp to the domain bound on every path before auto-apply threshold checks and canonical storage, so a persisted 2.0 cannot remain unbounded. Hardening split out of #546 per review. -
Russian and Spanish MEMORIA patterns contained literal backslash escapes (#560). The
ruinstruction pattern was written with\\\\s+and[^.,;!?\\\\n]inside a raw string, so it required a literal backslash in the text and Russian instruction extraction matched nothing at all. Sixespatterns (negation,decision,entity,sequence,instruction,preference) carried the same doubled\\\\n, which turned the newline exclusion into an exclusion of the letternand truncated every capture at the firstn. Both are now single-escaped, and the locale guard test intests/test_memoria_instruction_boundaries.pyrejects any future doubled escape. -
Hermes session switches left Mnemosyne memory bound to the previous session (#601). The standalone
mnemosyne-hermesprovider now rebinds itsBeamMemorysession when Hermes rotates the agent session through/new,/resume,/branch, undo, or context compression, so subsequent writes, reads, and tools use the active session.
test_stored_offset_bearing_valid_until_chronologically_filteredfailed for two hours every day (#525 follow-up). The test stores a space-separated naivevalid_untiltwo hours ahead and asserts a lexical filter drops it, since a space separator sorts before aTseparator. That only holds while the date components match. Between 22:00 and 00:00 UTC the value rolled into tomorrow, sorted after aware-UTC now, and the sanity assertion failed on an unchanged tree. The naive value is now clamped to the current UTC date, behind a 30-second validity margin so a clamp near midnight cannot leave the row expiring mid-test, so the trap it sets actually holds, verified across all 1440 minutes of the day. Behavior under test is unchanged; this was a defect in the fixture, not invalid_untilhandling.- MEMORIA instruction extraction inverted "whenever X" into "never X" (#507). The instruction pattern was not word-boundary anchored, so
nevermatched insidewheneverand the extractor stored the opposite of what the user said — on a production bank, "Good - whenever needed we can use it." was recorded as the instruction "never needed we can use it". All five locale patterns (en/de/ru/it/es) are now anchored with a leading\b. Genuine instructions are unaffected, including those preceded by another word or punctuation ("Note: never push to main", "wherever you go, always run the tests"). Reported by @Axmr1 from a 61-row production audit; original diagnosis and fix approach from @Sanjays2402 (#508) and @Souptik96 (#549). mnemosyne_recallcrashed on its own schema default (#555). The tool schema declaredquery_timewith"default": "", but_parse_query_timemapped onlyNoneto "now" — a blank string fell through to the ISO parser and raisedInvalid query_time format: ''. Any MCP harness that sends declared defaults could not callmnemosyne_recallat all. Blank and whitespace-only values are now treated as unset, the MCP handler normalizes""toNone(matching theor Noneidiom already used forvalid_untilandas_of), and the schema no longer advertises a default that means "omitted". Thanks @dalkommatt for the report and the diagnosis.- Enhanced Recall served invalidated rows until TTL expiry (#550, #554).
BeamMemory.invalidate()now clears the query cache after a successful update, including the persistedquery_cache.dbwhen the instance has no in-memory cache of its own. Missing or unauthorized IDs leave the cache untouched. Remaining gaps are tracked in #552 (live peer coherence) and #553 (forget_working). - Catastrophic regex backtracking in version-string extraction (#544). The pattern used by
extract_and_store_factscould be driven into exponential backtracking by Title-Case input, hanging everyremember()and import on attacker- or user-supplied content. The separator is now\s+, which makes each whitespace-delimited word consumable exactly one way. Behavioral equivalence was verified across a 200,000-string fuzz with zero differences.
Never published to PyPI. No
v3.15.0tag was cut, so this section documents work that first reached users in 3.15.1. Kept for history rather than folded, so the individual changes stay attributable.
- Memory browser startup and bank resolution (#532). The browser now renders its CSS template safely, resolves the default and named-bank databases using the canonical paths, and opens databases read-only so a missing path cannot create an empty database.
- Trim-before-embedding race (#491). Working-memory embedding storage now atomically checks that its parent row still exists. If trimming or concurrent deletion removes the parent before the fallback insert executes, both fallback and
vec_workingwrites become a clean no-op instead of logging an embedding-storage failure. - Jina v2 base embedding models silently fell back to 384 dimensions. The
jinaai/jina-embeddings-v2-base-{es,en,de,zh,code}models output 768-dim vectors, but were absent from_get_embedding_dim's table and so resolved to the 384 unknown-model fallback — a silent dimension mismatch that corrupts vector similarity search for anyone using these popular models (notably the-esSpanish/English bilingual model). Added explicit 768-dim entries plus a regression test intests/test_embeddings_multilingual.py.
- Write-approval gate for Mnemosyne memory writes (#456). When
memory.write_approval: trueis set in Hermes config.yaml,mnemosyne_rememberandmnemosyne_batchstage writes topending/memory/<id>.jsoninstead of committing directly. The newmnemosyne_apply_pendingtool replays approved records through the BEAM write path. Both standalone and bundled Hermes providers are supported. mnemosyne_forget_canonicaltool (#435). Completes the CRUD surface for canonical facts: remember, recall, and now forget (retire) a slot. Stampsvalid_untilon the current row, preserving history. Nothing is deleted.- Safe doctor and selected repair workflow. The
mnemosyne doctorcommand now supports a--safemode that performs read-only diagnostics, plus a--repairmode that can fix selected issues (orphaned references, WAL cleanup, vec_working migration gaps). - Query/document embedding prompt prefix env vars (#401).
MNEMOSYNE_EMBEDDING_QUERY_PREFIXandMNEMOSYNE_EMBEDDING_DOCUMENT_PREFIXallow customizing embedding prompt prefixes for BGE-style models. - Shared-surface sync hardening (#442). Blind-relay security: sync event payloads are sanitized before public broadcast, cross-model security findings are closed, and the sync server binds to a dedicated shared surface.
- CLA requirement for contributors. All PRs now require a signed Contributor License Agreement. Branch protection enforces the
license/clacheck on main.
- Config set crash (#481).
mnemosyne config setno longer crashes with AttributeError after writing the value. TheREQUIRES_RESTARTcheck now imports from the module-level set. - Expired Discord links (#479). All Discord invite links updated to
discord.gg/nousresearch. - onnxruntime thread affinity spam in LXC containers (#453).
TextEmbeddingnow receives an explicitthreads=parameter, preventingpthread_setaffinity_npEINVAL errors in unprivileged containers. Override withMNEMOSYNE_EMBEDDING_THREADSenv var. - Polyphonic recall collapse (#389).
_estimate_similaritynow uses word-level content Jaccard instead of voice-name Jaccard, preventing MMR diversity reranking from collapsing to a single result when one voice dominates. - Content mutation (#387). Temporal annotations (
[DATES:],[DURATIONS:]) are now stored in metadata instead of appended to the content field, preserving byte-identical content for verbatim-reproduction workflows. - Polyphonic content hydration (#471).
PolyphonicResultnow carries acontentattribute, hydrated from the database before diversity ranking, so the content-Jaccard scorer works correctly. - Main CI contracts restored (#480). Post-merge CI test alignment fixed, restoring green CI on main.
- Unsafe connection lifecycle change reverted (#477, #382). The
__del__-based WAL cleanup was reverted. Safe multi-DB lifecycle support needs a defined lease contract, not a destructor. - Embedding API retries (#475, #478). HTTP 429/5xx and transient network failures now receive bounded exponential-backoff retries with jitter. Permanent 4xx errors still fail fast.
- Hermes provider diagnostics.
mnemosyne diagnosenow resolves logs underHERMES_HOMEwhen set, and the doctor tool respects the resolved bank. - Local LLM SSE errors (#447). Streaming set to false to prevent SSE errors on certain local LLM backends.
- Legacy memory_embeddings FK migration (#452). Databases created by old DDL now migrate their foreign keys correctly on init.
- Hyphenated recall query expansion. Compound query tokens are now expanded for better matching.
- Hermes provider defaults after config bridge. New auto-seeded configs preserve user-only autosave and skip noisy contexts.
- Code audit cleanup (#460). Callable import, duplicate import, and F-rules linting fixed.
- Sync event payloads are sanitized before public broadcast.
- Cross-model security findings in sync layer closed.
- Branch protection enforces
license/clacheck on main (no admin bypass).FOREIGN KEY (memory_id) REFERENCES memories(id)constraint onmemory_embeddings. Thememoriestable is unused — working_memory ids are stored instead. WhenPRAGMA foreign_keys=ONwas enabled (#408), every embedding insert silently failed withIntegrityError: FOREIGN KEY constraint failed. This release adds an idempotent migration that rebuilds the table without the FK and removes the FK from thememory.pyDDL so fresh databases are clean. mcp_tools.pyvalidate(delete) path now cascades to child rows. The bareDELETE FROM working_memorypreviously left orphanedmemory_embeddings,annotations, andvec_workingrows behind. The path now deletes all dependent rows before removing the parent, with guarded vec_working handling for sqlite-vec-unavailable environments.
-
Config reload now bridges to the Hermes provider.
mnemosyne config setandmnemosyne config reloadpreviously wrote to the Mnemosyne config.yaml but the Hermes provider only read from the Hermes config.yaml (memory.mnemosyne.<key>). The two files never connected, so config changes appeared to do nothing. Now the provider falls back to the Mnemosyne config singleton when the Hermes config has no value, andMnemosyneConfig.get()auto-reloads on file mtime changes soconfig settakes effect immediately without an explicit reload. -
Config.yaml auto-seed on all entry points. The auto-seed now fires on
Mnemosyne()andBeamMemory()init, not just explicit config imports. Idempotent — checks file existence first. -
Test isolation for config auto-seed. Config profile tests now create an empty config.yaml before init so the auto-seed doesn't override test env vars with defaults.
- Config.yaml auto-seed on first access. Mnemosyne now creates a
config.yamlat the standard location with all 106 known keys and their default values. The file is created automatically on first access — no manual setup needed. For each key, if the corresponding env var is set, its value is used instead of the default, ensuring existing env var configurations are never silently overridden. Hot-reload withmnemosyne config reload. Precedence unchanged: config.yaml > env varshardcoded defaults.
- Config.yaml auto-seed respects existing env vars. The initial
implementation wrote all defaults blindly, which would silently override
any
MNEMOSYNE_*env vars the user had set (since config.yaml takes precedence over env vars). Now each key checks for an active env var before writing. Type coercion is applied: env var strings are parsed as bool/int/float to match the default type.
-
Config.yaml system with profiles, hot-reload, and write filters. Mnemosyne now supports profile-based configuration, hot-reloading config changes without restart, and write filters for fine-grained control over what gets stored. (#431, #433)
-
MNEMOSYNE_CROSS_SESSIONenv var for cross-session recall. When set,recall()searches across all sessions instead of only the current one. (#371) -
Atomic
mnemosyne_batchtool. Batch multiple memory operations (remember, update, forget, invalidate) in a single atomic transaction via the Hermes provider. (#400) -
Sync turn diagnostics.
sync_turnnow exposes diagnostic information for debugging sync pipeline issues. (#115162b) -
Read-only doctor hygiene signals. The doctor diagnostic tool now reports hygiene signals (foreign key gaps, orphaned rows, stale connections) without requiring write access. (#71e013d)
-
Orphan diagnostics to doctor. Doctor now detects orphaned memory rows with no corresponding FTS5 or embedding entries. (#417)
-
CLI bank selection, bank list, and schema migration.
mnemosyne storeand other CLI commands now honorMNEMOSYNE_BANK. Newmnemosyne bank listcommand for multi-tenant visibility. Newmnemosyne migratecommand for 3.11.0-era banks. (#404) -
Hermes memory providers skill v2.0.0. Bundled skill for the Hermes ecosystem documenting all memory providers. (#4ee3a58)
-
Zero and Pi agent integrations. Mnemosyne now integrates with the Zero agent framework and Pi agent. (#418, #c0a7176)
-
Layered agent memory roadmap. Architecture document defining the L0-L4 memory layer model for AI agents. (#96e6978)
-
MNEMOSYNE_ENHANCED_RECALL=1now routes through the full enhanced recall pipeline.Mnemosyne.recall()always calledbeam.recall()directly, bypassingbeam.recall_enhanced()entirely. The flag had zero effect on production call paths. Now routes torecall_enhanced()when the flag is set. (#436, reported by @ValentinSergief with full RCA) -
SSE transport Route handlers no longer crash Starlette. Route handlers returning
Nonecaused Starlette crashes in SSE transport mode. (#383) -
Diagnostics fallback DB path now respects
HERMES_HOME. The diagnostics tool used a hardcoded fallback path instead of resolving fromHERMES_HOME. (#384) -
Veracity forwarding through
Mnemosyne.remember(). The module-levelremember()function now forwards the veracity argument to the underlying beam, fixing the MCP remember handler silently dropping veracity. (#399, #386) -
Namespace collision:
tools/renamed to_benchmarks/. Thetools/directory collided withhermes-agenttool discovery. Renamed to avoid the conflict. (#9ca278a) -
Profile bank resolution in standalone CLI. CLI commands loaded standalone now correctly resolve the profile bank. (#6725b80)
-
ASGI middleware replaced with pure-ASGI bearer auth. Replaced
BaseHTTPMiddlewarewith a pure-ASGI approach for bearer auth in MCP SSE transport, fixing Mount compatibility. (#be8c865) -
Current-state recall ranking. Fixed a bug where recall ranking used stale scores instead of current-state values. (#416)
-
Bank name validation before path operations. Bank names are now validated before any filesystem path operations, preventing directory traversal and invalid characters. (#415)
-
SQLite write lock released across consolidation LLM calls.
BeamMemoryno longer holds the SQLite write lock while waiting for LLM consolidation responses, preventing WAL checkpoint blocking. (#432, reported by @kirocop in #382) -
Recall-touch transaction rolled back on failure. The recall-touch UPDATE now properly rolls back the transaction on failure instead of leaving a stale write lock. (#f418044)
-
PRAGMA foreign_keys=ONin both connection factories. Foreign key enforcement is now enabled in both the main and the thread-local connection factories. (#408, reported by @Iman-Sharif) -
Hygiene audit CLI and table handling hardened. The doctor CLI now handles edge cases in table detection and reporting. (#f072e1a)
-
Host LLM timeout now configurable. Added
MNEMOSYNE_LLM_TIMEOUTenv var (default 60s) for remote LLM consolidation and extraction calls. (#d290193) -
Hermes provider fixes (6 commits):
- Auto-sleep default enabled across both provider surfaces (#429)
- Bundled memory override skill installer (#424)
- Cross-session recall and CLI default scope (#422)
- Pip sync adapter parity with core (#419)
- L3 persona prompt parity restored (#ed05503)
HERMES_HOMEleak in CLI bank test (#6664e81)
- Default prompt context excludes consolidated working-memory rows.
BeamMemory.get_context()no longer includes rows whereconsolidated_at IS NOT NULL. SetMNEMOSYNE_CONTEXT_INCLUDE_CONSOLIDATED=1to restore legacy behavior. (#427)
- Installation steps revised for Hermes users (#414, @bruvv)
- Pi agent integration docs added (#c0a7176)
- Hermes Tweet compatibility table (#e032008, @Burak Bayır)
.coderabbit.yamlwith grouped reviews and architectural rigor (#da4832a)
@dplush (Denis H) — 11 commits: sync diagnostics, recall ranking, bank validation, orphan detection, auto-sleep, cross-session recall, batch tool, L3 persona, hygiene audit, veracity forwarding, pip sync parity
@codxt — 3 commits: CLI bank selection + migration, ASGI middleware fix, layered memory roadmap
@Milgauss — 2 commits: SQLite write lock fix, recall-touch rollback
@TurgutKural — 2 commits: profile bank resolution, host LLM timeout
@ValentinSergief — thorough ENHANCED_RECALL RCA with file+line references
@PlainWu, @ClaytonChew, @bruvv, @justanotherAIcontributor, @BurakBayır, @Iman-Sharif, @webtecnica — bug reports, fixes, and docs improvements
3.11.0 - 2026-06-30
-
Automated sleep model refresh. During
sleep(), Mnemosyne now asks the LLM for structured candidate updates to canonical model slots (user model, workflow model, project model). Validates the LLM response against the expected schema, generates proposals with confidence scores, and auto-applies or auto-rejects them by policy. Newmnemosyne_model_refreshdiagnostic tool for inspecting proposal outcomes. -
Recall diagnostics and task progress tools.
mnemosyne_recall_diagnosticsexposes per-row recall scoring breakdowns (weights, scores, signal contributions) for debugging hybrid ranking.mnemosyne_task_progresstracks multi-step task state across sessions with create/update/get/list operations. -
MNEMOSYNE_LLM_TIMEOUTenv var. Configurable HTTP timeout for remote LLM consolidation and extraction calls (default 60s). Useful for deployments routing through local proxies or models with long generation times. (#375) -
Tool whitelist allowlist. Hermes Mnemosyne providers can now restrict exposed tools with the optional
memory.mnemosyne.toolsconfig key while preserving memory context and prefetch behavior. Unknown names raise a clear startup error so typos don't silently lose tools. -
Hermes wrapper install mode for read-only / Docker deployments.
mnemosyne-hermes install --mode wrapper --python <path>creates a stable$HERMES_HOME/plugins/mnemosyne/shim that imports from the selected Python environment instead of symlinking into a rebuildable Hermes venv.mnemosyne-hermes statusreports wrapper mode, target interpreter, import health, and stale/broken targets.
-
Tool schemas consolidated to single source of truth. All 37+ tool schema definitions moved from duplicate copies in
hermes_memory_provider/__init__.pyandintegrations/hermes/src/mnemosyne_hermes/tools.pyto a sharedmnemosyne/tool_schemas.pymodule. Both provider copies import from the canonical source, ensuring tool definitions stay in sync. -
Hermes sync role default now saves user turns only. The
sync_rolesdefault changed from["user", "assistant"]to["user"]so automatic turn autosave avoids assistant transcript noise. Setmemory.mnemosyne.sync_roles: ["user", "assistant"]inconfig.yamlto restore the prior behavior.
-
mnemosyne backupnow works with sqlite-vec databases.create_backup()loads the sqlite-vec extension on backup connections soiterdump()andConnection.backup()can serialize vec0 virtual tables. Previously raisedOperationalError: no such module: vec0on all 3.10.x installs. -
Named Hermes profiles now get the plugin link (issue #365). Both
mnemosyne-installandmnemosyne-hermes installnow scan~/.hermes/profiles/*/config.yamlformemory.provider: mnemosyne, creating or removing the plugin symlink in each matching profile'splugins/directory. Previously the link was only created under the default~/.hermes/. -
Host LLM backend registration in skip-context sessions.
register_hermes_host_llm()was at the end ofinitialize(), after the skip-context early return. Cron, subagent, and background sessions never reached it, somnemosyne_sleepsilently fell back to AAAK. Registration now fires before the skip-context check;shutdown()only unregisters when the session is not in a skip context (#368, supersedes #361). -
HERMES_HOMErespected for fastembed cache default. The default ONNX model cache path resolves to<HERMES_HOME>/cache/fastembed(falling back to~/.hermes/cache/fastembed).MNEMOSYNE_FASTEMBED_CACHE_DIRstill overrides. -
mnemosyneCLI bank-aware underprofile_isolation. CLI commands (stats,inspect,sleep,export) now resolve the active profile bank instead of always reading the default bank, which reported empty state when the profile bank held the data. (#362, #363) -
Scope model refresh auto-apply edge cases. The auto-apply logic in sleep's model-refresh pass now handles edge cases around session boundaries and empty proposal sets.
3.10.1 - 2026-06-22
- Fix critical JWT signature verification bypass in sync server
(GHSA-xcw4-53cc-hv32,
CVSS 9.1). The sync server's authentication check decoded JWT bearer
tokens but never verified their HMAC-SHA256 signatures, allowing any
well-formed token (including
alg: none) to be accepted. An unauthenticated attacker with network access to the sync endpoint could impersonate any user, read their sync state, and push malicious sync state to corrupt the local database.- Replaces broken decode with a from-scratch HS256 verifier
- Constant-time signature comparison via
hmac.compare_digest - Strict
alg: HS256allowlist (rejectsnone, RS256, etc.) - UTC-aware
expvalidation with leeway - Loud, specific error messages
- Reported by Denis Hache (@dplush) via private channel on 2026-06-13
- Patched on 2026-06-19 (commit
a0b6b871)
pip install --upgrade mnemosyne-memory==3.10.1If you operate a sync server with network exposure, upgrade immediately. If you cannot upgrade right away, restrict network access to the sync endpoint (firewall, reverse proxy with mTLS, or localhost bind with SSH tunnel). The vulnerability is not exploitable against an unreachable endpoint.
- hermes integration:
hermes mnemosyne <stats|sleep|inspect|export>are now bank-aware underprofile_isolation— they resolve the active profile bank (or an explicit--bank) instead of always reading the default bank, which reported empty state when the profile bank held the data. (#362, #363)
3.10.0 - 2026-06-18
- L3 persona layer — always-on behavioral rules tier that survives past
the 24-hour working-memory TTL. New
memoria_personaSQLite table with tiered retention (permanent/long_term/working). New tools:mnemosyne_persona_promote,mnemosyne_persona_demote,mnemosyne_persona_list,mnemosyne_persona_reinforce. - Rule-based persona extractor (no LLM by default). Reads working_memory and episodic_memory, filters by source/importance, deduplicates by topic, renders Markdown grouped by topic. Deterministic and zero-cost.
- Auto-injection into system prompt via
persona.md. Reads~/.hermes/memory/persona.mdand includes it in thesystem_prompt_block()of the hermes provider. Feature-gated byMNEMOSYNE_PERSONA_ENABLED=true(default OFF). Mtime-cached for hot-path efficiency. Token cap enforced (MNEMOSYNE_PERSONA_TOKEN_CAP, default 1500). - 5 trigger conditions for persona regeneration (matches Hy-Memory PersonaTrigger pattern): explicit request, cold start, recovery, threshold (default 50 new memories), daily sync window.
- Schema migration is additive; existing tables untouched.
- Tool count: 28 -> 32.
- No breaking changes to existing
mnemosyne_remember/mnemosyne_recallbehavior. - Default OFF to preserve opt-in upgrade story; turn on with
MNEMOSYNE_PERSONA_ENABLED=trueafter upgrading.
3.9.0 - 2026-06-18
- Synchronous memory reindex (issue #308, PR by @Milgauss). New
mnemosyne reindexcommand that rebuilds all vectors (working, episodic, facts) after an embedding model or dimension change. Reuses existing write helpers for consistent encodings across all five representations. Auto-backup first,--dry-run,--model,--no-backup,--yes. Synchronous/blocking with a duration warning. - vec_working migration diagnostics (contributed by Denis H).
mnemosyne diagnose --repair-vec-workingreports migration coverage and idempotently backfills missing vec_working rows from the memory_embeddings fallback. - Bidirectional memory sync with optional client-side encryption
(issue #287). Event-log-based delta sync between Mnemosyne instances using
the SyncEngine protocol:
memory_eventstable: append-only event log with conflict detection- stdlib-only HTTP sync server (no FastAPI deps)
mnemosyne sync,sync-serve,sync-status,sync-generate-keyCLI- Encrypted payload detection and causal version chains for conflict resolution
- Sync tutorial, troubleshooting guide, and deploy configs (Docker, Caddy, Fly.io)
- Hermes plugin improvements:
mnemosyne-hermes upgrade— smart install-method detection (pipx / uv-tool / pip), version comparison, auto re-register after upgrade (PR #319)mnemosyne-hermes cleanup— removes plugin, old hermes-mnemosyne dir, resets config;--dry-runsafe (PR #317)mnemosyne-hermes statusnow shows Hermes' Python version + mismatch warning (PR #316)install --dry-runfor safe pre-flight checks- Sync tool schemas (SYNC_PUSH, SYNC_PULL, SYNC_STATUS) added to both provider copies. Total tool count: 25 -> 28
- Sleep orphan-claim recovery (issue #293). Added
reclaim_orphans()to clear stale consolidation claims whensleep()was interrupted after claiming working-memory rows but before writing an episodic summary.
- vec_working dedicated table for working vector search (contributed by Denis H). Working-memory vectors now live in a dedicated sqlite-vec table, with memory_embeddings as the compatibility fallback. New rows written to both, recall prefers vec_working when available. Import/backfill paths mirror to both stores.
- CLI version no longer depends on
__author__(removed in v3.7.0). Imports__version__only for resilience across releases. - Lower prefetch noise from raw conversation turns. sync_turn() now writes user messages at 0.5 importance (was 0.3) and assistant messages at 0.15 (was 0.2).
- auto-sleep uses
sleep_all_sessions()causing timeout (issue #342, PR by @ruangraung)._maybe_auto_sleep()calledsleep_all_sessions()which loops ALL sessions instead of just the current one, always exceeding the timeout on databases with many sessions. Now uses session-scopedbeam.sleep(). - daemon thread SQLite connection race (issue #342, PR by @ruangraung). Both
_maybe_auto_sleep()andon_session_end()ranbeam.sleep()in daemon threads but reusedself._beam.conn(the same SQLite connection as the main thread). Concurrent writes caused silent episodic INSERT failures. Now creates isolatedBeamMemoryinstances in daemon threads so each gets its own connection via_thread_local. - fact_recall ranking by query relevance (issue #309, PR by @Milgauss).
fact_recall() now preserves FTS rank order (was re-ordering by stored
confidence, collapsing all facts from the same path to one score), uses
relevance * confidencescoring, and returns full subject-predicate-object triples as content. Opt-in viaMNEMOSYNE_FACT_RECALL_ENABLED. - Audit log table renamed to
audit_logto avoid collision with the sync engine'smemory_eventstable. Both were creating tables namedmemory_eventswith incompatible schemas — the audit silently failed on INSERT after beam.py created its version first. - UTC Z timestamp parsing on Python 3.10 in sync conflict detection.
Normalizes trailing
Zbeforedatetime.fromisoformat(). - Security docs corrected — documentation claimed XChaCha20 and keyring
integration; actual code uses Fernet/XSalsa20 and key-manager-only key
sources.
from_config()scope fixed. - Provider diagnostic messages —
register_memory_provider()now catches construction failures and prints the actual exception, Python version, and Hermes' Python info to stderr instead of a vague warning.
- Dedicated vec_working table — working vector search uses a focused sqlite-vec table instead of the shared memory_embeddings table, reducing candidate set size.
- Query embedding cached once per recall() call (PR #298). Previously the embedding model was invoked multiple times from different filter paths within the same recall.
- Get_context hot path split (contributed by Denis H). Separate global and session queries with targeted indexes instead of a broad OR or temporary-sort query shape.
3.7.0 - 2026-06-13
- Usage-driven working memory decay (issue #289). Memory now lives longer
(default TTL 168h, was 24h), and frequently recalled items get their TTL
bumped (capped at
MNEMOSYNE_WM_BUMP_CAP_HOURS, default 24h per bump).MNEMOSYNE_WM_BUMP_CAP_HOURSenv var — configurable refresh ceilingMNEMOSYNE_WM_PINNED_IDSenv var — comma-separated memory IDs to pinpinnedcolumn onworking_memory— sleep consolidation skips pinned items
- Temporal-triple lifecycle re-applied (issue #246 regression). Triple
supersede/valid_until/endlifecycle was absent from v3.5.0 and v3.6.0 despite appearing merged. Re-applied cleanly. Newmnemosyne_triple_endtool andend_triple()module function added. - Optional local LLM fallback log level.
diagnoseno longer logs a warning when the optional fallback model is absent. sleep(force=False)assertion corrected. Theforceflag path now works without throwing.HERMES_HOMEresolution priority. CheckHERMES_HOMEenv var before falling back toPath.home()across beam, banks, memory, and integration files.- Packaging cleanup:
openclawdependency removed from[all]extra. Python 3.9 classifier dropped (3.10+ only).
3.6.0 - 2026-06-10
-
Owner-scoped canonical (single-source-of-truth) facts (issue #256). A new
CanonicalStore(mnemosyne/core/canonical.py) gives long-running personas an identity layer where each(owner_id, category, name)slot holds exactly one current value. Restating a stable self-fact is a no-op (no duplicate accumulation); a new value supersedes the old one, which is preserved as history — the TripleStorevalid_untilpattern, extended with an owner dimension. Implemented as one SQLite table plus a partial unique index (… WHERE valid_until IS NULL); no new dependency, no FTS table.- Two new tools,
mnemosyne_remember_canonicalandmnemosyne_recall_canonical(the latter covers exact-slot read, category/whole-bank listing, version history, and owner-scoped substring search). Exposed on both the Hermes provider and the MCP surface — total tool count 23 → 25. BeamMemorynow exposesself.canonical, sharing its thread-local connection (no extra file descriptor), mirroringself.annotations.- Owner isolation is enforced by construction: the provider derives
owner_idfrom the active profile identity and never reads it from tool args, so one profile cannot read or write another's canonical bank. The shared surface is untouched and keeps its cross-profile role. - Fully additive and opt-in: the
canonical_factstable is created lazily on first init; existing tables, tools, and recall output are unchanged.
- Two new tools,
-
Hermes Holographic Memory importer (
mnemosyne/core/importers/holographic.py). Reads directly from Hermes' SQLite-based holographic memory plugin (~/.hermes/memory_store.db) — preserves content, category, tags, trust scores, timestamps, and entity links. Trust scores map to Mnemosyne importance (both 0-1). Entity extraction flag passes through tomnemosyne.remember()for annotation-store entity recall. Category/tag/min_trust filtering for targeted imports. Fully dry-run compatible. (--from holographic) -
API embedding fallback chain.
embed()andembed_query()now fall through to local fastembed when the API embedding call fails (network outage, rate limit, timeout). The fallback model is configurable viaMNEMOSYNE_EMBEDDING_FALLBACK_MODEL(default:BAAI/bge-small-en-v1.5).available()now accounts for fallback capability, so recall doesn't skip vector search just because the API is down. (#269)
-
Fact recall no longer treats one plain shared word as relevance for broad queries. Single-token fact matches are now limited to lookup-style queries or distinctive structured identifiers, preventing unrelated high-importance facts from surfacing on conversational glue words while preserving direct lookups.
-
Holographic import CLI no longer demands an API key. Holographic is a local SQLite importer (no API key needed) but the generic provider path checked for
--api-keyon every non-hindsightprovider. Added--db-pathand--min-trustCLI flags and a holographic special case (same pattern as hindsight) that skips the key gate. Import parity with docs atapi-reference.mdis now operational. -
Provider registration + db_path on non-isolated init (fixes #254, #255).
register()now callsregister_memory_provider()— the provider was silently failing to load.BeamMemory()now derivesdb_pathfromhermes_homewhen available instead of falling back toPath.home(), preventing silent data loss across processes. Installer auto-cleans oldhermes-mnemosyneplugin directory and migrates config. -
Embeddings deps are now unconditional. Vector search (fastembed + sqlite-vec) is not optional — it's what makes recall work. The
[embeddings]extra is now a hard dependency, so fresh installs don't silently ship with FTS5-only keyword search. -
Hermes host LLM registration in CLI path. Both copies of
cli.pynow callregister_hermes_host_llm()before creatingBeamMemory. Previously the registration only happened insideMnemosyneMemoryProvider.initialize()which the CLI handler never hits, soMNEMOSYNE_HOST_LLM_ENABLED=truewas silently ignored when runninghermes mnemosyne sleepfrom the terminal. -
Per-entity identity injection in prefetch. The provider now includes per-contact identity memories in every prefetch regardless of recall query, ensuring the agent always has the user's stable self-descriptors without requiring an explicit identity search.
-
Entity performance: skip Levenshtein when length ratio rules out a match. The prefix-guard branch now bails out early when the token length ratio exceeds a threshold, avoiding expensive string edits on obviously non-matching candidates.
-
Docs generator overhaul. Rewritten to be merge-conflict-free, single-source ground truth (24 MCP tools, 9 config keys), canonical copies always written to
docs/api/. Website sibling writes guarded withisdir+isfilechecks. Removed ghostmnemosyne_endtool (23 real tools). Plugin path corrected from~/.hermes/plugins/memory/mnemosyne/to~/.hermes/plugins/mnemosyne/. Switched from hardcodedpython3.11path to dynamic resolution.
- Recall relevance before importance (contributed by WXBR). Proves high-importance unrelated memories cannot surface for an unrelated query. Locks in the invariant that importance may boost ordering only after a candidate has passed relevance, instead of rescuing unrelated rows.
3.4.0 - 2026-06-01
- Known dimensions for local SentenceTransformers multilingual models.
paraphrase-multilingual-MiniLM-L12-v2,all-MiniLM-L6-v2, andparaphrase-multilingual-mpnet-base-v2are now listed for low-resource local multilingual embedding setups.
- Unicode recall tokenization for Latin-script languages. Recall lexical
gates now keep diacritics inside tokens, so words like
Stoßlüften,Bürgeramt, andPrimärquellenare no longer split into ASCII fragments.
sync_rolesconfig for role-based autosave filtering.sync_turn()now checksmemory.mnemosyne.sync_rolesbefore persisting conversation turns. Default["user", "assistant"]preserves existing behavior. Set to["user"]to save only user turns, or[]to disable conversation autosave while keeping explicitmnemosyne_remembercalls working. Unknown roles are warned and ignored. (Contributed by bitr8, closes #209.)MNEMOSYNE_SYNC_TURN_USER_LIMIT/MNEMOSYNE_SYNC_TURN_ASSISTANT_LIMITenv vars.sync_turn()now respects configurable truncation limits instead of hardcoded 500/800 slices. Defaults to500(user) and800(assistant) for backward compatibility. Set to0to disable truncation.- Fact recall merged into standard
beam.recall()path. SetMNEMOSYNE_FACT_RECALL_ENABLED=1to merge LLM-extracted facts (fromextract=true) into recall results. Facts are deduplicated against regular memories by content hash and scored at 0.9x their confidence. - Auto-default
scope=globalwhenextract=true. If a caller doesn't explicitly passscope, settingextract=truenow infersscope=globalinstead of the defaultsession. Explicit scope overrides are respected. fact_recall()now searchesconsolidated_facts(sleep-consolidated fact triples) in addition to the rawfactstable. Previously only accessible through polyphonic recall (MNEMOSYNE_POLYPHONIC_RECALL=1). Fact data stored withextract=trueis now visible through the default recall path.MNEMOSYNE_EMBEDDING_API_URLindependent ofOPENROUTER_BASE_URL. Embedding models can now use local llama.cpp, OpenAI, Anthropic, or any other provider without requiring OpenRouter configuration. Also fixes a bug where_OPENAI_BASE_URLwas stale after env read. (Contributed by mia-fourier, PR #206.)
remember()silently never stored embeddings. Onlyremember_batch()called_vec_insert(). The Hermes provider usesremember(), so thousands of working memories had no vectors, making conflict detection always a no-op and degrading vector recall quality. Added_vec_insert()call toremember(). Threshold for conflict detection relaxed from 0.92 to 0.88 (32 conflicts found vs 23 in real data).- Hardcoded embedding dimension in
binary_vectors.py.EMBEDDING_DIMwas hardcoded to 384 (bge-small-en-v1.5), causingmaximally_informative_binarizationto silently truncate larger embeddings (e.g. 1024-dim multilingual-e5-large) to the first 384 components, losing up to 62.5% of vector information. The dimension is now derived frommnemosyne.core.embeddings.EMBEDDING_DIMat import time with a 384 fallback when the embeddings module is unavailable.BYTES_PER_VECTOR,compression_ratio, andtheoretical_size_mbinget_stats()are likewise computed from the resolved dimension instead of hardcoded constants. (Contributed by Whishp, PR #200.) - Same hardcoded 384 in
shmr.pyandpolyphonic_recall.py.shmr.pyused the identical hardcoded constant.polyphonic_recall.pyhardcoded384for bit-type vector normalization, silently breaking for non-384-dim models. Both now derive fromembeddings.EMBEDDING_DIM. (Contributed by Whishp.) - Last hardcoded 384 in
test_integration.py.np.random.randn(384)on line 238 missed in the earlier pass. Now uses EMBEDDING_DIM like the rest. (Contributed by Whishp.) - Plugin directory named
mnemosyneshadows pip package. Hermes adds~/.hermes/plugins/tosys.path, so a symlink namedmnemosyneresolves before the actualmnemosyne-memorypip package, causingModuleNotFoundErroronfrom mnemosyne.core.memory import Mnemosyne. The try/except swallowed this silently — tools never registered. Renamed tohermes-mnemosyne. (Fixes #212.) - Cross-session deletion of scope=global memories blocked.
forget_working()usedWHERE id = ? AND session_id = ?, preventing deletion of global memories returned by recall() from a different session. Now uses the same pattern asinvalidate():WHERE id = ? AND (session_id = ? OR scope = 'global'). (Fixes #204.) _vec_insert()ran inside deferred transaction. sqlite-vec virtual table writes were silently lost when the transaction never committed. Now commits after each_vec_insertcall. (Contributed by chinesewebman.)shutil.rmtree()crashes on symlink targets. Users who installed viadeploy_hermes_provider.shhave a symlink at~/.hermes/plugins/mnemosyne/.shutil.rmtree()raisesCannot call rmtree on a symbolic link. Fixed withis_symlink()detection andunlink()fallback.- Directory junctions used on Windows. Instead of symlinks (which require admin), the installer now creates directory junctions. No admin required.
- Dead
hermes_plugintests breaking CI collection. 4 test files still imported from the removedhermes_plugin/directory, causingModuleNotFoundErrorand killing the entire test suite. Deleted:test_hermes_plugin_session.py,test_hermes_plugin_tools.py,test_c13_memory_context_single_injection.py,test_c27_provider_init_error_visible.py. Pruned 2 MCP-routing classes fromtest_e6a_followup_gaps.py.
- refactor: modular Hermes provider. Split the 2007-line
__init__.pymonolith into 5 clean modules:tools.py(460L — 23 tool schemas),__init__.py(1515L — MemoryProvider),audit.py(138L),cli.py(332L),hermes_llm_adapter.py(164L). Moved tointegrations/hermes/src/mnemosyne_hermes/following the MemoriLabs pattern. Ships as standalonemnemosyne-hermespip package. Removed legacyhermes_plugin/directory, rootplugin.yaml, anddeploy_hermes_provider.shhack. - refactor: consolidate
extensions/andhermes/intointegrations/. Single directory for all external adapters:integrations/hermes/,integrations/obsidian-mnemosyne/,integrations/vscode-mnemosyne/. Python-package integrations stay inmnemosyne/integrations/. - Drop Python 3.9 CI support. EOL since Nov 2025.
requires-pythonbumped to>=3.10inpyproject.tomlandsetup.py. MCP and OpenClaw extras already gated on>=3.10, so this formalizes existing behavior. MNEMOSYNE_EMBEDDING_API_URLenv var no longer falls back toOPENROUTER_BASE_URL. Embedding providers are independent of the general routing endpoint.
- LongMemEval 98.9% recall benchmark restored to README alongside BEAM
numbers. Comparison table now shows both:
65.2% BEAM / 98.9% LongMem. - Hermes Plugin section revamped: 23 tools in 5 categories, pip install
mnemosyne-hermesflow,hermes tools disable memorystep, updated TOC. - Standalone README for
mnemosyne-hermes: Memori-inspired, no em-dashes, professional formatting, header image. - Hermes-first positioning in root README.
- Advise disabling built-in Hermes memory when using Mnemosyne (prevents double-injection and token waste).
- Multilingual embedding setup documented in README with
MNEMOSYNE_EMBEDDING_MODELenv var and Language Support section. - New env vars documented in
integrations/hermes/README.mdconfig table:SYNC_TURN_USER_LIMIT,SYNC_TURN_ASSISTANT_LIMIT,FACT_RECALL_ENABLED,PREFETCH_CONTENT_CHARS. - Install script link fixed in
hermes-mcp.md. (Contributed by Joao Fernandes, PR #201.) - UPDATING.md updated for v3.1.2 release notes.
- 26 tests for
sync_rolesconfig (bitr8) - 8 tests for sync_turn content limit env vars
- 4 tests for fact recall integration
- 5 tests for auto-scope-global
- Pre-existing fact concurrency, polyphonic, and prefetch tests preserved and passing
Contributors: Abdias J, Whishp, mia-fourier, bitr8, chinesewebman, Joao Fernandes
- Irrelevant context injection in recall. Three root-cause fixes for
#198:
- Strict fact matching is now the default. Set
MNEMOSYNE_LENIENT_FACT_MATCH=1to opt back into permissive matching (which matched any query word against any stored fact, dragging in unrelated memories with a false +20% score boost). - Entity prefix similarity (
similarity()inentities.py) now requires a minimum 30% length ratio. Short prefixes like "her" no longer match "Hermes" at 0.828. - Single-token strict fact queries (5+ chars, stopword-filtered) now match. Queries like "hermes", "python", "react" were silently rejected.
- Strict fact matching is now the default. Set
.codegraph/no longer accidentally tracked in git.
MNEMOSYNE_STRICT_FACT_MATCHenv var removed. UseMNEMOSYNE_LENIENT_FACT_MATCH=1to opt back into permissive fact matching.RELEASING.mdadded with official SemVer release policy..githooks/pre-pushhook validates tags match__version__and SemVer format.- Git hooks path set to
.githooks(rungit config core.hooksPath .githookson clones).
- Preferred embedding env vars.
MNEMOSYNE_EMBEDDING_API_URLandMNEMOSYNE_EMBEDDING_API_KEYare now the preferred names for custom embedding endpoints. The oldOPENROUTER_BASE_URLandOPENROUTER_API_KEYnames still work as fallbacks for backward compatibility. Restores the v2.8.x naming convention. (#193)
- Shared surface memory CRUD. Cross-agent shared memory database with dedicated read/write/search/delete/stats API. Each agent's shared surfaces are fully isolated from private memories. (
5a0b16a) - Multilingual MEMORIA. Language detection pipelines for German, Russian, and Chinese. MEMORIA now auto-detects the input language and applies language-specific extraction patterns. (
afd53c3,669a7cf,0f486cc) - Custom embedding endpoints. Configure any OpenAI-compatible embedding provider via
OPENROUTER_BASE_URL(set to your own server URL), with Jina model dimension auto-detection and custom SSL cert support. AddMNEMOSYNE_EMBEDDINGS_VIA_API=trueif using OpenRouter-hosted models. (d0a8421) - Deterministic
get(id)primitive. Direct memory retrieval by memory ID — no vector search, no ranking, just the exact memory. Useful for tool calls, confirmation UI, and graph traversal seed points. (022929b) hermes mnemosyne statscommand. Exposes memoria-specific statistics (fact count, instruction count, preference count, language distribution) via the CLI. (8b146dd)- Chinese and multilingual embedding models. Auto-dimension detection for models that don't expose fixed output sizes, enabling seamless use of multilingual embedding providers. (
f37f4bb) - Community health files.
CODE_OF_CONDUCT.md,SECURITY.md, and a GitHub PR template for smoother community contributions. (c2bf1d3) - Community badges. 100% Python badge added to README via shields.io. (
22e212f)
- sqlite-vec int8 search syntax. The
AND k=Nclause (required by sqlite-vec's int8 vector type for proper search) replaces the standardLIMITclause in vec_search. Without this fix,int8vector search silently returned wrong results. (0a41e3b) - Hermes plugin tool schemas. All 6 hermes_plugin tool schemas now include the
bankparameter, enabling multi-bank operation from the Hermes plugin layer. (8cd718d) - sqlite-vec extension loading.
_get_connectionnow correctly loads thesqlite-vecextension before any vector operations, preventingno such function: vec_distance_cosinecrashes. (a0de5f3) - Working memory vector generation.
remember()now generates and persists the vector embedding on every call, not just during recall-time lazy generation. (892f136) - Active DB path in diagnose.
mnemosyne diagnosenow reports the actual provider-level database path instead of the base config path. (00ca612) - Timezone normalization in temporal recall. Temporal queries now properly normalize timezone-aware timestamps, fixing off-by-hour windowing errors. (
f4b18f7) - MEMORIA regex cross-session dedup. Tightened regex patterns to prevent fact duplication across sessions and improved metric extraction. (
81cc6fc) - MULTILINGUAL_PATTERNS deduplication. Removed duplicate
instructionkeys and false positive German patterns across multiple iterations. (3f0e250,a16aa6e,cd3b1b2) - E1 ingest type safety. Fixed
tool count assertionand_lang string/int TypeErrorduring conversation ingestion. (ed85e51) - Fact accumulation metadata skip. Fixed metadata keys being incorrectly counted in fact accumulation during
ingest_conversation. (86d8c1e) - MEMORIA JSON parsing.
_parse_factsnow handles both structured JSON and raw text output from the MEMORIA extraction prompt. (d863220) - String boolean config handling. YAML config
true/falsestrings are now properly coerced to Python booleans in_apply_provider_config. (21a157d) - Vector type probing. Schema preservation during vector type probing prevents table corruption on re-probe. (
67fca7a) - Sys.path ordering. Fixed import resolution for
Hermes MemoryProviderby moving sys.path setup before mnemosyne imports. (62b0218) - Test stability. Patched lambda mocks and disabled embeddings in recall diagnostics tests to prevent CI flakiness. (
4ba74eb,066a3c6,e3bdc63) - Config import in eval tool. Moved logging import to module level in evaluation tool to prevent CI import errors.
- UPDATING.md rewritten. Complete restructuring covering v2.7→v3.1 path, PEP 668 troubleshooting, and schema verification steps. (
dc170ce) - README overhaul. Centered hero section, table of contents, imperative tone throughout. (
887c8c0) - BEAM benchmarks accuracy. Corrected Hindsight benchmark from false 64.1% to 73.4% and removed unsupported SOTA claims. (
341c82e)
- DEVOPS.md from git tracking. Private operational doc removed from version control. (
34483af) - Local scratch and benchmark artifacts. Cleaned up development artifacts from the repo. (
7826de9) - Personal emails from source files. PII filter-repo scrub with .mailmap and PII pre-commit hook added. (
58507ea)
- MEMORIA Architecture. Structured fact extraction and retrieval system.
New SQLite tables (
memoria_facts,memoria_timelines,memoria_kg,memoria_instructions,memoria_preferences) with fact versioning, previous-value tracking, and valid-from/to windows. - Structured retrieval router.
memoria_retrieve()dispatches queries by ability (IE, MR, KU, TR, CR, EO, ABS, IF, PF, SUM) to specialized retrieval paths with different SQL strategies per question type. - Gap analysis loop. Recursive re-querying for multi-hop and temporal questions. Extracts ISO dates from context, performs hard keyword searches for GAP-identified missing information.
- Strict fact matching (wysie, #143). Token-based conservative matching
behind
MNEMOSYNE_STRICT_FACT_MATCH=1. Filters stopwords, requires multi-token overlap or distinctive structural markers. - Proactive memory linking (coe0718, #146). Zero-LLM graph edge creation
at ingestion via content similarity (FTS5) and entity overlap strategies.
Gated behind
MNEMOSYNE_PROACTIVE_LINKING=1. - Benchmark LLM consolidation. The evaluation harness now routes
beam.sleep()summarization through OpenRouter with a cheap flash model instead of AAAK compression. The pipeline itself is unchanged — this is a benchmark config change only.
- Namespace migration. All
nous_tables/functions renamed tomemoria_to avoid implying affiliation with any external entity. - Fact versioning. Metrics with the same key now create version chains instead of overwriting. Previous values preserved for temporal recall.
- Retrieval engine upgrade. BEAM benchmark retrieval moved from FTS5-only to structured MEMORIA routing with 4-layer fallback.
- KU key collision. Context-aware metric keys prevent different metrics
(e.g.,
response_time_msvsconnection_timeout_ms) from colliding on generic key names. - CR UNION search. Contradiction resolution now searches both episodic memory and structured facts via UNION query.
- EO strict JSON mode. Event ordering prompts now force JSON-only output with negative examples to prevent rambling.
- IE latest-value guidance. Information extraction prompts now prioritize most recent values for evolving facts.
- TR token bump. Temporal reasoning max_tokens increased from 1024 to 2048 to accommodate date extraction preamble.
- BEAM 100K OVERALL: 65.2% (Llama 3.3 70B) — passes Honcho (63.0%)
- IE: 91.5%, MR: 87.5%, KU: 50%, TR: 75%, ABS: 100%
- Ingestion: 36s for 188 messages with full MEMORIA extraction
- MCP SDK 1.x compatibility (
mcp_server.py). Thestdio_server()transport no longer accepts aServerobject as argument since v0.9.1; the stream pair is obtained viaasync with stdio_server()and then passed toserver.run(). Tool definitions are now returned asToolPydantic objects instead of raw dicts, matching the SDK 1.xlist_toolshandler signature. Both stdio and SSE transports are patched.
- CompressionPlugin (
mnemosyne/core/plugins.py) — new built-in plugin providing optional pre-compression of memory content before LLM summarization. Disabled by default; enabled viaMnemosyneConfig.compression.enabled = Trueor the deprecatedMNEMOSYNE_USE_CAVEMAN=1env var. Supports therust_cave_001provider for stopword-based compression. Unknown providers fall back gracefully (no-op). Includescompress_lines(text, provider)method and_plugins.get_manager().get_plugin("compression")access point. - Deprecated env var —
MNEMOSYNE_USE_CAVEMAN=1still activates compression but emits aDeprecationWarningpointing to the config-based path (MnemosyneConfig.compression.enabled = True).MNEMOSYNE_USE_CAVEMAN=0explicitly disables it. - Test coverage — 7 new tests in
tests/test_plugins.pycovering: disabled by default, enabled via config,compress_linesnoop when disabled,compress_linesworks with caveman provider, deprecated env var fallback, registered as builtin plugin, unknown provider fallback. - Provider tool parity (15 → 17 tools). Added missing
export,import,diagnose,graph_query, andgraph_linktools to the Hermes memory provider. - Graph traversal & link memory. BFS multi-hop traversal with
edge_typeandmin_weightfiltering, integrated into polyphonic recall's_graph_voice. - Entity extraction quality fix. Case-insensitive meta-word stopword filtering blocks noise words (ASSISTANT, USER, SKILL) from mention annotations.
- Bad domain database (669K entries). Crowdsourced blocklists from BlocklistProject, Phishing Army, and URL shorteners. Sub-microsecond lookups for Discord link filtering.
- IP:port detection in link filter. Raw IP addresses like
182.3.4.5:8877are now caught alongside domain-based URLs. - Automated version bump script. Deterministic version bumper that updates all 8 version-carrying files and runs verification grep.
- Beam.py migration —
beam.pyno longer directly imports and callsrust_cave_001. Instead it checks_plugins.get_manager().get_plugin("compression")and delegates toCompressionPlugin.compress_lines(). Therust_cave_001dependency is now fully encapsulated behind the plugin interface. - MNEMOSYNE_USE_CAVEMAN — still activates compression but emits a
DeprecationWarningpointing to the config-based path. UseMnemosyneConfig.compression.enabled = Trueinstead. - Test assertion counts — 3 existing assertion counts in
test_plugins.pybumped from 3→4 to account for the 4th built-in plugin.
- CI embedding timeout.
fastembedmodel downloads blocked subprocess tests. AddedMNEMOSYNE_NO_EMBEDDINGSenv guard and lazy-loading inavailable(). - Provider export/import routing. Fixed handlers to route through the
Mnemosynewrapper instead ofBeamMemorydirectly. - Stale version references. Six files across the repo still displayed v2.7 after the initial v2.8.0 build (plugin yamls, docs pages, README badge, codebase surface). All corrected.
- LLM_MAX_TOKENS default too low for reasoning models (#81). Default raised from 256 → 2048 tokens. Reasoning models (DeepSeek V4, Claude thinking, Kimi K2) need ~2K tokens to complete chain-of-thought and produce usable consolidation output. Previously
finish_reason=lengthon reasoning models. Configurable viaMNEMOSYNE_LLM_MAX_TOKENSenv var.
-
Disaster recovery CLI commands (#69, D2+D3). New
mnemosyne backup,mnemosyne restore,mnemosyne verify, andmnemosyne backupscommands. Backup and restore now use the sqlite3 online backup API (lock-aware, WAL-safe, atomic) instead of rawshutil.copyfileobj. Exposes the existing DR module (mnemosyne/dr/recovery.py) to users via first-class CLI. -
Content sanitization on ingest (#69, D1).
BeamMemory.remember(),remember_batch(), andMnemosyne.remember()now detect binary-shaped content and extract it to content-addressed blob storage (~/.hermes/mnemosyne/blobs/). Three-stage detection: (1)data:URI prefix decodes base64 payload, (2) >1MB content always extracted, (3) >100KB content with Shannon entropy >5.0 bits/char extracted. Prevents SQLite corruption and DB bloat from inline images, base64 payloads, and encoded blobs.
E6.a — follow-up gaps surfaced by the E6 review
Mnemosyne.forget()andBeamMemory.forget_working()now cascade-delete annotations for the forgotten memory_id. Pre-fix,mentions/fact/occurred_on/has_sourcerows stayed in the annotations table after forget — they leaked throughexport_to_file, kept surfacing in_find_memories_by_entityand_find_memories_by_fact, and remained queryable through MCP tools. Privacy regression introduced by E6 (annotations table didn't exist pre-E6, so the cascade gap is new).mnemosyne_triple_addMCP tool now routes annotation-flavored predicates (mentions,fact,occurred_on,has_source) toAnnotationStore.add()instead ofTripleStore.add(). Pre-fix, an agent calling the tool withpredicate="mentions"would silently invalidate prior(subject, "mentions")annotation rows via the same auto-invalidation bug E6 was designed to fix — the bug remained reachable from the MCP layer. Current-truth predicates (anything outsideANNOTATION_KINDS) still route toTripleStorefor backward compatibility.
E6 — TripleStore silent-destruction bug
TripleStore.add()auto-invalidates rows with matching(subject, predicate)regardless ofobject. Every production write used annotation semantics ((memory_id, "mentions", entity),(memory_id, "fact", text), etc.), so each new annotation for a memory silently setvalid_untilon prior annotation rows with the same key. Effect: entity / fact graphs on each Mnemosyne database have lost data any time a memory had more than one entity or fact extracted.- Fix splits storage into two purpose-specific tables:
triplestable retains current-truth temporal semantics with auto-invalidation, suitable for facts like(user, prefers, X)later superseded by(user, prefers, Y). No production caller writes here today; the table is preserved for future use.- New
annotationstable (mnemosyne/core/annotations.py,AnnotationStore) is append-only and now hostsmentions,fact,occurred_on,has_source— all multi-valued by design.
- Production call sites migrated to
AnnotationStore:BeamMemory._extract_and_store_entities,_extract_and_store_facts,_add_temporal_tripleBeamMemory._find_memories_by_entity,_find_memories_by_factMnemosyne.remember(extract_entities=True)andMnemosyne.remember(extract=True)
- Auto-migration on first BeamMemory init. Existing databases auto-migrate annotation-flavored rows from
triplestoannotationswith a backup written to{db}.pre_e6_backup. SetMNEMOSYNE_AUTO_MIGRATE=0to disable auto-migration and runpython scripts/migrate_triplestore_split.pymanually instead. TripleStore.add_facts()is deprecated. EmitsDeprecationWarning; legacy write behavior preserved for backward compatibility. New code should callAnnotationStore.add_many(memory_id, "fact", facts)directly.
mnemosyne/core/annotations.py—AnnotationStoreclass +ANNOTATION_KINDSconstant (mentions,fact,occurred_on,has_source)scripts/migrate_triplestore_split.py— idempotent, transactional, file-level-backup migration script with--dry-run,--no-backup,--db PATHflagsMNEMOSYNE_AUTO_MIGRATEenv var (default1; set to0for explicit operator control)scripts/mnemosyne-stats.py— newannotationssection in JSON output alongside the existingtriplessection- 30+ new tests covering the new store, the migration script, the auto-migrate hook, and end-to-end production-path regression guards
NAI-0 Algorithmic Sprint
BeamMemory.format_context(results, format="bullet"|"json")— structured context formattingBeamMemory._sandwich_order()— U-shaped attention ordering (high-first, medium-middle, high-last)BeamMemory._fact_line()— clean one-line fact format with date, source, confidenceBeamMemory._format_context_json()/_format_context_bullet()— JSON and markdown output- RRF (Reciprocal Rank Fusion) in
PolyphonicRecallEngine._combine_voices()with k=60 constant - Covering indexes:
idx_em_scope_imp,idx_wm_session_recall,idx_mem_emb_type tools/bench_nai0.py— minimal 20-question benchmark for quick before/after measurement
Self-Healing Quality Pipeline (scripts/heal_quality.py, PR #67 by ether-btc)
- Detects degraded episodic memory entries (bullet-format, <300 chars) and repairs them via a 4-stage LLM-as-Judge closed loop: Extract → Generate → Judge → Repair
- Fault taxonomy:
truncated,generic,missing_facts,wrong_format - Judge scores 4 dimensions (factual density, format compliance, length sufficiency, grounding) each 0-100
- Repair strategies are fault-specific: context doubling, specificity enforcement, fact injection, format rewrite
- Loop with
MAX_RETRIES(default 3) and automatic escalation to stronger model after 2 failures - Quality provenance in
metadata_json:quality_score,judge_model,consolidated_at,fault_before_repair,retry_loop_count - Configurable via env:
MNEMOSYNE_HEAL_JUDGE_THRESHOLD,MNEMOSYNE_HEAL_MAX_RETRIES,MNEMOSYNE_HEAL_MIN_LEN,MNEMOSYNE_HEAL_BUDGET,MNEMOSYNE_HEAL_ESCALATE_AFTER - Works with any LLM backend (MiniMax M2.7 via mmx-cli, local GGUF, or remote OpenAI-compatible API)
- CLI:
python scripts/heal_quality.py [--detect-only] [--entry-id ID] [--dry-run]
Chunked LLM Summarization (mnemosyne/core/local_llm.py)
- Splits large memory lists into context-window-sized chunks before summarization
- Two-pass: summarize each chunk individually, then consolidate chunk summaries
- Fixes truncation issues with smaller models (Qwen2.5-1.5B) on large sessions
BeamMemory.recall()defaulttop_k: 5 → 40- Polyphonic recall voice combination: weighted average → position-based RRF
mnemosyne/__init__.py: version bump to 2.5.0
Hindsight Importer — migrate FROM Hindsight INTO Mnemosyne
- New
HindsightImporterclass inmnemosyne/core/importers/hindsight.py - Import from Hindsight JSON exports OR live Hindsight HTTP API (
/v1/default/banks/{bank}/memories/list) - Writes directly to
episodic_memory(not working memory) — preserves original timestamps, fact types, session grouping, metadata, scope, and veracity - Stable duplicate skipping via SHA256-based IDs (
hs_prefix) - Importance scoring derived from Hindsight
fact_type(world=0.75, experience=0.65, observation=0.55) + proof_count bonus - Full metadata preservation: hindsight_id, fact_type, context, dates, entities, chunk_id, tags, consolidation timestamps
- CLI:
mnemosyne import-hindsight <file.json|url> [bank] - Registered in provider registry alongside Mem0, Letta, Zep, Cognee, Honcho, SuperMemory
- 102 lines of regression tests: timestamp preservation, episodic-only import, stable duplicate skipping, FTS indexing, provider-registry usage
Host LLM Adapter — route consolidation through Hermes' authenticated provider
- New
mnemosyne/core/llm_backends.py— tinyLLMBackendProtocol (one method:complete()), process-global registry,CallableLLMBackenddataclass for tests - New
hermes_memory_provider/hermes_llm_adapter.py—HermesAuxLLMBackendroutes throughagent.auxiliary_client.call_llm(task="compression", ...) MnemosyneMemoryProvider.initialize()registers the backend;shutdown()unregisters it with a brief drain for in-flight threadssummarize_memories()andextract_facts()consult host first whenMNEMOSYNE_HOST_LLM_ENABLED=true- Host-skips-remote rule (A3): When host attempt produces no usable text, remote URL is skipped — falls straight to local GGUF. Prevents stale URL leaks.
llm_available()returnsTruewhen host backend is registered, so Hermes-only users don't get short-circuited bybeam.sleep()on_session_end()runs sleep in daemon thread with 15s join timeout;shutdown()drains 2s before unregistering- Fact extraction uses
temperature=0.0for determinism; consolidation stays at0.3 - 7 new tests covering registry round-trip, host-route precedence, A3 skip-remote rule, gate semantics, shutdown drain race, daemon exception logging, bullet-list output preservation
- Live end-to-end verified with
openai-codexOAuth subscription through ChatGPT backend
Hindsight importer: Before this, migrating FROM Hindsight required going through remember(), which assigned current timestamps and wrote to working memory. Historical memories lost their original context. Now Hindsight migrations preserve the full temporal record with zero data loss.
Host LLM adapter: Hermes users on OAuth-backed providers (ChatGPT/Codex subscriptions) could not use Mnemosyne's LLM-backed operations because MNEMOSYNE_LLM_BASE_URL expects an OpenAI-compatible API key endpoint, not OAuth. Now they can route through Hermes' already-authenticated auxiliary client with zero extra credentials.
- Auto-sleep consolidation blocks TUI agent:
_maybe_auto_sleep()now runs in a background thread with a 5-second timeout instead of synchronously. Local LLM summarization (ctransformers) can no longer hang the agent worker thread. (#23) MNEMOSYNE_AUTO_SLEEP_ENABLEDenv var now controls auto-sleep behavior. Default isfalse(disabled) for interactive safety. Set totrueto re-enable.- Config schema updated to reflect new default.
Tiered Episodic Degradation — long-term recall without unbounded growth
- Three degradation tiers: Tier 1 (0-30d, full detail), Tier 2 (30-180d, LLM-compressed), Tier 3 (180d+, entity-extracted signal)
- Automatic tier promotion during
sleep()— no manual maintenance - Tier multipliers in recall scoring: cold memories need 4x stronger semantic match
- Configurable via
MNEMOSYNE_TIER2_DAYS,MNEMOSYNE_TIER3_DAYS,MNEMOSYNE_TIER*_WEIGHT - Mnemonics can now truthfully claim "remembers what you told it a year ago"
Smart Compression — entity-aware tier 2→3 extraction
_extract_key_signal()scores sentences by entity density (proper nouns, acronyms, security terms, tech stack, urgency)- Preserves facts buried anywhere in a long memory, not just the first sentence
- Configurable:
MNEMOSYNE_SMART_COMPRESS=1(default on),MNEMOSYNE_TIER3_MAX_CHARS=300
Memory Confidence — veracity signal for every memory
- New
veracityfield:stated,inferred,tool,imported,unknown remember(veracity="stated")— set confidence at write timerecall(veracity="stated")— filter by confidence level- Recall applies veracity multiplier to scores (stated=1.0x, inferred=0.7x, tool=0.5x)
get_contaminated()— surface non-stated memories for review- Configurable weights via
MNEMOSYNE_*_WEIGHTenv vars
local_llm.summarize()→summarize_memories()— would crash on LLM degradation path- SQLite connection conflicts in batch degradation tests
- Removed hallucinated Phase 2 from roadmap
Cross-Provider Importers — migrate from any memory platform
- New
mnemosyne/core/importers/module with 6 provider importers - Mem0: SDK pagination → REST → structured export fallback chain; preserves user/agent/app scoping
- Letta (MemGPT): AgentFile
.afformat parsing (JSON/YAML/TOML); memory blocks → working_memory, messages → episodic - Zep: users → sessions →
memory.get()per-session iteration; messages + summaries + facts extraction - Cognee:
get_graph_data()nodes/edges extraction; nodes → episodic memories, edges → triples - Honcho: peers → sessions →
context()+ messages; peer identity preserved as author_id - SuperMemory:
documents.list()+search.execute(); container tags mapped to channel_id - Agentic importer: generates ready-to-run Python migration scripts and AI agent instructions for all 6 providers
CLI: hermes mnemosyne import extended
--from <provider>— import directly from Mem0, Letta, Zep, etc.--list-providers— show all supported providers with docs links--generate-script— generate a migration script for any provider--agentic— output instructions to give your AI agent for extraction--dry-run— validate and transform without writing
Plugin tool updated
mnemosyne_importschema extended withprovider,api_key,user_id,agent_id,dry_run,channel_idparams
- README: added "Migrate from other memory providers" section with examples
Multi-Agent Identity Layer
- New columns
author_id,author_type,channel_idonworking_memoryandepisodic_memorywith indexes Mnemosyne(author_id=..., author_type=..., channel_id=...)constructor paramsremember()auto-populates identity columns from session contextrecall(author_id=..., author_type=..., channel_id=...)filter paramsget_stats(author_id=..., author_type=..., channel_id=...)filter params- Cross-session channel recall: when
channel_idis provided, scope expands to include all memories in that channel regardless of session - MCP server: per-connection instances replace module-level cache; identity via tool args or env vars (
MNEMOSYNE_AUTHOR_ID,MNEMOSYNE_AUTHOR_TYPE,MNEMOSYNE_CHANNEL_ID) - Hermes plugin
_get_memory()reads identity from environment variables
- MCP
_get_instance()renamed to_create_instance()— creates fresh instances per connection - Episodic memory SELECTs and recall-tracking UPDATEs use dynamic session/channel scope
Phase 1: Entity Sketching
- Regex-based entity extraction (
@mentions,#hashtags, quoted phrases, capitalized sequences) - Pure-Python Levenshtein distance with O(min) space optimization
- Fuzzy entity matching with prefix/substring bonuses and configurable threshold
extract_entities=Trueparameter onremember()— backward compatible, default False
Phase 2: Structured Fact Extraction
- LLM-driven fact extraction via
extract_facts()andextract_facts_safe() - Graceful fallback chain: remote OpenAI-compatible API → local ctransformers GGUF → skip
- Fact parsing with numbering/bullet cleanup, length filter, cap at 5 facts
Phase 3: Temporal Recall
- Exponential decay temporal scoring:
exp(-hours_delta / halflife) temporal_weight,query_time,temporal_halflifeparameters onrecall()- Environment variable
MNEMOSYNE_TEMPORAL_HALFLIFE_HOURSfor global default - Temporal boost applied across all recall tiers (working, episodic, entity, fact)
Phase 4: Configurable Hybrid Scoring
- User-tunable scoring weights:
vec_weight,fts_weight,importance_weight _normalize_weights()with env var fallback and sensible defaults (50/30/20)- Per-query weight overrides without global state mutation
Phase 5: Memory Banks
BankManagerclass for named namespace isolation- Per-bank SQLite files under
banks/<name>/mnemosyne.db - Bank operations: create, delete, list, rename, exists check, stats
Mnemosyne(bank="work")constructor parameter- Bank name validation (alphanumeric + hyphens/underscores, max 64 chars)
Phase 6: MCP Server
- Model Context Protocol server with 6 tools
- stdio transport (Claude Desktop, etc.) and SSE transport (web clients)
- Per-bank instance caching
- CLI entry:
mnemosyne mcp
Phase 7: Hermes Agent Integration
- 15 Hermes tools: remember, recall, stats, triple_add, triple_query, sleep, scratchpad_write/read/clear, invalidate, export, update, forget, import, diagnose
- 3 lifecycle hooks:
pre_llm_call(context injection),on_session_start,post_tool_call - AAAK compression for context injection
- Session-aware memory instances
Phase 8: v2 Differentiation
MemoryStream— push (callbacks) and pull (iterator) event stream, thread-safeDeltaSync— checkpoint-based incremental synchronization between instancesMemoryCompressor— dictionary-based, RLE, and semantic compressionPatternDetector— temporal (hour/weekday), content (keyword, co-occurrence), sequence patternsMnemosynePluginABC with 4 lifecycle hooksPluginManagerwith auto-discovery from~/.hermes/mnemosyne/plugins/- 3 built-in plugins:
LoggingPlugin,MetricsPlugin,FilterPlugin
- CLI rewritten — all commands now use v2
Mnemosyne/BeamMemoryinstead of stale v1MnemosyneCore - SQLite WAL mode — both
memory.pyandbeam.pynow use WAL journal mode with 5s busy timeout for better concurrency - FastEmbed cache — model cache persists at
~/.hermes/cache/fastembedinstead of ephemeral/tmp - Legacy dual-write — uses
INSERT OR REPLACEfor dedup safety
cli.pyDATA_DIR hardcoded to stale v1 path — now usesMNEMOSYNE_DATA_DIRenv var- Duplicate
_recency_decay()definitions inbeam.pymerged into single function - SQLite concurrency test failures — WAL mode + proper tearDown cleanup
plugin.yamldeclared only 9 of 15 tools — now declares all 15
- 292 tests passing (up from unknown baseline)
- New test files:
test_entities.py,test_entity_integration.py,test_banks.py,test_mcp_tools.py,test_streaming.py,test_temporal_recall.py - All test tearDown methods handle WAL
-wal/-shmfiles
- Temporal queries — query the knowledge graph with time awareness (
temporal_halflife,temporal_weight) - Memory bank isolation — separate namespaces for different projects or contexts
- Configurable hybrid scoring — tune vector vs. FTS vs. importance weights per query
- PII-safe diagnostic tool (
mnemosyne_diagnose) — inspect your memory without exposing sensitive data
sqlite-vecLIMIT parameter handling- Triples module-level helpers
- Embeddings fallback when
sqlite-vecis absent - Memory embeddings table auto-creation for sqlite-vec fallback
- Feature comparison matrix vs. cloud providers (Honcho, Zep, Mem0, Hindsight)
- DevOps policy — comprehensive procedures for releases, security, and operations
- Documentation cleanup — replaced placeholder files with proper repo docs
- Token-aware batch sizing in consolidation — no more OOM on large memory sets
- Remote API support for LLM summarization in
sleep()
- Consolidation edge cases with mixed local/remote LLM configs
mnemosyne_updatetool — modify existing memories without full replacementmnemosyne_forgettool — targeted memory deletion- Global stats flag —
hermes mnemosyne stats --globalfor workspace-wide metrics
- Working memory scope handling across sessions (PR #11)
- Default scope set to 'global' for migrated memories
- Working memory stats and recall tracking consistency
- PyPI release —
pip install mnemosyne-memoryworks out of the box - CI/CD pipeline — GitHub Actions for testing and release automation
pyproject.toml— modern Python packaging- UPDATING.md — migration guide for existing users
- Plugin
register()export for Hermes plugin loader discovery - Cross-session recall inconsistency (Issue #7, Bug 2)
- Subagent context write blocking (PR #8)
- Plugin auto-discovery —
register()method for Hermes plugin CLI - Bug report template — official GitHub issue template
- 6 bugs from Issue #6 — edge cases in recall, scope handling, and tool registration
- PEP 668 PSA — documentation for Ubuntu 24.04 / Debian 12 users hitting
externally-managed-environment
- Provider
register_cliusing nested parser instead of subparser sys.pathinjection with gracefulImportErrorfallback
- Feature request template — GitHub issue template for enhancements
- Simple versioning adopted — MAJOR.MINOR instead of semver
fastembeddependency correction (was incorrectly listingsentence-transformers)- Benchmarks restored to README with LongMemEval scores
- Export/import — cross-machine memory migration (
mnemosyne_export/mnemosyne_import) - One-command installer —
curl | bashsetup for new users - MemoryProvider mode — deploy Mnemosyne as a standalone memory provider via plugin system
- Anchored table of contents in README
- README fully rewritten — professional, community-focused, removed bloat
- FluxSpeak branding removed from LICENSE and metadata (Mnemosyne is its own thing)
- Temporal validity — memories can have expiration dates
- Global scope — memories visible across all sessions
- Local LLM-based sleep() — summarization without cloud APIs
- Recall tracking — knows what you already remembered
- Recency decay — older memories naturally fade in relevance
- Path type bug in memory override skill
plugin.yamlmoved to repo root for Hermes compatibility
- Memory override skill — bake memory into pre_llm_call and session_start hooks
- Critical deprecation notice for legacy memory tool
- Scale limits — tested and documented for 1M+ token capacity
- Legacy DB migration script — upgrade path from early schemas
- Auto-logging of
tool_executiondisabled by default (privacy)
- BEAM architecture — sqlite-vec + FTS5 + sleep consolidation
- BEAM benchmarks — dedicated benchmark suite with published results
- Dense retrieval via fastembed
- AAAK compression — compressed memory format for context injection
- Temporal triples — structured fact storage with subject/predicate/object
- Thread-local connection bug
- Initial release — zero-dependency AI memory system
remember()/recall()/sleep()— core memory cycle- SQLite + fastembed embeddings — local vector search
- Hermes plugin registration — basic tool integration
- AAAK compression — early context compression for token limits