levh 2.26.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. levh-2.26.8.dist-info/METADATA +677 -0
  2. levh-2.26.8.dist-info/RECORD +178 -0
  3. levh-2.26.8.dist-info/WHEEL +5 -0
  4. levh-2.26.8.dist-info/entry_points.txt +2 -0
  5. levh-2.26.8.dist-info/licenses/LICENSE +21 -0
  6. levh-2.26.8.dist-info/top_level.txt +1 -0
  7. server/__init__.py +1 -0
  8. server/api.py +1475 -0
  9. server/cli.py +1666 -0
  10. server/configs.py +210 -0
  11. server/connectors/__init__.py +82 -0
  12. server/connectors/base.py +58 -0
  13. server/connectors/calendar.py +269 -0
  14. server/connectors/email_connector.py +274 -0
  15. server/connectors/github.py +307 -0
  16. server/connectors/local_files.py +275 -0
  17. server/connectors/notion.py +343 -0
  18. server/connectors/obsidian.py +194 -0
  19. server/connectors/transcript.py +204 -0
  20. server/core/__init__.py +1 -0
  21. server/core/admission.py +142 -0
  22. server/core/answerer.py +111 -0
  23. server/core/backup.py +76 -0
  24. server/core/benchmark.py +113 -0
  25. server/core/conflict.py +137 -0
  26. server/core/conflict_service.py +203 -0
  27. server/core/crypto.py +104 -0
  28. server/core/database.py +1145 -0
  29. server/core/demo_data.py +287 -0
  30. server/core/dogfood.py +287 -0
  31. server/core/embedder.py +209 -0
  32. server/core/engine_provider.py +41 -0
  33. server/core/entities.py +123 -0
  34. server/core/entity_index_service.py +112 -0
  35. server/core/episodic.py +65 -0
  36. server/core/evaluation.py +374 -0
  37. server/core/hscore.py +227 -0
  38. server/core/memory_engine.py +2542 -0
  39. server/core/onboarding.py +222 -0
  40. server/core/organizations.py +180 -0
  41. server/core/people.py +145 -0
  42. server/core/rate_limit.py +41 -0
  43. server/core/runtime_config.py +179 -0
  44. server/core/short_term.py +51 -0
  45. server/core/summarizer.py +98 -0
  46. server/core/trust.py +245 -0
  47. server/core/trust_service.py +202 -0
  48. server/core/types.py +126 -0
  49. server/core/vector_store.py +99 -0
  50. server/dashboard/.gitkeep +0 -0
  51. server/dashboard/404/index.html +1 -0
  52. server/dashboard/404.html +1 -0
  53. server/dashboard/_next/static/EZcdmRBfcJ0DYaaVxZUoA/_buildManifest.js +1 -0
  54. server/dashboard/_next/static/EZcdmRBfcJ0DYaaVxZUoA/_ssgManifest.js +1 -0
  55. server/dashboard/_next/static/chunks/172-374e517eabaafcd5.js +1 -0
  56. server/dashboard/_next/static/chunks/248-0c9b0a583e391364.js +1 -0
  57. server/dashboard/_next/static/chunks/255-d19a68d5cdf61c74.js +1 -0
  58. server/dashboard/_next/static/chunks/344-3be46b3334d4bc18.js +1 -0
  59. server/dashboard/_next/static/chunks/354-9411c560837c5643.js +1 -0
  60. server/dashboard/_next/static/chunks/356-5550b6a9746410aa.js +1 -0
  61. server/dashboard/_next/static/chunks/4bd1b696-409494caf8c83275.js +1 -0
  62. server/dashboard/_next/static/chunks/546-53d4e5ffa0741b01.js +1 -0
  63. server/dashboard/_next/static/chunks/558-9f3c6f8ed16a6154.js +1 -0
  64. server/dashboard/_next/static/chunks/619-f072ac750404f9da.js +1 -0
  65. server/dashboard/_next/static/chunks/710-d525d6a7457e5361.js +1 -0
  66. server/dashboard/_next/static/chunks/850-f246cebe0acecdde.js +1 -0
  67. server/dashboard/_next/static/chunks/993-0d39fec9aa3922aa.js +1 -0
  68. server/dashboard/_next/static/chunks/app/_not-found/page-bb8623766fc272ad.js +1 -0
  69. server/dashboard/_next/static/chunks/app/briefing/page-431da3d802906b86.js +1 -0
  70. server/dashboard/_next/static/chunks/app/conflicts/page-f310caecd3370eab.js +1 -0
  71. server/dashboard/_next/static/chunks/app/decisions/page-506a20f430b2d7e1.js +1 -0
  72. server/dashboard/_next/static/chunks/app/graph/page-9342ca8482acab35.js +1 -0
  73. server/dashboard/_next/static/chunks/app/layout-0887bec82bd4e181.js +1 -0
  74. server/dashboard/_next/static/chunks/app/meeting-prep/page-960d2dddc5228bdf.js +1 -0
  75. server/dashboard/_next/static/chunks/app/memories/page-14785c77921a05f7.js +1 -0
  76. server/dashboard/_next/static/chunks/app/organizations/page-530f19e026dfd476.js +1 -0
  77. server/dashboard/_next/static/chunks/app/page-9070d4eaaebc7f50.js +1 -0
  78. server/dashboard/_next/static/chunks/app/people/page-35fc9df360827250.js +1 -0
  79. server/dashboard/_next/static/chunks/app/projects/page-c084863047d2a1a6.js +1 -0
  80. server/dashboard/_next/static/chunks/app/review/page-2f2ac3f22a8e3698.js +1 -0
  81. server/dashboard/_next/static/chunks/app/sessions/page-efdac681166a9439.js +1 -0
  82. server/dashboard/_next/static/chunks/app/settings/page-8c0c28a283f49875.js +1 -0
  83. server/dashboard/_next/static/chunks/app/timeline/page-9436e6fa22ee3645.js +1 -0
  84. server/dashboard/_next/static/chunks/app/visualize/page-0d25c2374a0b6fce.js +1 -0
  85. server/dashboard/_next/static/chunks/framework-f52ebcb9f26a1e11.js +1 -0
  86. server/dashboard/_next/static/chunks/main-875cb23dc0a1f18e.js +1 -0
  87. server/dashboard/_next/static/chunks/main-app-181ccbd6eeeceb1e.js +1 -0
  88. server/dashboard/_next/static/chunks/pages/_app-5addca2b3b969fde.js +1 -0
  89. server/dashboard/_next/static/chunks/pages/_error-022e4ac7bbb9914f.js +1 -0
  90. server/dashboard/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  91. server/dashboard/_next/static/chunks/webpack-681601bcc1224fcd.js +1 -0
  92. server/dashboard/_next/static/css/809d4951adcd5b63.css +3 -0
  93. server/dashboard/brand/levh-mark.png +0 -0
  94. server/dashboard/briefing/index.html +1 -0
  95. server/dashboard/briefing/index.txt +24 -0
  96. server/dashboard/conflicts/index.html +1 -0
  97. server/dashboard/conflicts/index.txt +24 -0
  98. server/dashboard/decisions/index.html +1 -0
  99. server/dashboard/decisions/index.txt +24 -0
  100. server/dashboard/graph/index.html +1 -0
  101. server/dashboard/graph/index.txt +24 -0
  102. server/dashboard/icon.svg +9 -0
  103. server/dashboard/index.html +1 -0
  104. server/dashboard/index.txt +24 -0
  105. server/dashboard/meeting-prep/index.html +1 -0
  106. server/dashboard/meeting-prep/index.txt +24 -0
  107. server/dashboard/memories/index.html +1 -0
  108. server/dashboard/memories/index.txt +24 -0
  109. server/dashboard/organizations/index.html +1 -0
  110. server/dashboard/organizations/index.txt +24 -0
  111. server/dashboard/people/index.html +1 -0
  112. server/dashboard/people/index.txt +24 -0
  113. server/dashboard/projects/index.html +1 -0
  114. server/dashboard/projects/index.txt +24 -0
  115. server/dashboard/review/index.html +1 -0
  116. server/dashboard/review/index.txt +24 -0
  117. server/dashboard/sessions/index.html +1 -0
  118. server/dashboard/sessions/index.txt +24 -0
  119. server/dashboard/settings/index.html +1 -0
  120. server/dashboard/settings/index.txt +24 -0
  121. server/dashboard/timeline/index.html +1 -0
  122. server/dashboard/timeline/index.txt +24 -0
  123. server/dashboard/visualize/index.html +1 -0
  124. server/dashboard/visualize/index.txt +24 -0
  125. server/fixtures/__init__.py +0 -0
  126. server/fixtures/evaluation/01_project_decision_superseded.json +30 -0
  127. server/fixtures/evaluation/02_meeting_deadline_changed.json +31 -0
  128. server/fixtures/evaluation/03_same_fact_independent_sources.json +41 -0
  129. server/fixtures/evaluation/04_same_source_duplicate.json +24 -0
  130. server/fixtures/evaluation/05_low_trust_imported_text.json +19 -0
  131. server/fixtures/evaluation/06_confirmed_human_memory.json +31 -0
  132. server/fixtures/evaluation/07_fading_important_memory.json +35 -0
  133. server/fixtures/evaluation/08_redacted_secret.json +18 -0
  134. server/fixtures/evaluation/09_conflict_false_positive_guard.json +28 -0
  135. server/mcp_sse.py +55 -0
  136. server/mcp_stdio.py +71 -0
  137. server/routes/__init__.py +1 -0
  138. server/tools/__init__.py +1 -0
  139. server/tools/admission.py +74 -0
  140. server/tools/ask.py +52 -0
  141. server/tools/backup.py +87 -0
  142. server/tools/briefing.py +57 -0
  143. server/tools/clear_short_term.py +19 -0
  144. server/tools/conflicts.py +90 -0
  145. server/tools/connector_sync.py +86 -0
  146. server/tools/connectors.py +126 -0
  147. server/tools/consolidate.py +25 -0
  148. server/tools/consolidate_memories.py +56 -0
  149. server/tools/context_file.py +31 -0
  150. server/tools/decisions.py +37 -0
  151. server/tools/dedupe.py +50 -0
  152. server/tools/entities.py +80 -0
  153. server/tools/export_import.py +43 -0
  154. server/tools/feedback.py +74 -0
  155. server/tools/forget.py +30 -0
  156. server/tools/get_context.py +35 -0
  157. server/tools/list_memories.py +55 -0
  158. server/tools/meeting_prep.py +61 -0
  159. server/tools/organizations.py +78 -0
  160. server/tools/people.py +69 -0
  161. server/tools/pin.py +42 -0
  162. server/tools/privacy.py +72 -0
  163. server/tools/profiles.py +137 -0
  164. server/tools/projects.py +42 -0
  165. server/tools/recall.py +49 -0
  166. server/tools/register.py +143 -0
  167. server/tools/reinforce.py +35 -0
  168. server/tools/related.py +28 -0
  169. server/tools/review.py +80 -0
  170. server/tools/search.py +41 -0
  171. server/tools/session.py +48 -0
  172. server/tools/set_importance.py +23 -0
  173. server/tools/stats.py +26 -0
  174. server/tools/store.py +65 -0
  175. server/tools/summarize.py +27 -0
  176. server/tools/timeline.py +34 -0
  177. server/tools/trust.py +72 -0
  178. server/tools/update.py +48 -0
