curl | bash kit, or a second memory brain beside :3069.
No CEO decision. Sleep-consolidation mutate stays dry-run on purpose (the unit already says so). Do not run their installer.
hallway/tunnel symbols in tools/agent-memory*.js (grep 2026-08-16). Independent JS on Postgres, never their AGPL Go. Owner: existing node task:harness.eval-agentsmemory-mcp (do not fork a sibling).
Sleep pass is no longer missing: tools/agent-memory-consolidate.js exists, timer last ran 2026-08-16 04:43 EEST, dry-run mutated=false gists=25 crossAgent=10 contradictions=270 downscale=5 promote=5. Keep apply off.
Why, in one honest paragraph. As a central notebook that many AI agents read and write at the same time, MPRO's engine sits on Postgres - a database built for many writers at once (called MVCC: everyone can write together without stepping on each other) - plus a fast approximate vector search (HNSW) and a live change-broadcast bus (pg_notify). agentsmemory's durable store is a single SQLite file with no "wait-your-turn" setting configured, and its own code comments admit its locks do not coordinate across copies - so the exact thing it is designed for (many agents, many server copies) meets a single-file write bottleneck. So the foundation is ours to keep. Ours was "broken" only by a few hours of configuration drift (see the Live hard-proof tab); theirs is an unproven early skeleton whose concurrency limit is baked into the architecture. [multiagent lens; head-to-head lens]
23cdd44ba. ours nowtools/agent-memory-quality.js + wired at agent-memory-mcp-server.js (~2265). ours nowcontent_sha on write + mine idempotency. ours nowkgQuery(..., asOf) in agent-memory-capabilities.js. Populate-from-mine is still thin (task:memory.populate-bitemporal-kg). code ours / data sparsecmd/server/main.go, store/hybrid.go, store/sqlitevec/sqlitevec.go (theirs) and tools/agent-memory-mcp-server.js, core/db.js (ours).To be fair to their design: a swap to their self-hosted server would win ONLY if all five of these became true - and none is true today:
Until those flip, (A) is the grounded call. The one idea worth banking from a swap for the far future is their bearer-token + stateless-HTTP tenant boundary - adopt it only IF/WHEN MPRO ever exposes memory to untrusted external tenants across machines. That is a business decision for later, not a technical one for now. [multiagent lens - recommendation section]
Source of truth: live-proof.json (a real read-only probe of the live DB this session).
768-d vectors (Ollama nomic-embed-text), but the live agent_memory.embedding column was vector(384) - so a hybrid search errored with FATAL: different vector dimensions 384 and 768.has_tsvector: false - the value_tsv full-text column was not present, so the keyword half of "hybrid" search did not exist.5 of 14 rows had a vector at all - 9 of 14 had none. So recall silently degraded to recency-only ("newest N"), NOT "most relevant".23cdd44baagent_memory.embedding column to vector(768) to match the live model.value_tsv tsvector migration (the keyword arm) + its GIN index.rank.go lesson - see the Full comparison tab).Source of truth: live-proof-after.json.
A real query ("OLLAMA BRIDGE found REVIEW review TASK") now recalls the right memory with BOTH arms contributing:
recalled_key : ollama-bridge-returns-canned-greeting vector_sim : 0.675 (meaning match - the semantic arm) bm25 : 0.0317 (keyword match - the lexical arm) similarity : 1.000 (normalized fused score = top rank) BOTH_ARMS_FIRED: trueResult: genuine hybrid recall restored - the vector arm and the keyword arm both fire and blend, on infrastructure we already run. The other four candidates fall away cleanly (vector 0.45-0.46, bm25 0.0). [multiagent lens live-state; live-proof-after.json]
The comparison tabs weigh two designs. This tab proves the harder point: a beautiful design that is mis-configured is worse than a plain one that runs. Ours was the mis-configured one - now it is the running one. Verify it yourself:
python3 -c "import json;d=json.load(open('/home/darius/MPRO/docs/agentsmemory-eval/live-proof-after.json'));print('cols',d['embedding_coltype'],'| tsv',d['has_tsvector'],'| embedded',d['rows']['emb'],'/',d['rows']['n'],'| both arms',d['hybrid_proof']['BOTH_ARMS_FIRED'])"
# -> cols vector(768) | tsv True | embedded 14 / 14 | both arms True
Every row is grounded in a real function body read on both sides (satya / validate-body-not-title). The "winner" column is called plainly, with the reason. Legend: theirs agentsmemory stronger · ours MPRO stronger · tie / neither.
| Capability | agentsmemory | MPRO | Winner + why |
|---|---|---|---|
| Recall fusion (ranking maths) | rank.go rankHybrid: real Okapi-BM25, min-max normalized to [0,1], then 0.6*vec + 0.4*bm25 + closetBoost; stable-sort ties. | Was 0.7*vec + 0.3*ts_rank_cd with BM25 un-normalized (~0.01-0.1) -> keyword term dwarfed -> effectively vector-only. Now normalized (this session's fix). | theirs taught it; the single biggest recall-quality gap - now closed on our side. [head-to-head] |
| Candidate pool | service.go: over-fetch limit*3 (or 10000 when filtering), re-rank survivors. | Each CTE arm hard-capped at LIMIT 50; FULL OUTER JOIN admits a keyword-only hit within its own 50. | theirs (marginal) - sizes the pool to the request. [head-to-head] |
| Backend recall (scale) | Default = brute-force exact cosine over SQLite (O(N)); ANN only if Qdrant wired. | pgvector HNSW ANN by default (sub-linear), generated tsvector + GIN. | ours - ours scales by default; theirs full-scans until Qdrant stood up. [head-to-head] |
| Dedup / write identity | chunk.go DrawerID = SHA256(team,wing,room,source,chunkIndex,content) - identical content is idempotent; distinct content never collides. | content_sha on agent_memory + mine-file idempotency (tools/agent-memory-mine.js). Key upsert still exists; hash prevents silent same-body forks. | ours now - content-hash landed after the July eval. [remeasured 2026-08-16] |
| Supersede / temporal KG | kg.go bitemporal: no-auto-supersede, valid_to keeps history, as-of time-travel, inverted-interval guards, current-dedup. | kgQuery(..., asOf) in agent-memory-capabilities.js (lines 95-123). Mining still barely fills the table (task:memory.populate-bitemporal-kg open). | code ours / data sparse - as-of query exists; extraction is the remaining leak. [remeasured 2026-08-16] |
| Topic index / closet | closet.go: extract action-phrases + headers + quotes -> embedded pointer index; diminishing rank-boost [0.40,0.25,0.15,0.08,0.04]. | computeClosetBoosts / applyClosetBoosts in tools/agent-memory-quality.js, applied in search at agent-memory-mcp-server.js (~2265) via memory_topics. | ours now - July "no searchable topic-boost" is stale. [remeasured 2026-08-16] |
| Chunking + entity/date | Boundary-aware chunker (1600/320/50), entity extraction (COCA stoplist), content-date extraction (frontmatter -> body). | Stores the whole value as ONE un-split row; no entity/date extraction. | theirs outright (impact smaller for our short notes). [head-to-head] |
| Multi-agent shareability | ONE network server; cross-machine, cross-tenant; versioned team skills. | ONE central Postgres; shared/team namespaces + pinned Letta core-memory blocks broadcast to all. | tie - theirs central across machines; ours central across a trusted fleet on one box. [multiagent] |
| Tenant isolation | Physical: one Qdrant collection per team; bearer -> exactly one team at one choke point (ResolveToken). A missing filter cannot leak. | Logical only: shared table + namespace filter; agent_id is self-declared (no bearer, no RLS at the memory layer) - spoofable by any DB-connected process. | theirs for untrusted multi-tenancy; fine for one trusted org (ours). [multiagent] |
| Concurrency / consistency | Single SQLite SoT, no WAL / busy_timeout set anywhere; in-process locks that "do NOT coordinate across horizontally scaled instances" (their comment). | Postgres MVCC - real concurrent writers; pool + backoff + circuit-breaker; row-level upsert. | ours clearly - the biggest gap on THEIR side for many agents. [multiagent] |
| Transport / scale | Stateless Streamable-HTTP MCP - trivially horizontal behind a load balancer (on paper). | stdio MCP plus live AgentMemoryHTTP :3069 (mpro-AgentMemoryHTTP.service, health 200 on 2026-08-16). Bridges share one Postgres; pg_notify is the bus. | ours - HTTP share for kimi/grok/ollama already exists; do not add a second MCP. [remeasured 2026-08-16] |
| Realtime bus | None (pure store). | pg_notify streams (agent_events / memory_block_update) + session heartbeat, crash detection, cross-agent handoff. | ours - live propagation theirs does not have. [multiagent] |
| Lifecycle / forgetting | NONE - a drawer lives forever until deleted/re-mined. | temporal_class + search-time decay filter + startMemoryDecayCleanup daemon (ephemeral@3d / short@30d -> archive) + valid_to. | ours decisively - theirs never ages a memory out. [lifecycle, head-to-head] |
| Working tier / session mgmt | None - no pinned core memory, no session concept. | Letta memory_blocks (pinned, ~2000-token budget at resume) + memory_load_layers (L0-L3) + full session lifecycle. | ours - a real hot buffer; theirs is a flat store. [lifecycle] |
| Consolidation - associative | Closets + hallways (entity co-occurrence ≥2) + tunnels = a derived memory graph. | None (has addressing, no derived association graph). | theirs - genuinely brain-like association. [lifecycle] |
| Consolidation - summarize / gist | Still ABSENT on their side (drawers stay full). | agent-memory-consolidate.js + nightly timer. Last run 2026-08-16 04:43 EEST dry-run: gists=25 crossAgent=10 contradictions=270 downscale=5 promote=5 mutated=false. Apply stays off. | ours (dry-run) - July "neither has it" is stale. Keep mutate off. [remeasured 2026-08-16] |
| Usage telemetry in rank | None on drawers (L7 edge dynamics exist but are dormant scaffolding, never updated). | access_count / surface_count / last_surfaced_at updated on every read; feeds the decay score. | ours - a real recency-of-use signal. [lifecycle] |
| Code quality / DDD | A-: textbook bounded contexts, ubiquitous language IS the schema, correct dependency direction, ports-and-adapters. | A large, battle-tested JS server; less formal DDD but hardened in a live fleet for years. | theirs for structure; ours for production mileage. [code-review] |
| Tests / coverage | Tests B+ (golden-parity ranker suite); coverage C+/B- static estimate; MCP tool boundary = 0 tests. | TDD-gated across the harness; the memory server is exercised live (this session's probes are the proof). | split - their domain math is well-tested; their transport/boundary is the hole. [code-review] |
| Security | A-: tenant isolation by construction, const-time compares, PKCE, fail-closed webhooks. Gaps: cookie/rate-limit/supply-chain hardening. | Honor-system single-tenant by design (one org); code-based signing + RLS elsewhere in the harness. | theirs for multi-tenant security posture. [code-review] |
| Export / GDPR | dataexport.BuildTeamArchive - a scoped standalone SQLite with credential redaction + fail-closed table manifest. | No per-subject export tool in the memory server (has a decay archive + pg_dump). | theirs outright - a genuine per-tenant portable export. [head-to-head, multiagent] |
| License | AGPL-3.0 (network copyleft) - a real constraint if ever exposed externally + modified. | Internal to MPRO; no external-copyleft obligation. | ours for our use; theirs is a business-risk to weigh before any "expose it" idea. [multiagent, cta] |
| Deployment / ops | Self-contained Go binary + Dockerfile - clean as a standalone product. But adds Qdrant + Ollama bge-m3 + Go as new surface for us. | Runs inside the existing MPRO stack (Postgres + pgvector + Ollama already live) - zero new services. | ours for MPRO; theirs cleaner as a shipped product. [multiagent] |
Plain-language, one risk per card. These are the things that change the decision.
agent_id) is just a value it types - no password proves it. Fine because we are one trusted company, but it means our memory is single-tenant and could not safely hold other companies' data the way theirs can (each team gets its own locked vault via a bearer token).curl | bash installer registers a remote MCP at aiagentmemory.dev/mcp - so every memory read + write leaves the machine to their cloud, by default, not behind a flag. It also rewrites your .claude config (a Stop hook + a CLAUDE.md bootstrap) and can chain a second curl|bash from a third-party org. For MPRO this is disqualifying - our memory (company + client context) must never leave MPRO infra. "Adopt agentsmemory" could only ever mean self-host, never this.go build path avoids the egress entirely. [audit lens sec 0; cta lens; multiagent lens]| Mechanism | Plain meaning + the citation |
|---|---|
| Sleep replay + systems consolidation | In deep sleep the brain re-plays the day's firing patterns at high speed and slowly moves memories from the fast temporary store to the slow permanent one. Diekelmann & Born 2010, Nat Rev Neurosci nrn2762; Wilson & McNaughton 1994, Science (seminal replay); Rasch & Born 2013, Physiol Rev. |
| SHY - sleep downscales synapses | By night the brain is "louder" from a day of use; deep sleep turns the overall volume DOWN - weak, rarely-used connections shrink toward zero (forgotten), strong ones survive. Forget the noise, keep the signal. Tononi & Cirelli 2006, Sleep Med Rev PMID 16376591; 2014 review, Neuron. |
| CLS - a fast store + a slow store | The hippocampus learns FAST from one experience (episodic); the neocortex learns SLOWLY over many (semantic). Sleep is the bridge, avoiding "catastrophic forgetting". The direct blueprint for a two-tier agent memory. McClelland, McNaughton & O'Reilly 1995, Psych Review; Kumaran, Hassabis & McClelland 2016, Trends Cogn Sci (the AI-facing update). |
| Adaptive forgetting + the forgetting curve | Forgetting the right things on purpose makes you smarter - it stops stale details misleading today's decisions. Retention drops roughly exponentially (R = e^(-t/S)) unless reviewed; each review flattens the curve. Richards & Frankland 2017, Neuron S0896-6273(17)30365-3; Ebbinghaus 1885. |
| Salience / emotional tagging | An important or emotional memory gets "tagged", and the tag lets it capture the scarce resources to become permanent. Retention tracks what matters. Frey & Morris 1997, Nature 385:533; Redondo & Morris 2011, Nat Rev Neurosci nrn2963. |
The AI bridge is not new: reinforcement-learning "experience replay" is a literal copy of hippocampal replay (Mnih et al. 2015, Nature - DQN), and Generative Agents (Park et al. 2023, UIST) already score memories by recency × importance × relevance and periodically write higher-level reflections back - a software echo of tagging + gist.
No Qdrant, no new service, no data migration - Postgres + pgvector + Ollama, using columns we already have.
| # | Upgrade | The HOW (concrete) |
|---|---|---|
| 1 ⭐ | The "SLEEP" consolidation pass (build first - closes the gap NEITHER tool has) | An idle-time daemon modeled on the decay daemon, guarded to never fire while a session is active. Four phases: REPLAY (pull recent rows since last run) -> GIST-MERGE (cluster near-duplicates by the embedding cosine we already compute, then call the local Ollama model to write ONE consolidated "gist" row, temporal_class:'long') -> DOWNSCALE (archive the weak raw episodics - extend the existing startMemoryDecayCleanup) -> PROMOTE the salient rows + new gists into the working tier. Read-mostly, reversible (sources kept). [brainscience Upgrade 1; lifecycle #1] |
| 2 | Learned salience (cheapest, high value - MPRO already has the inputs) | End the static importance = 0.5. Recompute in the decay daemon: importance = clamp(0.3 + 0.12*ln(1+surface_count) + 0.08*ln(1+access_count) - staleness + kg_link_bonus, 0.05, 1.0). Because importance already feeds the search decay score, frequently-surfaced memories auto-resist decay = the rehearsal/spacing effect, for free. Pure SQL. [brainscience Upgrade 3; lifecycle #2] |
| 3 | Real forgetting curve + prune the unbounded classes | Add a stability column (starts ~1, +=1 per surface); retention R = exp(-days_since_surfaced / stability); archive when R < 0.1 for ALL classes incl. the default medium (today search-hidden but never physically pruned = a disk leak). Each retrieval flattens the curve. [lifecycle #3] |
| 4 | Adopt their association layer + a bounded working set | Port their closets/hallways (entities -> a co-occurrence + topic-boost table that lifts recall - MPRO has none, theirs proves its value). And make memory_blocks a true attention buffer: when the pinned-block budget is exceeded, auto-demote the least-recently-surfaced block. [lifecycle #4; brainscience Upgrade 2/4] |
Caveat (satya): the "sleep-inspired LLM memory" arXiv preprint (2603.14517) was seen in this turn's search listing only - a pointer, not a validated result. The five brain mechanisms above are each cited to a seminal paper AND a current review.
The three brain tiers are not something we need to build - they map onto three MPRO objects we already run. [grounded in SYSREF-004 (Knowledge Base) + SYSREF-001 (Mastermind)]
| Brain tier | The MPRO object we already run |
|---|---|
| Working / conscious | Letta memory_blocks (pinned) - the small hot set always kept in front of the agent. |
| Fast episodic / recent (hippocampus + recent neocortex) | agent_memory rows - the pgvector hybrid, just fixed to 768-d. |
| Crystallized semantic (the neocortex proper) | KB @ 3001 (knowledge.items, 4,371 items), imported by 177 fleet files - the shared neocortex the whole company reads. |
The KB is more neocortex-like than a plain search box: it is a full RAG stack - a reranker (Ollama cross-encoder) + a CRAG confidence-grader + a HyDE query-expander + a 5-guard hallucination check + graph-community associations. And the load-bearing detail: the KB and agent_memory embed through the SAME RAGGateway (nomic-embed-text), so promoting a memory from Tier-2 to Tier-3 needs no re-embed - rock-solid now the 384->768 dimension fix has landed. Mastermind is the brain's salience filter: only confirmed, generalizable knowledge crystallizes into the KB; raw daily episodics stay in agent_memory or decay away.
Put together, these three tiers form a real brain circuit - and the write-wire between them was cut since the Windows->Linux migration and is now reconnected:
ec79448ac)Design takeaway: don't build a new store. The three tiers already exist - the work is to wire them: promotion/demotion between tiers + the sleep-consolidation pass (the Fix-first tab), all on the stack we already own.
file:line we read.| Axis | Grade | One-line |
|---|---|---|
| Architecture / DDD | A- | Textbook bounded contexts; ubiquitous language IS the schema; clean domain->transport direction. |
| Code quality | A- | Exceptional why-comments, sentinel errors, small functions; a few real error-swallows. |
| TDD / tests | B+ | Critical maths genuinely tested incl. golden-parity; transport layer has 0 tests. |
| Coverage (est.) | C+/B- | ~55-70% overall; domain 75-90%, MCP tool surface & cmd/server ~0%; no coverage/-race gate in CI. |
| Security | A- | Tenant isolation by construction; const-time compares, PKCE, fail-closed webhooks. |
| Reliability | B | Sensible bounds + in-proc locks; one non-atomic data-loss window; no -race. |
Single biggest strength: tenant isolation is enforced by construction - the bearer token resolves to a TeamID at exactly one choke point (tenant.go:284), threaded through every query as Where("team_id = ?"); no MCP tool ever reads a team id from its arguments; even a hypothetical foreign vector hit is dropped by a defense-in-depth re-filter (service.go:428). For a multi-tenant SaaS that is the thing that matters most, and it is done right.
| # | Finding + location | The fix |
|---|---|---|
| R1 | Non-atomic mine = silent data-loss window. Mine purges the whole prior version of a source before storing the new drawers, and an embed call that can fail sits in between (mine.go:128-166). A crash mid-re-mine leaves the source erased with no rollback. | Embed before purge, and/or wrap purge + store + closet-build in one transaction that commits only after all writes succeed. Add the failing-embedder test. Highest-impact - it is silent loss of the product's core asset. |
| T1 | The MCP tool surface has 0 tests. internal/mcpserver/* (~927 LOC of handlers - the actual boundary agents call) + cmd/server have no _test.go. This is where a tenant-isolation regression would hide. | Add handler-level tests: (a) missing/foreign token rejected by admit, (b) args sanitized, (c) output shape. Add go test -race -coverprofile to CI to turn the honest "we don't know the number" into a tracked one. |
| R2 | Interval-blind KG dedup silently drops updates. KGAdd returns the existing id whenever any open fact matches, ignoring the requested valid_from/valid_to (kg.go:179-190,351-355). Re-adding with a corrected start or a bounded interval is discarded. | Include the interval in the dedup match, or return a distinct signal so the caller knows nothing was recorded. Add a test. |
| S1/S2 | Web session hardening. Auth/2FA/passkey cookies lack the Secure flag; no HSTS. No server-side rate-limit on login/TOTP; the 2FA counter lives in a user-held cookie (bypassable). (web.go:80-86,299-319) | Set Secure: true (dev-gated) + emit HSTS; add per-user + per-IP failed-attempt lockout for both factors. |
| S3/S4 | Installer has no integrity check. The release binary is downloaded with no checksum/signature, default version latest; --recommended pipes a third-party curl|bash from another org's main, unpinned. (install.sh:46-62, main.go:30) | Publish + verify a per-asset sha256 (ideally a signature); pin the version in the shown command; pin/vendor the third-party fetch with explicit consent naming the source. |
| polish | Doc/behavior mismatch: rank.go:158 comment says "min-max normalized" but the code is max-only (ordering unaffected). Swallowed setSessionUser error redirects a not-logged-in user to /dashboard (oauth.go:137). | Fix the comment; redirect to /login on the non-nil error, mirroring the branch above it. |
Fair and friendly: the product is well-built, but a cold developer does not adopt today - and every fix is free. Journey scores: FEEL 6.5/10, TRUST 3/10, ACT 4/10.
v0.0.45, and the README self-describes as an "early skeleton" while the landing reads like a mature SaaS. Fix: a "who's behind this + honest status" band + a /security page; lean on the real differentiators (physical isolation, portability, no lock-in), not on the empty star count.Reassuring finding from our read: across every script, there were no reverse-shells, no hidden phone-home beyond the by-design remote MCP, no prompt-injection payloads. The flagged behaviours are visible and understandable - not malware. [audit lens sec 0; cta lens; code-review lens]
23cdd44ba: re-aligned agent_memory.embedding to vector(768), applied the value_tsv tsvector migration + GIN index, re-embedded all rows (14/14), and adopted normalized hybrid fusion so the keyword arm actually competes with the meaning arm. Proven by live-proof-after.json (both arms fire: vec 0.675 + bm25 0.0317).ec79448ac. Since the Windows->Linux migration it read an inline transcript instead of transcript_path and wrote to a Windows-only memory dir, so auto-capture of the CEO's corrections was DEAD - every learned lesson silently dropped into an empty catch. Now it parses the real transcript_path (inline fallback), resolves the co-located project memory dir, mirrors CEO corrections to KB @ 3001 as @source:lessons, and emits an anti-silent-failure warning so it can never rot undetected again. Fail-open, 7/7 TDD + live smoke. This reconnects the brain write-circuit: Mastermind (salience) -> learn-hook (hippocampal write) -> KB @ 3001 (neocortex store).| Idea (from their code) | Status | Note |
|---|---|---|
| Normalized hybrid fusion | DONE | Shipped as part of the live fix (23cdd44ba). |
| Closet topic-boost index | DONE | Wired 2026-08-16 remeasure: computeClosetBoosts + memory_topics. |
| Content-hash idempotent dedup | DONE | content_sha + mine idempotency present. |
memory_mine pipeline | DONE | tools/agent-memory-mine.js + file miner exist. |
| Bitemporal KG as-of query | DONE (code) | kgQuery(asOf) exists. Populate-from-mine still open on task:memory.populate-bitemporal-kg. |
| Sleep-consolidation pass | DONE (dry-run) | Nightly timer; 2026-08-16 04:43 EEST mutated=false. Keep apply off. |
| Hallway / tunnel association graph | open | Only remaining idea. Independent JS. Do not copy AGPL Go. Tracked on task:harness.eval-agentsmemory-mcp. |
| Their installer / second MCP / GDPR SQLite export | skip | AGPL + fights managed hooks + we are one org. SkillAPI :3045 already covers versioned skills (health 200, 417 rich skills). |
ec79448ac) and the reconnected brain write-circuit (Mastermind salience -> learn-hook hippocampal write -> KB @ 3001 neocortex). Added Brain-science Part 4 (KB@3001 already IS our neocortex - 4,371 items, shared RAGGateway = no re-embed on promote) + Part 5 (the circuit). PNGs rendered via the repo pipeline (validate-mmd.js + @mermaid-js/mermaid-cli).23cdd44ba): dim 384->768, value_tsv added, 14/14 re-embedded, normalized fusion adopted, both hybrid arms firing. Nothing from agentsmemory installed or executed.cmd/server/main.go, internal/tenant/tenant.go, internal/palace/{service,rank,mine,kg,closet,chunk,entity,contentdate,repo}.go, internal/store/{hybrid,sqlitevec,qdrant}.go, internal/dataexport/dataexport.go, clients/claude-code/install.sh, README.tools/agent-memory-mcp-server.js, tools/agent-memory-capabilities.js, core/db.js, migration 006-memory-vectors.sql.```mermaid block AND a plain-text ASCII box diagram - alongside the rendered picture. Nothing here needs a CDN or a live script: the PNGs were rendered offline with the repo pipeline (tools/scripts/validate-mmd.js + @mermaid-js/mermaid-cli) and embedded as static images. Copy any block below and hand it straight to an agent. Every component is grounded in the real code read this session - none is invented.
cmd/server/main.go, internal/tenant/tenant.go, internal/palace/{service,mine,chunk,entity,closet,hallway,tunnel,kg,rank}.go, internal/store/{sqlitevec,qdrant,hybrid}.go, internal/embed/ollama.Mermaid source - copy the fenced block into any Mermaid viewer:
```mermaid
%%{init: {'theme': 'dark'}}%%
flowchart TB
A1["N remote agents (any machine)<br/>POST /mcp + Bearer token"]
A1 -->|bearer| MCP
subgraph SVC["Stateless Streamable-HTTP MCP (Go)"]
MCP["server.WithStateLess(true)<br/>OAuth 2.1 + direct bearer"]
RT["ResolveToken<br/>bearer maps to ONE team (tenant)"]
MCP --> RT
end
RT -->|team_id| MINE
subgraph MINING["Mining pipeline (per source, keyedMutex)"]
MINE["Mine: purge-then-write"]
CHUNK["ChunkText 1600/320/50 boundary-aware"]
ENT["entity + content-date extraction"]
MINE --> CHUNK --> ENT
end
ENT --> EMB["Ollama embed - bge-m3 (1024-d)"]
EMB --> SQLITE
EMB -. optional .-> QDR
subgraph VAULT["Per-team physical vault (cryptographic isolation)"]
SQLITE[("SQLite = SOURCE OF TRUTH<br/>single writer, no WAL")]
QDR[("Qdrant collection per team<br/>mempalace_(sha16)_drawers<br/>OPTIONAL ANN index")]
end
subgraph PALACE["Memory-palace schema"]
WING["Wings = project namespaces"]
ROOM["Rooms = aspects"]
DRAWER["Drawers = verbatim chunks<br/>DrawerID = SHA256(team,wing,room,content)"]
CLOSET["Closets = topic/quote index<br/>team::closets namespace"]
HALL["Hallways = within-wing links<br/>entity co-occurrence 2+"]
TUN["Tunnels = cross-wing links"]
KG["Knowledge-Graph<br/>bitemporal, as-of time-travel"]
WING --> ROOM --> DRAWER
DRAWER --> CLOSET
DRAWER --> HALL --> TUN
DRAWER --> KG
end
SQLITE --- PALACE
subgraph RANK["Hybrid ranker (rank.go rankHybrid)"]
VEC["vector sim (bge-m3 cosine)"]
BM["BM25 Okapi -> min-max [0,1]"]
CB["closet-boost 0.40..0.04"]
FUSE["fused = 0.6*vec + 0.4*bm25 + closetBoost"]
VEC --> FUSE
BM --> FUSE
CB --> FUSE
end
SQLITE --> RANK
QDR --> RANK
```
ASCII - no renderer needed (agent-friendly):
N remote agents (any machine) --POST /mcp + Bearer token-->
|
v bearer
+----------------------------------------------------------+
| Stateless Streamable-HTTP MCP (Go) |
| server.WithStateLess(true) + OAuth2.1 / direct bearer |
| ResolveToken: bearer --> ONE team (tenant) |
+----------------------------------------------------------+
| team_id
v
+----------------------------------------------------------+
| Mining pipeline (per source, keyedMutex) |
| Mine purge-then-write --> ChunkText 1600/320/50 |
| --> entity + content-date extraction |
+----------------------------------------------------------+
|
v
Ollama embed bge-m3 (1024-d)
| \ optional
v v
+===========================+ +==========================+
| SQLite = SOURCE OF TRUTH | | Qdrant per-team |
| single writer, no WAL | | collection (ANN index) |
+===========================+ +==========================+
\____ per-team PHYSICAL vault (cryptographic isolation) ____/
|
v MEMORY-PALACE SCHEMA
Wings (project ns) --> Rooms (aspects) --> Drawers (verbatim chunks,
DrawerID = SHA256(content))
Drawers --> Closets (topic/quote index, team::closets)
Drawers --> Hallways (within-wing, entity co-occur >= 2)
Hallways --> Tunnels (cross-wing links)
Drawers --> Knowledge-Graph (bitemporal, as-of time-travel)
|
v HYBRID RANKER (rank.go rankHybrid)
fused = 0.6*vector(bge-m3 cosine) + 0.4*BM25(min-max [0,1]) + closet-boost
memory_blocks -> agent_memory pgvector -> KB @ 3001); the normalized pgvector hybrid search; decay daemon + pg_notify bus + namespaces on Postgres MVCC. Grounded in tools/agent-memory-mcp-server.js, tools/agent-memory-capabilities.js, core/db.js, migration 006-memory-vectors.sql, .claude/hooks/learning-capture.js.Mermaid source:
```mermaid
%%{init: {'theme': 'dark'}}%%
flowchart TB
C1["N agents in one trusted fleet<br/>stdio MCP children + bridges + bots<br/>self-declared agent_id"]
C1 --> WRITE
C1 --> READ
subgraph BRAIN["Brain write-circuit (JUST RECONNECTED)"]
MM["Mastermind = salience / amygdala trigger"]
LH["learn-hook (Stop) = hippocampal write-path<br/>FIXED commit ec79448ac"]
MM --> LH
end
LH --> WRITE
WRITE["memory_write<br/>ON CONFLICT (agent, category, key)"]
WRITE --> EMB["RAGGateway to Ollama<br/>nomic-embed-text (768-d)"]
subgraph TIERS["3 cognitive tiers"]
T1["TIER 1 - working / conscious<br/>Letta memory_blocks (pinned) + L0-L3 layers"]
T2[("TIER 2 - deep / sub-conscious<br/>agent_memory Postgres pgvector<br/>embedding vector(768) + value_tsv")]
T3[("TIER 3 - crystallized neocortex<br/>Knowledge Base @ 3001")]
T1 --> T2 --> T3
end
EMB --> T2
LH -. mirror @source:lessons .-> T3
subgraph SEARCH["pgvector HYBRID search (JUST FIXED)"]
V["vector cosine 768-d - HNSW ANN"]
B["tsvector BM25 - GIN index"]
F["normalized 0.6*vec + 0.4*bm25 fusion"]
D["decay filter - drop score below 0.1"]
V --> F
B --> F
F --> D
end
READ["memory_search / session_resume"] --> SEARCH
T2 --> SEARCH
subgraph LIFE["Lifecycle + realtime"]
DECAY["decay daemon (forgetting)<br/>startMemoryDecayCleanup<br/>ephemeral@3d / short@30d to archive"]
BUS["pg_notify realtime bus<br/>agent_events / memory_block_update"]
NS["namespaces: private / shared / team"]
end
T2 --> DECAY
T2 --> BUS
T2 --> NS
PG[("Postgres MVCC - many writers")]
PG --- T2
```
ASCII:
N agents, one trusted fleet: stdio MCP children + bridges + bots
(self-declared agent_id)
| |
| BRAIN WRITE-CIRCUIT (just reconnected) | query
v v
Mastermind (salience / amygdala trigger) memory_search /
| session_resume
v |
learn-hook (Stop) = hippocampal write-path |
[FIXED commit ec79448ac] |
| |
+--------------------> memory_write <--------------+
| ON CONFLICT (agent, category, key)
v
RAGGateway --> Ollama nomic-embed-text (768-d)
|
+======================= 3 COGNITIVE TIERS =======================+
| TIER 1 working / conscious : Letta memory_blocks (pinned) |
| + memory_load_layers L0-L3 |
| | promote / demote |
| v |
| TIER 2 deep / sub-conscious: agent_memory (Postgres pgvector) |
| vector(768) + value_tsv |
| | Mastermind crystallizes confirmed knowledge |
| v (SAME RAGGateway embed --> no re-embed) |
| TIER 3 crystallized neocortex: Knowledge Base @ 3001 |
+================================================================+
| | |
v v v
pgvector HYBRID search decay daemon pg_notify bus
0.6*vec(HNSW 768d) (forgetting) agent_events /
+ 0.4*BM25(tsvector/GIN) ephemeral@3d / memory_block_update
--> normalized fuse short@30d --> archive
--> decay filter (<0.1) namespaces:
[JUST FIXED] Postgres MVCC private / shared / team
(many writers)
Mermaid source:
```mermaid
%%{init: {'theme': 'dark'}}%%
flowchart TB
subgraph AM["agentsmemory (Go) - multi-tenant network service"]
direction LR
aw1["WRITE: agent -> POST /mcp (bearer)"] --> aw2["mine -> chunk -> entity/date"] --> aw3["embed bge-m3 1024-d"] --> aw4["SQLite (SoT) + closet index"] --> aw5["Qdrant index (optional)"]
ar1["RECALL: query -> embed"] --> ar2["SQLite brute-force cosine<br/>(or Qdrant ANN)"] --> ar3["BM25 over candidate pool (x3)"] --> ar4["normalized fuse 0.6/0.4<br/>+ closet-boost"] --> ar5["ranked drawers"]
end
subgraph MP["MPRO (Node + Postgres) - single-org fleet memory"]
direction LR
mw1["WRITE: agent -> stdio MCP (agent_id)"] --> mw2["memory_write<br/>+ learn-hook auto-capture"] --> mw3["embed nomic 768-d"] --> mw4["agent_memory pgvector<br/>+ value_tsv"] --> mw5["pg_notify broadcast"]
mr1["RECALL: query -> embed"] --> mr2["HNSW ANN vector CTE"] --> mr3["GIN tsvector BM25 CTE"] --> mr4["normalized 0.6/0.4 fuse<br/>-> decay filter"] --> mr5["ranked memories"]
end
AM ~~~ MP
```
ASCII:
agentsmemory (Go) - multi-tenant network service
WRITE : agent --POST /mcp(bearer)--> mine->chunk->entity/date --> embed bge-m3(1024d)
--> SQLite(SoT) + closet index --> Qdrant index (optional)
RECALL: query --> embed --> SQLite brute-force cosine (or Qdrant ANN)
--> BM25 over candidate pool (x3) --> normalized fuse 0.6/0.4 + closet-boost
--> ranked drawers
MPRO (Node + Postgres) - single-org fleet memory
WRITE : agent --stdio MCP(agent_id)--> memory_write (+ learn-hook auto-capture)
--> embed nomic(768d) --> agent_memory pgvector + value_tsv --> pg_notify broadcast
RECALL: query --> embed --> HNSW ANN vector CTE + GIN tsvector BM25 CTE
--> normalized 0.6/0.4 fuse --> decay filter --> ranked memories
Mermaid source:
```mermaid
%%{init: {'theme': 'dark'}}%%
flowchart TB
W["1. WORKING / conscious (hippocampus)<br/>MPRO: memory_blocks + L0-L3 (PRESENT)<br/>agentsmemory: ABSENT"]
D["2. DEEP / long-term (neocortex)<br/>MPRO: pgvector HNSW ANN<br/>agentsmemory: SQLite brute-force (Qdrant optional)"]
CA["3a. CONSOLIDATION - associative<br/>agentsmemory: closets + hallways + tunnels (WINS)<br/>MPRO: ABSENT"]
CG["3b. CONSOLIDATION - gist / summarize<br/>NEITHER has it = the shared gap = the SLEEP pass"]
F["4. FORGETTING / decay / archival<br/>MPRO: decay daemon + search filter (WINS)<br/>agentsmemory: none"]
S["5. SALIENCE-weighted retention<br/>MPRO: partial (importance, surface_count)<br/>agentsmemory: none (dormant L7 scaffolding)"]
W --> D --> CA --> CG --> F --> S
CG:::gap
classDef gap fill:#7c3f12,stroke:#fbbf24,stroke-width:2px,color:#fde68a
```
ASCII:
BRAIN FUNCTION agentsmemory MPRO
---------------------------- -------------------- ----------------------------
1 working / conscious ABSENT memory_blocks + L0-L3 (MPRO wins)
2 deep / long-term SQLite brute-force pgvector HNSW ANN (~tie)
(Qdrant optional)
3a consolidation-associative closets + hallways ABSENT (theirs wins)
+ tunnels
3b consolidation-GIST/summary ABSENT DRY-RUN nightly timer (MPRO leads; apply off)
4 forgetting / decay none decay daemon + filter (MPRO wins)
5 salience-weighted retention none (dormant L7) partial (importance, (MPRO leads)
surface_count)
Remaining gap is 3a hallway/tunnel, not gist. Gist exists as dry-run; keep apply off.