@@ -0,0 +1,677 @@
1
+ Metadata-Version: 2.4
2
+ Name: levh
3
+ Version: 2.26.8
4
+ Summary: Shared memory layer for AI coding tools — memory that forgets like you do, unless it matters
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/agiulucom42-del/stackmemory-new
7
+ Project-URL: Repository, https://github.com/agiulucom42-del/stackmemory-new
8
+ Keywords: mcp,memory,ai,claude,cursor,llm,forgetting-curve,spaced-repetition
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Topic :: Software Development :: Libraries
15
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: fastapi>=0.115
20
+ Requires-Dist: uvicorn>=0.30
21
+ Requires-Dist: aiosqlite>=0.20
22
+ Requires-Dist: numpy>=2.0
23
+ Requires-Dist: mcp>=1.0
24
+ Requires-Dist: fastmcp>=2.0
25
+ Requires-Dist: python-dotenv>=1.0
26
+ Requires-Dist: httpx>=0.27
27
+ Requires-Dist: cryptography>=42
28
+ Provides-Extra: local
29
+ Requires-Dist: sentence-transformers>=3.0; extra == "local"
30
+ Requires-Dist: transformers>=4.40; extra == "local"
31
+ Requires-Dist: torch>=2.0; extra == "local"
32
+ Provides-Extra: openai
33
+ Provides-Extra: secure
34
+ Requires-Dist: cryptography>=42; extra == "secure"
35
+ Provides-Extra: dev
36
+ Requires-Dist: pytest; extra == "dev"
37
+ Requires-Dist: pytest-asyncio; extra == "dev"
38
+ Requires-Dist: build; extra == "dev"
39
+ Requires-Dist: twine; extra == "dev"
40
+ Dynamic: license-file
41
+
42
+ # LEVH
43
+
44
+ > **Brand transition:** LEVH is the product name. The PyPI package will be published as `levh`. The CLI command, environment variables, and database format remain `stackmemory` in this release for compatibility.
45
+
46
+
47
+ <p align="center">
48
+ <strong>StackMemory</strong><br>
49
+ Shared Memory Layer for AI Coding Workflows<br>
50
+ <em>Memory that forgets like you do — unless it matters.</em>
51
+ </p>
52
+
53
+ <p align="center">
54
+ <img src="https://img.shields.io/badge/MCP-Protocol-blue?logo=anthropic" alt="MCP">
55
+ <img src="https://img.shields.io/badge/Claude-Desktop%20%7C%20Code-7c3aed?logo=claude" alt="Claude">
56
+ <img src="https://img.shields.io/badge/Cursor-IDE-000?logo=cursor" alt="Cursor">
57
+ <img src="https://img.shields.io/badge/VS%20Code-Extension-007acc?logo=visualstudiocode" alt="VS Code">
58
+ <img src="https://img.shields.io/badge/SQLite-Embedded-003b57?logo=sqlite" alt="SQLite">
59
+ <img src="https://img.shields.io/badge/Python-3.11%2B-3776ab?logo=python" alt="Python">
60
+ <img src="https://img.shields.io/badge/License-MIT-green" alt="License">
61
+ </p>
62
+
63
+ ---
64
+
65
+ ## What is StackMemory?
66
+
67
+ StackMemory gives your AI coding agents a **persistent, searchable memory** across sessions, projects, and tools. Instead of starting from scratch every time, your AI remembers what you discussed, decisions you made, and context from every workspace.
68
+
69
+ Most memory tools optimize for **perfect recall** — store everything, retrieve everything, let noise pile up forever. StackMemory instead models memory the way **human memory actually works**: every memory has its own decay curve; unused memories fade; memories you actually rely on get reinforced and become durable, automatically, with no manual curation. Signal rises to the top on its own.
70
+
71
+ It works as an **MCP (Model Context Protocol) server** — plug it into Claude Desktop, Cursor, Claude Code, VS Code (Cline), Windsurf, or any MCP-compatible client. It also exposes a **REST API + WebSocket** for custom integrations and ships with a built-in **dashboard** with a real-time activity feed.
72
+
73
+ **Key insight**: Your AI tools (Claude, Cursor, Copilot) are stateless. StackMemory makes them stateful — and self-curating.
74
+
75
+ ---
76
+
77
+ ## The memory model
78
+
79
+ This is the core mechanic, not a footnote:
80
+
81
+ - **Every memory has its own half-life.** New memories start at a default (168h) — like short-term memory, they fade fast unless something happens.
82
+ - **Recalling a memory resets its clock and reinforces it.** Each time a memory is retrieved (or explicitly reinforced), its half-life grows — modeled on spaced repetition / the testing effect, the same mechanism flashcard apps like Anki use to build long-term retention.
83
+ - **Importance accelerates consolidation**, like emotional salience in human memory: a `0.9`-importance memory becomes durable much faster per recall than a `0.1` one.
84
+ - **Outcome feedback closes the loop.** `memory_feedback(helpful=false)` cuts a memory's stability so wrong or stale information fades out fast instead of resurfacing; `helpful=true` reinforces it. Your AI can call this whenever you correct it.
85
+ - **New information interferes with old.** Storing a memory near-identical to an existing one weakens the older version (retroactive interference) — "the deploy branch is prod" naturally supersedes "the deploy branch is main" without anyone deleting anything.
86
+ - **Pinning is permanent encoding** — for rules and facts that must never be forgotten, skip decay entirely. Pinned memories are also immune to interference and never auto-deduped.
87
+ - **Fading memories surface for review.** The dashboard (and the `list_fading_memories` tool) shows memories whose predicted retention has dropped below 35% — rescue what still matters with one click, let the rest go.
88
+ - Every memory's curve is visible in the dashboard: open any memory to see its predicted retention over the next 30 days, its current strength, and one-click **Reinforce** / **Stale** actions.
89
+
90
+ ```
91
+ retention(t) = 0.5 ^ (hours_since_last_recall / stability_hours)
92
+ stability_hours(after recall) = min(stability × (1 + gain·(0.5+importance)), max_stability)
93
+ stability_hours(negative feedback) = max(stability × weaken_factor, 1h)
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Features
99
+
100
+ - **Adaptive memory decay**: per-memory half-life, reinforced by recall, weakened by negative feedback and interference, visualized as a forgetting curve — see [The memory model](#the-memory-model)
101
+ - **3-Layer Memory**: ShortTerm (FIFO deque, max 50) → Episodic (SQLite) → Vector Store (NumPy cosine similarity)
102
+ - **H(x,ψ) Scoring**: Multi-factor ranking: `α·(1-similarity) + β·(1-decay) + γ·(1-importance) + δ·(1-frequency)` — every weight env-configurable, every score explainable in the UI
103
+ - **Ask your memory**: `ask_memory` / `POST /api/ask` / dashboard "Ask" panel — a natural-language question returns a synthesized answer that **cites the exact memories** it drew from (with dates). LLM-powered when `OPENAI_API_KEY` is set, deterministic ranked-evidence fallback offline. Read-only: asking never reinforces memories.
104
+ - **People graph**: everyone you interact with, extracted automatically from calendar attendees, email senders/recipients, and transcript speakers — `/api/people`, `list_people`/`about_person` MCP tools, and a dashboard **People** page ("what do I know about X?"). No manual tagging.
105
+ - **Timeline**: episodic memories grouped by the day they actually happened — "what happened this/last week" — `/api/timeline`, the `timeline` MCP tool, and a dashboard **Timeline** page.
106
+ - **Daily Briefing**: memory as an active assistant — today's events, open commitments detected from your own words ("I'll send…", "yapacağım…"), and what you may be forgetting. Fully offline/deterministic (no LLM key). `/api/briefing`, the `briefing` MCP tool, and a dashboard **Briefing** page.
107
+ - **Organizations**: the people graph rolled up by email domain — the companies you actually interact with, who from them, and how often. Personal email providers excluded. `/api/organizations`, `list_organizations`/`about_organization` MCP tools, and a dashboard **Organizations** page.
108
+ - **Decisions**: decision statements detected from memory content ("we decided", "agreed to", "karar verdik") — what was decided, and when/where. Deterministic, no LLM. `/api/decisions`, the `list_decisions` MCP tool, and a dashboard **Decisions** page.
109
+ - **Encrypted Backup & Restore**: full portable snapshot (every memory *with its decay state* + every session), optionally encrypted at rest with a passphrase (PBKDF2 + Fernet/AES). `/api/backup` + `/api/restore`, `create_backup`/`restore_backup` MCP tools, and a Settings → **Backup & Restore** panel with merge/replace.
110
+ - **Meeting Prep**: the proactive "before you walk in" brief — your next meeting, who's attending, what you last discussed with each of them, plus relevant open commitments and decisions. Deterministic, offline. `/api/meeting-prep`, the `meeting_prep` MCP tool, and a dashboard **Meeting Prep** page.
111
+ - **Memory Consolidation**: sleep-like compression — clusters of related, aged, unpinned memories collapse into one durable summary each (the raw episodes archived inside it, not lost). `/api/memories/consolidate-similar`, the `consolidate_similar` MCP tool, and Settings → Data Management buttons.
112
+ - **Spaced-Repetition Review**: the fading queue as an active review flow — keep / reinforce / weaken / pin / snooze / forget each memory losing strength, every decision recorded in its history. Closes the lifecycle loop. `/api/memories/review`, `list_review_memories`/`review_memory` MCP tools, `stackmemory review` CLI, and a dashboard **Review** page.
113
+ - **Memory Admission Gate**: decides what happens to an incoming memory *before* it's stored — admit / review / reject (duplicates) / redact (strips secrets like API keys & passwords; keeps emails). Deterministic, offline. `/api/memories/admit` + `/api/memories/evaluate-admission`, `admit_memory`/`evaluate_admission` MCP tools, `stackmemory admit` CLI, and a Settings preview card.
114
+ - **Connector Framework v2**: gate-integrated, incremental ingest — every fetched item is routed through the admission gate (dedupe + secret redaction) with per-item error isolation, and each run is recorded so re-syncing is incremental and reportable. `/api/connectors/sync` + `/api/connectors/sync-state`, `sync_connector`/`connector_sync_status` MCP tools, `stackmemory sync` CLI, and a Settings gate toggle + sync history.
115
+ - **Hard-delete Audit & Redaction**: deletes a memory from the tracked primary stores and reports any detected residue (`purge_memory`), and finds + strips secrets stored before the gate existed (`audit_secrets` / `redact_secrets`, logged to each memory's redaction history). Full derived-state cascade is verified by the 2.26.x privacy-hardening gates before public RC. `/api/memories/audit-secrets` + `/redact-all` + `/{id}/redact` + `/{id}/purge`, matching MCP tools, `stackmemory audit-secrets`/`redact-secrets`/`purge` CLI, and a Settings **Privacy & Redaction** card.
116
+ - **Entity Knowledge Graph**: memories indexed into persistent `entities` + `memory_entities` tables — person / organization / event / document / task — so "which memories mention X" and "which entities co-occur with X" are real joins. `/api/entities` (reindex / stats / list / detail), `reindex_entities`/`list_entities`/`about_entity` MCP tools, `stackmemory entities` CLI, and a dashboard **Graph** page.
117
+ - **Provenance / Trust Score**: a deterministic, explainable *reliability* signal per memory — source type + entity-graph corroboration (how many independent sources back it) + review lifecycle + recency − risk flags → a `confidence` with a label and a human-readable breakdown. Separate from the H-score; never changes recall ranking; not a "truth" claim. `/api/memories/{id}/trust` + `/trust/recompute` + `/low-trust`, `memory_trust`/`recompute_trust_scores`/`list_low_trust_memories` MCP tools, `stackmemory trust` CLI, and a Settings **Trust & provenance** card.
118
+ - **Conflict Candidates**: deterministic flagging of memories that *might* disagree (share an entity + an opposing surface pattern: antonym / negation / same-attribute-different-value) — a **review signal, never a verdict**, never auto-deletes; an open candidate adds a small risk to the trust score. Not LLM contradiction detection. `/api/conflicts` (detect / list / review), `detect_conflict_candidates`/`list_conflict_candidates`/`review_conflict_candidate` MCP tools, `stackmemory conflicts` CLI, and a dashboard **Conflicts** page.
119
+ - **59 MCP Tools**: Store, admit, evaluate-admission, recall, ask, forget, purge, audit-secrets, redact-secrets, search, update, list, stats, consolidate, consolidate-similar, sessions, export/import, backup/restore, connectors, connector-sync, pinning, reinforcement, feedback, fading review, spaced-repetition review, projects, people, organizations, decisions, timeline, briefing, meeting prep, context files, dedupe, related memories, session summarization
120
+ - **Auto-Capture Summaries**: `summarize_session` distills a session's memories into one durable memory — LLM-powered when `OPENAI_API_KEY` is set, deterministic offline fallback otherwise; can run automatically on `end_session` (`AUTO_SUMMARIZE_SESSIONS=true`)
121
+ - **Related Memories**: live nearest-neighbour "see also" for any memory, computed from embedding similarity — no manual linking, no extra schema
122
+ - **Recall Quality Benchmark**: `stackmemory benchmark` / Settings panel reports hit@1/hit@3/hit@5/MRR on a labelled query set, so recall quality is measurable, not just claimed
123
+ - **Project Memory**: Namespace memories per repo/workspace; filter recall by project
124
+ - **Source Tracking**: Know which AI client (claude-code, cursor, ...) stored each memory
125
+ - **Pinned Memories**: Rules and decisions that never decay and always surface
126
+ - **Context File Generation**: Compile memories into `CLAUDE.md` / `.cursorrules` so every session starts pre-loaded — from the UI, the CLI, or an MCP tool
127
+ - **Auto-Capture**: `stackmemory hook install` captures every git commit message as a memory; `stackmemory capture` for one-liners (auto-detects your repo as the project)
128
+ - **4 Embedding Modes**: OpenAI `text-embedding-3-small`, local `all-MiniLM-L6-v2`, **Ollama** (fully offline), or deterministic hash fallback — the system always works
129
+ - **App Connectors**: Import from Calendar (.ics), Email (.mbox/.eml), Meeting transcripts (.vtt/.srt), Notion, Obsidian, GitHub repos, local files
130
+ - **Live Dashboard**: Next.js dashboard served by the API itself (one process, one port) with a real-time WebSocket activity feed, semantic search, insights charts, and full memory management
131
+ - **Zero Infrastructure**: SQLite (no MongoDB, no Redis, no external services); old v1 databases migrate automatically
132
+
133
+ ---
134
+
135
+ ## Quick Start
136
+
137
+ ### 1. Install
138
+
139
+ ```bash
140
+ git clone https://github.com/ali-ulu/levh.git
141
+ cd levh
142
+ pip install -e ".[dev]"
143
+ # PyPI package coming soon — until published, install from source with: pip install -e .
144
+ pip install levh
145
+ ```
146
+
147
+ ### 2. Configure
148
+
149
+ ```bash
150
+ cp .env.example .env
151
+ # Defaults work out of the box; set EMBEDDER_MODE=local for semantic search
152
+ ```
153
+
154
+ ### First run: choose demo or real data
155
+
156
+ **Try the demo**
157
+
158
+ ```bash
159
+ # PyPI package coming soon — until published, install from source with: pip install -e .
160
+ pip install levh
161
+ stackmemory setup --demo --client claude --profile work
162
+ stackmemory serve
163
+ ```
164
+
165
+ **Start with real data**
166
+
167
+ ```bash
168
+ # PyPI package coming soon — until published, install from source with: pip install -e .
169
+ pip install levh
170
+ stackmemory setup --real --client claude --profile work
171
+ stackmemory capture "Atlas uses PostgreSQL in production."
172
+ stackmemory serve
173
+ ```
174
+
175
+ The first-run dashboard offers the same two paths, shows real readiness state,
176
+ generates focused MCP configs, and explains the local/off-by-default dogfood
177
+ measurement. See [Getting Started](docs/getting-started.md) and the
178
+ [5-minute demo](docs/demo/5-minute-demo.md).
179
+
180
+ ### 3. Run
181
+
182
+ ```bash
183
+ stackmemory serve # or: uvicorn server.api:app --port 8000
184
+ # Dashboard + API on http://localhost:8000
185
+ # MCP SSE stream endpoint: http://localhost:8000/api/mcp/sse
186
+ ```
187
+
188
+ The dashboard is served by the API. Source checkouts serve `frontend/out/`; built
189
+ wheels include a packaged static export under `server/dashboard/`. To rebuild the
190
+ dashboard after changes: `cd frontend && npm ci && npm run build`.
191
+
192
+ ### Try it in 5 minutes
193
+
194
+ Empty store? Load a deterministic demo corpus — 4 people, 2 organizations, a few
195
+ decisions and tasks, meetings, and one real conflict candidate — so every view has
196
+ something to show:
197
+
198
+ ```bash
199
+ stackmemory seed-demo # or click "Load demo data" on the empty dashboard
200
+ ```
201
+
202
+ Then open the dashboard and explore **Briefing** (daily digest), **Meeting Prep**
203
+ (who/what before a call), **Conflicts** (review the disputed Atlas deadline), and
204
+ **Insights** (watch low-importance memories fade while pinned ones persist). The
205
+ seed refuses to touch a non-empty store unless you pass `--force`.
206
+
207
+ ### 4. Connect Your AI Client
208
+
209
+ Open **Settings** in the dashboard for copy-paste MCP configs, or see
210
+ [Platform Setup](#platform-setup) below.
211
+
212
+ ### 5. (Optional) Auto-capture your work
213
+
214
+ ```bash
215
+ stackmemory hook install # capture every git commit message
216
+ stackmemory capture "we use pnpm, not npm" --pin
217
+ stackmemory context -o CLAUDE.md # compile memory into a context file
218
+ ```
219
+
220
+ ---
221
+
222
+ ## Platform Setup
223
+
224
+ All clients use the same stdio server. Generate any config with
225
+ `stackmemory mcp config <claude|claude_code|cursor|windsurf|vscode|cline>`.
226
+
227
+ ### Claude Desktop
228
+
229
+ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
230
+
231
+ ```json
232
+ {
233
+ "mcpServers": {
234
+ "stackmemory": {
235
+ "command": "python",
236
+ "args": ["-m", "server.mcp_stdio"],
237
+ "cwd": "/path/to/stackmemory-new",
238
+ "env": {
239
+ "EMBEDDER_MODE": "local"
240
+ }
241
+ }
242
+ }
243
+ }
244
+ ```
245
+
246
+ ### Cursor IDE
247
+
248
+ Same server block in `.cursor/mcp.json` in your project root.
249
+
250
+ ### Claude Code (CLI)
251
+
252
+ Same server block in `~/.claude.json`.
253
+
254
+ ### VS Code (Cline Extension)
255
+
256
+ Same server block (under a `servers` key) in `.vscode/mcp.json`.
257
+
258
+ ### Windsurf
259
+
260
+ Same server block in `~/.codeium/windsurf/mcp_config.json`.
261
+
262
+ ---
263
+
264
+ ## Architecture
265
+
266
+ ```
267
+ ┌─────────────────────────────────────────────┐
268
+ │ StackMemory Server │
269
+ │ │
270
+ Claude Desktop ───┤ ┌─────────┐ ┌──────────┐ ┌──────┐ │
271
+ Cursor IDE ───┤ │ MCP │ │ Memory │ │Embed │ │
272
+ Claude Code ───┤ │ Server ├───▶│ Engine ├───▶│OpenAI│ │
273
+ VS Code/Cline ───┤ │(stdio + │ │ 3-Layer │ │Local │ │
274
+ Windsurf ───┤ │ SSE) │ │ H(x,ψ) │ │Ollama│ │
275
+ │ └─────────┘ └────┬─────┘ └──────┘ │
276
+ REST API ───┤ one shared │ │
277
+ WebSocket ───┤ engine ┌─────▼──────┐ │
278
+ Dashboard (/) ───┤ instance │ SQLite │ │
279
+ │ │ (aiosqlite)│ │
280
+ ─────────────────┤ └────────────┘ │
281
+ App Connectors: │ │
282
+ Notion / Obsidian │ import_from_app() → store → recall │
283
+ GitHub / Files ───┤ │
284
+ └─────────────────────────────────────────────┘
285
+ ```
286
+
287
+ All transports share **one engine instance** — a memory stored via MCP is
288
+ instantly visible in the dashboard's live feed, and vice versa.
289
+
290
+ ---
291
+
292
+ ## 59 MCP Tools
293
+
294
+ > **Tool profiles.** Advertising all 59 tools to a client hurts tool-selection
295
+ > accuracy, so StackMemory groups them into cumulative profiles —
296
+ > `minimal` (5) ⊂ `work` (15) ⊂ `admin` (54) ⊂ `full` (59). Generated configs
297
+ > default to **`work`**; run `stackmemory mcp profiles` to see the bands, or set
298
+ > `STACKMEMORY_MCP_PROFILE` / `mcp config --profile <name>` to change it. The
299
+ > full table below is the `full` surface. Profiles only filter which tools a
300
+ > client sees — they are **not** an authentication or authorization boundary;
301
+ > every profile talks to the same engine instance with the same access.
302
+
303
+ | # | Tool | Description |
304
+ |---|------|-------------|
305
+ | 1 | `store_memory` | Store a memory with importance, tags, project, source, pin |
306
+ | 2 | `recall_memory` | Recall memories ranked by H(x,ψ) score (filter by session/project) |
307
+ | 3 | `forget_memory` | Delete from all 3 layers |
308
+ | 4 | `search_memory` | Semantic search with detailed results |
309
+ | 5 | `update_memory` | Update content/importance/tags |
310
+ | 6 | `list_memories` | List with type/tag/session/project/source/pinned filters |
311
+ | 7 | `get_memory_stats` | System statistics and metrics |
312
+ | 8 | `consolidate_memories` | Promote short-term → episodic |
313
+ | 9 | `clear_short_term` | Clear the live FIFO deque |
314
+ | 10 | `set_importance` | Set importance (0.0-1.0) |
315
+ | 11 | `get_context` | Build context window (short-term + pinned + important) |
316
+ | 12 | `create_session` | Start a named session |
317
+ | 13 | `end_session` | End session + consolidate its memories |
318
+ | 14 | `export_memories` | Export all memories as JSON |
319
+ | 15 | `import_memories` | Import memories from JSON |
320
+ | 16 | `import_from_app` | Import from Calendar/Email/Transcripts/Notion/Obsidian/GitHub/local files |
321
+ | 17 | `list_connectors` | List available app connectors |
322
+ | 18 | `get_connector_help` | Get config help for a connector |
323
+ | 19 | `pin_memory` | Pin a memory — exempt from decay, always in context files |
324
+ | 20 | `unpin_memory` | Restore normal decay |
325
+ | 21 | `list_projects` | Workspaces with memory counts |
326
+ | 22 | `list_sources` | Which AI clients stored memories |
327
+ | 23 | `generate_context_file` | Compile memories into CLAUDE.md / .cursorrules |
328
+ | 24 | `dedupe_memories` | Find/remove near-duplicates (dry-run by default) |
329
+ | 25 | `reinforce_memory` | Manually strengthen a memory — resets decay clock, grows stability |
330
+ | 26 | `memory_feedback` | helpful=true reinforces; helpful=false makes wrong/stale info fade fast |
331
+ | 27 | `list_fading_memories` | Review queue of memories about to be forgotten |
332
+ | 28 | `related_memories` | Nearest-neighbour "see also" for a memory (live, embedding-based) |
333
+ | 29 | `summarize_session` | Distill a session's memories into one durable summary memory |
334
+ | 30 | `ask_memory` | Ask your memory a question → synthesized, cited answer (read-only) |
335
+ | 31 | `list_people` | People across your memories (calendar/email/transcript), by frequency |
336
+ | 32 | `about_person` | One person's profile + the memories mentioning them |
337
+ | 33 | `timeline` | Recent memories grouped by day (what happened this/last week) |
338
+ | 34 | `briefing` | Daily briefing — today's events, open commitments, and what you may be forgetting |
339
+ | 35 | `list_organizations` | Organizations across your memories (people grouped by email domain) |
340
+ | 36 | `about_organization` | One organization's profile + the memories mentioning it |
341
+ | 37 | `list_decisions` | Decisions detected in recent memories — what was decided, when/where |
342
+ | 38 | `create_backup` | Write a full backup (memories + sessions) to a file, optionally passphrase-encrypted |
343
+ | 39 | `restore_backup` | Restore memories + sessions from a backup file (merge or replace) |
344
+ | 40 | `meeting_prep` | Pre-meeting brief — next meeting, attendees, what you last discussed, open items |
345
+ | 41 | `consolidate_similar` | Compress clusters of related aged memories into durable summaries (sleep-like) |
346
+ | 42 | `list_review_memories` | Memories due for spaced-repetition review (fading, unpinned, un-snoozed) |
347
+ | 43 | `review_memory` | Apply a review decision — keep / reinforce / weaken / pin / forget / snooze |
348
+ | 44 | `evaluate_admission` | Preview the admission verdict for candidate text (admit/review/reject/redact) |
349
+ | 45 | `admit_memory` | Store through the admission gate — dedupe + secret redaction |
350
+ | 46 | `sync_connector` | Connector v2 — fetch + gate-filtered incremental ingest |
351
+ | 47 | `connector_sync_status` | Per-source sync bookkeeping (last synced, totals, run count) |
352
+ | 48 | `audit_secrets` | Scan stored memories for secrets (credentials, tokens) |
353
+ | 49 | `redact_secrets` | Strip secrets from stored memories (preview or apply) |
354
+ | 50 | `purge_memory` | Hard-delete a memory and report residue across tracked storage layers |
355
+ | 51 | `reindex_entities` | Rebuild the persistent entity graph from all memories |
356
+ | 52 | `list_entities` | List graph entities by type, most-mentioned first |
357
+ | 53 | `about_entity` | One entity's profile — its memories and co-occurring entities |
358
+ | 54 | `memory_trust` | A memory's provenance/trust breakdown (confidence + explainable evidence) |
359
+ | 55 | `recompute_trust_scores` | Recompute provenance/trust scores for all memories |
360
+ | 56 | `list_low_trust_memories` | Memories below a confidence threshold, least-trusted first |
361
+ | 57 | `detect_conflict_candidates` | Flag memory pairs that might disagree (shared entity + opposing pattern) |
362
+ | 58 | `list_conflict_candidates` | List conflict candidates by status (open/confirmed/…) |
363
+ | 59 | `review_conflict_candidate` | Human review — dismiss / confirm / keep-A / keep-B / both-valid |
364
+
365
+ ---
366
+
367
+ ## CLI
368
+
369
+ ```bash
370
+ stackmemory serve # API + dashboard on :8000
371
+ stackmemory doctor # health checks
372
+ stackmemory setup --status # computed first-run readiness
373
+ stackmemory setup --demo --client claude --profile work
374
+ stackmemory setup --real --client cursor --profile minimal
375
+ stackmemory seed-demo # load a demo corpus into an empty store
376
+ stackmemory capture "note" --pin # store a memory (auto-detects git repo as project)
377
+ stackmemory context -o CLAUDE.md # generate a context file from memories
378
+ stackmemory hook install # git post-commit auto-capture
379
+ stackmemory summarize <session_id> # distill a session into one summary memory
380
+ stackmemory benchmark # recall-quality harness (hit@k / MRR)
381
+ stackmemory mcp config cursor # print MCP config for a client
382
+ stackmemory mcp stdio # run the MCP stdio server
383
+ stackmemory eval run # golden-fixture memory evaluation → eval_report.json
384
+ stackmemory eval report # print the last written evaluation report
385
+ stackmemory dogfood status # aggregate view of the local usage journal
386
+ stackmemory dogfood export -o out.json # write the aggregate dogfood report (explicit)
387
+ ```
388
+
389
+ ---
390
+
391
+ ## H(x,ψ) Scoring Algorithm
392
+
393
+ Memories are ranked by a multi-factor score (lower = more relevant):
394
+
395
+ ```
396
+ H(x,ψ) = α·(1-similarity) + β·(1-decay_factor) + γ·(1-importance) + δ·(1-frequency)
397
+
398
+ α = 0.4 (semantic relevance) — HSCORE_ALPHA
399
+ β = 0.2 (time decay) — HSCORE_BETA
400
+ γ = 0.3 (user-marked importance) — HSCORE_GAMMA
401
+ δ = 0.1 (access frequency) — HSCORE_DELTA
402
+ ```
403
+
404
+ - **Similarity**: Cosine similarity between query and memory embeddings
405
+ - **Decay**: Exponential decay measured from **last access** (not creation) at the memory's own `stability_hours` half-life — default 168h, but it grows every time the memory is recalled or reinforced (see [The memory model](#the-memory-model)). **Pinned memories never decay.**
406
+ - **Importance**: User-assigned importance (0.0-1.0) — also accelerates stability growth on reinforcement
407
+ - **Frequency**: How often the memory has been accessed — only memories actually returned by a recall are counted
408
+ - **Explainability**: every score can be broken into its four components — in the dashboard's detail drawer or via `GET /api/memories/{id}/score-breakdown`; the forgetting curve itself is available via `GET /api/memories/{id}/forgetting-curve`
409
+
410
+ ---
411
+
412
+ ## REST API Endpoints
413
+
414
+ | Method | Endpoint | Description |
415
+ |--------|----------|-------------|
416
+ | POST | `/api/memories` | Store a memory |
417
+ | GET | `/api/memories` | List with filters (`q`, `project`, `source`, `tag`, `pinned`, ...) |
418
+ | GET | `/api/memories/{id}` | Get single memory |
419
+ | PUT | `/api/memories/{id}` | Update memory |
420
+ | PATCH | `/api/memories/{id}/pin` | Pin / unpin |
421
+ | POST | `/api/memories/{id}/reinforce` | Manually strengthen a memory |
422
+ | POST | `/api/memories/{id}/feedback` | helpful=true/false — learn from recall outcomes |
423
+ | GET | `/api/memories/fading` | Memories about to be forgotten (review queue) |
424
+ | DELETE | `/api/memories/{id}` | Delete memory |
425
+ | POST | `/api/memories/recall` | Recall by query (optional project filter) |
426
+ | POST | `/api/memories/consolidate` | Short-term → episodic |
427
+ | POST | `/api/memories/dedupe` | Find or remove near-duplicates |
428
+ | POST | `/api/memories/export` | Export all as JSON |
429
+ | POST | `/api/memories/import` | Import from JSON |
430
+ | GET | `/api/memories/{id}/score-breakdown` | Explain a memory's H(x,ψ) score |
431
+ | GET | `/api/memories/{id}/forgetting-curve` | Predicted retention curve over time |
432
+ | GET | `/api/memories/{id}/related` | Nearest-neighbour "see also" memories |
433
+ | POST | `/api/sessions` | Create session |
434
+ | GET | `/api/sessions` | List sessions |
435
+ | GET | `/api/sessions/{id}` | Get session |
436
+ | PATCH | `/api/sessions/{id}/end` | End session (consolidates) |
437
+ | POST | `/api/sessions/{id}/summarize` | Distill a session into one summary memory |
438
+ | GET | `/api/projects` | Projects with counts |
439
+ | GET | `/api/sources` | AI clients with counts |
440
+ | GET | `/api/tags` | Tags with counts |
441
+ | GET | `/api/people` | People across memories, by frequency |
442
+ | GET | `/api/people/{key}` | A person's profile + their memories |
443
+ | GET | `/api/timeline` | Memories grouped by day |
444
+ | GET | `/api/context` | Current context window |
445
+ | POST | `/api/context-file` | Generate CLAUDE.md / .cursorrules |
446
+ | GET | `/api/stats` | System statistics |
447
+ | GET | `/api/config` | Server configuration |
448
+ | GET | `/api/health` | Health check |
449
+ | POST | `/api/benchmark/recall` | Run the recall-quality benchmark (hit@k / MRR) |
450
+ | WS | `/ws/memory` | Real-time event stream + RPC actions |
451
+ | SSE | `/api/mcp/sse` | MCP SSE stream endpoint |
452
+ | POST | `/api/connectors/import` | Import from app |
453
+ | GET | `/api/connectors` | List connectors |
454
+ | GET | `/api/connectors/{name}/config` | Connector config help |
455
+
456
+ ---
457
+
458
+ ## App Connectors
459
+
460
+ Import data from your existing tools directly into StackMemory's memory layer.
461
+ Every import can be namespaced under a project.
462
+
463
+ ```python
464
+ import_from_app("calendar", config={"ics_path": "/path/to/calendar.ics"}) # or ics_url
465
+ import_from_app("email", config={"mbox_path": "/path/to/mail.mbox"}) # or eml_path / eml_dir
466
+ import_from_app("transcript", config={"transcript_path": "/path/to/meeting.vtt"}) # or transcript_dir
467
+ import_from_app("notion", config={"api_key": "ntn_xxx", "database_ids": ["..."]})
468
+ import_from_app("obsidian", config={"vault_path": "/path/to/vault"})
469
+ import_from_app("github", config={"token": "ghp_xxx", "repos": ["owner/repo"]})
470
+ import_from_app("local_files", config={"directory": "/path/to/project"})
471
+ ```
472
+
473
+ **Calendar, Email & Transcripts — the work-life capture trio** (roadmap Phase 1):
474
+ *when/who* + *correspondence* + *what was said*. All parse the universal *offline*
475
+ export formats with zero extra dependencies, so no OAuth, no API keys, nothing leaves
476
+ your machine:
477
+
478
+ - **Calendar** (`.ics`): the format Google Calendar, Outlook, and Apple Calendar all
479
+ export. Each event → a memory with title, time, attendees, and location — so you can
480
+ ask *"what did I discuss with X last week?"*. Optional `past_days`/`future_days` window.
481
+ - **Email** (`.mbox` / `.eml`): Gmail Takeout, Thunderbird, Apple Mail, Outlook export.
482
+ Each message → a memory with sender, recipients, subject, date, and a body excerpt —
483
+ ask *"what did Dana email me about pricing?"*. Options: `past_days`, `max_messages`,
484
+ `body_chars`, `exclude_senders` (skip no-reply/notification noise).
485
+ - **Transcripts** (`.vtt` / `.srt` / `.txt`): Zoom, Google Meet, Teams, Otter, Fireflies,
486
+ or Whisper output. Each meeting → one **summarized** memory (LLM if `OPENAI_API_KEY` is
487
+ set, offline extractive otherwise) with the speaker list and a transcript excerpt — ask
488
+ *"what did we decide in the roadmap call?"*. Options: `summarize`, `max_chars`.
489
+
490
+ Or use the **Import from Apps** panel in the dashboard's Settings page.
491
+
492
+ ---
493
+
494
+ ## Docker
495
+
496
+ ```bash
497
+ docker compose up -d
498
+ # Dashboard + API: http://localhost:8000
499
+ # MCP SSE stream: http://localhost:8000/api/mcp/sse
500
+ ```
501
+
502
+ One container, one port. The image builds the dashboard and serves it from the API.
503
+
504
+ ---
505
+
506
+ ## Configuration
507
+
508
+ Runtime settings use one precedence order across CLI, API, MCP and generated
509
+ client configs:
510
+
511
+ ```text
512
+ explicit CLI/API override > environment > .stackmemory/config.json > defaults
513
+ ```
514
+
515
+ `stackmemory init` and `stackmemory setup` create `.stackmemory/config.json`.
516
+ Relative database paths in that file are resolved from the working directory.
517
+ StackMemory does not load `.env` implicitly; export environment variables in
518
+ the process that launches it when environment overrides are required.
519
+
520
+ | Variable | Default | Description |
521
+ |----------|---------|-------------|
522
+ | `SQLITE_DB_PATH` | `./stackmemory.db` | SQLite database path |
523
+ | `EMBEDDER_MODE` | `auto` | `auto`, `local`, `openai`, `ollama`, `hash`; `auto` is local-first and never selects OpenAI just because a key exists |
524
+ | `OPENAI_API_KEY` | — | Used only when `EMBEDDER_MODE=openai` (and separately by optional LLM summaries/Ask features) |
525
+ | `LOCAL_MODEL` | `all-MiniLM-L6-v2` | Local embedding model |
526
+ | `OLLAMA_URL` | `http://localhost:11434` | Ollama server (mode `ollama`) |
527
+ | `OLLAMA_MODEL` | `nomic-embed-text` | Ollama embedding model |
528
+ | `SHORT_TERM_MAX` | `50` | Max short-term memories |
529
+ | `DECAY_HALF_LIFE_HOURS` | `168` | Starting half-life for new memories |
530
+ | `HSCORE_ALPHA` | `0.4` | Similarity weight |
531
+ | `HSCORE_BETA` | `0.2` | Decay weight |
532
+ | `HSCORE_GAMMA` | `0.3` | Importance weight |
533
+ | `HSCORE_DELTA` | `0.1` | Frequency weight |
534
+ | `REINFORCEMENT_GAIN` | `0.5` | Stability growth per recall (higher = faster consolidation) |
535
+ | `MAX_STABILITY_HOURS` | `8760` | Cap on how durable a memory can become (1 year) |
536
+ | `FEEDBACK_WEAKEN_FACTOR` | `0.5` | Stability multiplier on negative feedback |
537
+ | `INTERFERENCE_THRESHOLD` | `0.97` | Similarity above which new memories weaken old ones (1.0 = off) |
538
+ | `INTERFERENCE_FACTOR` | `0.6` | Stability multiplier applied to superseded memories |
539
+ | `AUTO_SUMMARIZE_SESSIONS` | `false` | Auto-summarize a session's memories on `end_session` |
540
+ | `SUMMARY_MODEL` | `gpt-4o-mini` | OpenAI chat model used for session summaries |
541
+ | `STACKMEMORY_TOKEN` | — | Optional shared-secret gate on `/api/*` (except `/api/health`) and the WebSocket |
542
+ | `STACKMEMORY_CORS_ORIGINS` | localhost only | Comma-separated allowed browser origins (`*` for wildcard) |
543
+ | `STACKMEMORY_AUTH_RATE_LIMIT` | `10` | Failed token attempts allowed per rate-limit window, per client/process |
544
+ | `STACKMEMORY_API_RATE_LIMIT` | `120` | Authenticated API requests allowed per window, per client/process |
545
+ | `STACKMEMORY_RATE_LIMIT_WINDOW_SECONDS` | `60` | In-process rate-limit window; not a distributed quota system |
546
+ | `STACKMEMORY_SQLITE_BUSY_TIMEOUT_MS` | `5000` | SQLite lock wait before failing; file databases use WAL mode |
547
+ | `STACKMEMORY_SAFETY_BACKUP_DIR` | DB sibling `safety-backups/` | Location for automatic pre-replace SQLite safety backups |
548
+
549
+ ---
550
+
551
+ ## Security
552
+
553
+ StackMemory binds to loopback by default. `stackmemory serve --host 0.0.0.0`
554
+ (or any non-loopback host) is refused unless `STACKMEMORY_TOKEN` is set. The
555
+ Docker Compose default publishes only `127.0.0.1:8000`; deployments that widen
556
+ the bind must set a strong token. CORS is not an authorization boundary.
557
+
558
+ The default REST, WebSocket, MCP `store_memory`, CLI `capture`, and connector
559
+ import paths pass through the deterministic admission gate before persistence.
560
+ Rejected/review candidates are not stored; secrets are redacted. An explicit
561
+ `admit_memory(force=true)` operation is the audited administrative override.
562
+
563
+
564
+ StackMemory is designed as a **local, single-user tool** — no accounts, no
565
+ multi-tenancy. Two lightweight controls harden the default local deployment:
566
+
567
+ - **CORS defaults to localhost origins**, not `*` — without this, any website
568
+ open in your browser could `fetch()` your entire memory store from a
569
+ service running on `localhost:8000`. Widen it with `STACKMEMORY_CORS_ORIGINS`
570
+ only if you know what you're exposing.
571
+ - **Optional shared-secret token** (`STACKMEMORY_TOKEN`) gates every `/api/*`
572
+ route and the WebSocket behind an `X-StackMemory-Token` header (or `?token=`
573
+ for the socket). When enabled, failed token attempts and authenticated API
574
+ traffic are limited in-process. Unset by default so local use stays zero-config.
575
+
576
+ This is not per-user auth or a substitute for running behind your own
577
+ reverse proxy if you expose the service beyond localhost.
578
+
579
+ SQLite file databases use WAL mode, a 5-second busy timeout, numbered
580
+ `PRAGMA user_version` migrations, and an FTS5 text index when the local SQLite
581
+ build supports it. Full-text search falls back to `LIKE` if FTS5 is unavailable.
582
+ Before `restore --replace` changes an existing file database, StackMemory creates
583
+ a consistent online SQLite safety backup in a local `safety-backups/` directory.
584
+
585
+ ---
586
+
587
+ ## Evaluation & Dogfood (2.25)
588
+
589
+ Two offline tools for judging whether the memory system is actually working,
590
+ distinct from the H(x,ψ) recall benchmark above.
591
+
592
+ - **`stackmemory eval run [--fixtures DIR] [--embedder-mode hash|local|...] [--output FILE]`**
593
+ runs the golden-fixture evaluation (`tests/fixtures/evaluation/*.json`)
594
+ through the real admission → store → trust → conflict → recall → review
595
+ pipeline and writes a JSON report (default `eval_report.json`). Deterministic
596
+ for a fixed fixture set + embedder mode.
597
+ - **`stackmemory eval report [--output FILE]`** prints the last written report.
598
+ - **`stackmemory dogfood status`** prints the aggregate view of the local
599
+ usage journal (event counts, time-to-first-value, recall-feedback rate,
600
+ review distribution).
601
+ - **`stackmemory dogfood export --output report.json`** is an explicit user
602
+ action that writes the *aggregate* journal report to a file.
603
+
604
+ Live instrumentation (2.25.1) is **opt-in**: set
605
+ `STACKMEMORY_DOGFOOD_ENABLED=true` and every transport that uses the shared
606
+ engine (REST API / `serve`, MCP stdio, MCP SSE) journals coarse usage events
607
+ automatically — store, recall, briefing opened, meeting prep opened, trust
608
+ viewed, review keep/reinforce/weaken/forget, conflict confirm/dismiss, seed
609
+ demo completed. The journal is a plain JSONL file next to the SQLite database
610
+ (override with `DOGFOOD_JOURNAL_PATH`). With the flag unset (the default)
611
+ nothing is journaled at all.
612
+
613
+ Privacy rules, both tools:
614
+
615
+ - **Local-only, no network.** Neither module performs any network I/O; the
616
+ evaluation report and the dogfood journal are plain local files.
617
+ - **No default telemetry.** Nothing is collected or sent anywhere unless the
618
+ user explicitly sets `STACKMEMORY_DOGFOOD_ENABLED=true`; there is no
619
+ opt-out because there's no opt-in by default.
620
+ - **No raw memory content.** The evaluation report contains fixture keys,
621
+ scenario names, labels, and numbers only. The dogfood journal accepts only
622
+ a whitelisted set of event types and scalar attributes (ids, counts,
623
+ labels) — content and query text are rejected at the API boundary, not
624
+ filtered after the fact.
625
+ - **Export is explicit.** `dogfood export` writes aggregates, never raw event
626
+ lines; you decide if and when a report leaves the machine.
627
+
628
+ Do not quote evaluation numbers (hit@1, hit@3, MRR, precision/recall, etc.)
629
+ from anything other than a real `stackmemory eval run` against a known
630
+ fixture set — every report is tagged with its `evaluation_version`
631
+ (currently `memory-eval-v1`) so a number is only meaningful alongside the
632
+ fixture set that produced it.
633
+
634
+ ---
635
+
636
+ ## Testing
637
+
638
+ ```bash
639
+ pip install -e ".[dev]"
640
+ EMBEDDER_MODE=hash python -m pytest -q
641
+ ```
642
+
643
+ **125 tests** covering memory lifecycle, H(x,ψ) scoring, adaptive decay/reinforcement,
644
+ outcome feedback, retroactive interference, fading review queue, forgetting curves,
645
+ sessions, consolidation, export/import, concurrent operations, edge cases, session
646
+ isolation, project namespacing, source tracking, pinning,
647
+ recall correctness (no side effects on
648
+ non-returned candidates), env-configurable weights, mixed embedding dimensions,
649
+ v1 → v2 schema migration, dedupe, context file generation, related memories,
650
+ session summarization, the recall-quality benchmark harness, and the REST API.
651
+
652
+ Benchmark recall quality directly with `stackmemory benchmark`. Source-tree users
653
+ can also run `python scripts/benchmark_recall.py`; the runtime implementation is
654
+ packaged under `server.core.benchmark` so wheel installs do not depend on the
655
+ non-package `scripts/` directory. Run with `EMBEDDER_MODE=local`, `ollama`, or
656
+ `openai` for a meaningful semantic signal; the default `hash` embedder is
657
+ non-semantic and intended for deterministic smoke checks.
658
+
659
+ ---
660
+
661
+ ## Tech Stack
662
+
663
+ | Component | Technology |
664
+ |-----------|-----------|
665
+ | MCP Server | Python `mcp` SDK + `FastMCP` |
666
+ | API | FastAPI + Uvicorn |
667
+ | Database | SQLite via `aiosqlite` (auto-migrating schema) |
668
+ | Embeddings | OpenAI / sentence-transformers / Ollama / hash |
669
+ | Vector Search | NumPy cosine similarity |
670
+ | Frontend | Next.js 14 (static export) + shadcn/ui + Recharts |
671
+ | Container | Docker (single image: API + dashboard) |
672
+
673
+ ---
674
+
675
+ ## License
676
+
677
+ MIT