homestead-memory 0.2.0__tar.gz
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.
- homestead_memory-0.2.0/LICENSE +21 -0
- homestead_memory-0.2.0/PKG-INFO +272 -0
- homestead_memory-0.2.0/README.md +242 -0
- homestead_memory-0.2.0/pyproject.toml +44 -0
- homestead_memory-0.2.0/setup.cfg +4 -0
- homestead_memory-0.2.0/src/homestead_memory/__init__.py +10 -0
- homestead_memory-0.2.0/src/homestead_memory/adapters/__init__.py +13 -0
- homestead_memory-0.2.0/src/homestead_memory/adapters/autogen_memory.py +118 -0
- homestead_memory-0.2.0/src/homestead_memory/adapters/crewai_memory.py +62 -0
- homestead_memory-0.2.0/src/homestead_memory/adapters/langgraph_store.py +304 -0
- homestead_memory-0.2.0/src/homestead_memory/adapters/litellm_memory.py +110 -0
- homestead_memory-0.2.0/src/homestead_memory/adapters/okf.py +147 -0
- homestead_memory-0.2.0/src/homestead_memory/adapters/openai_agents.py +81 -0
- homestead_memory-0.2.0/src/homestead_memory/adapters/openai_compat.py +110 -0
- homestead_memory-0.2.0/src/homestead_memory/adapters/tools.py +138 -0
- homestead_memory-0.2.0/src/homestead_memory/api/__init__.py +1 -0
- homestead_memory-0.2.0/src/homestead_memory/api/mcp_server.py +362 -0
- homestead_memory-0.2.0/src/homestead_memory/api/server.py +211 -0
- homestead_memory-0.2.0/src/homestead_memory/benchmarks/__init__.py +1 -0
- homestead_memory-0.2.0/src/homestead_memory/benchmarks/longmemeval.py +589 -0
- homestead_memory-0.2.0/src/homestead_memory/benchmarks/official_eval.py +103 -0
- homestead_memory-0.2.0/src/homestead_memory/cli.py +434 -0
- homestead_memory-0.2.0/src/homestead_memory/core/__init__.py +1 -0
- homestead_memory-0.2.0/src/homestead_memory/core/chunking.py +111 -0
- homestead_memory-0.2.0/src/homestead_memory/core/distill.py +346 -0
- homestead_memory-0.2.0/src/homestead_memory/core/index.py +385 -0
- homestead_memory-0.2.0/src/homestead_memory/core/portability.py +376 -0
- homestead_memory-0.2.0/src/homestead_memory/core/provenance.py +59 -0
- homestead_memory-0.2.0/src/homestead_memory/core/remember.py +86 -0
- homestead_memory-0.2.0/src/homestead_memory/core/resolve.py +235 -0
- homestead_memory-0.2.0/src/homestead_memory/core/signing.py +172 -0
- homestead_memory-0.2.0/src/homestead_memory/core/store.py +141 -0
- homestead_memory-0.2.0/src/homestead_memory/core/telemetry.py +68 -0
- homestead_memory-0.2.0/src/homestead_memory/core/temporal.py +258 -0
- homestead_memory-0.2.0/src/homestead_memory/core/tuning.py +103 -0
- homestead_memory-0.2.0/src/homestead_memory/core/vault.py +302 -0
- homestead_memory-0.2.0/src/homestead_memory/core/verify.py +549 -0
- homestead_memory-0.2.0/src/homestead_memory/sdk.py +129 -0
- homestead_memory-0.2.0/src/homestead_memory.egg-info/PKG-INFO +272 -0
- homestead_memory-0.2.0/src/homestead_memory.egg-info/SOURCES.txt +66 -0
- homestead_memory-0.2.0/src/homestead_memory.egg-info/dependency_links.txt +1 -0
- homestead_memory-0.2.0/src/homestead_memory.egg-info/entry_points.txt +3 -0
- homestead_memory-0.2.0/src/homestead_memory.egg-info/requires.txt +22 -0
- homestead_memory-0.2.0/src/homestead_memory.egg-info/top_level.txt +1 -0
- homestead_memory-0.2.0/tests/test_adapters.py +137 -0
- homestead_memory-0.2.0/tests/test_cli_bench.py +54 -0
- homestead_memory-0.2.0/tests/test_distill.py +201 -0
- homestead_memory-0.2.0/tests/test_hot_swap_demo.py +59 -0
- homestead_memory-0.2.0/tests/test_index.py +305 -0
- homestead_memory-0.2.0/tests/test_litellm_memory.py +47 -0
- homestead_memory-0.2.0/tests/test_mcp.py +218 -0
- homestead_memory-0.2.0/tests/test_multi_agent_demo.py +19 -0
- homestead_memory-0.2.0/tests/test_okf.py +218 -0
- homestead_memory-0.2.0/tests/test_openai_compat.py +128 -0
- homestead_memory-0.2.0/tests/test_portability.py +155 -0
- homestead_memory-0.2.0/tests/test_provenance.py +134 -0
- homestead_memory-0.2.0/tests/test_provenance_compat.py +41 -0
- homestead_memory-0.2.0/tests/test_remember.py +219 -0
- homestead_memory-0.2.0/tests/test_resolve.py +244 -0
- homestead_memory-0.2.0/tests/test_rotbench_integrity.py +213 -0
- homestead_memory-0.2.0/tests/test_sdk.py +67 -0
- homestead_memory-0.2.0/tests/test_signing.py +236 -0
- homestead_memory-0.2.0/tests/test_store_newlines.py +18 -0
- homestead_memory-0.2.0/tests/test_temporal_refresh.py +76 -0
- homestead_memory-0.2.0/tests/test_tuning.py +119 -0
- homestead_memory-0.2.0/tests/test_vault.py +121 -0
- homestead_memory-0.2.0/tests/test_verify_json.py +73 -0
- homestead_memory-0.2.0/tests/test_verify_temporal.py +192 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kinetic Labs Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: homestead-memory
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Verifiable, local-first AI memory that catches rot, tampering, and poisoning. Plain markdown you own.
|
|
5
|
+
Author: Kinetic Labs
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: ai,memory,local-first,agents,verification,rag,homestead
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Provides-Extra: api
|
|
15
|
+
Requires-Dist: fastapi>=0.110; extra == "api"
|
|
16
|
+
Requires-Dist: uvicorn>=0.27; extra == "api"
|
|
17
|
+
Provides-Extra: sign
|
|
18
|
+
Requires-Dist: cryptography>=42; extra == "sign"
|
|
19
|
+
Provides-Extra: langgraph
|
|
20
|
+
Requires-Dist: langgraph>=0.2; extra == "langgraph"
|
|
21
|
+
Provides-Extra: crewai
|
|
22
|
+
Requires-Dist: crewai>=0.70; extra == "crewai"
|
|
23
|
+
Provides-Extra: autogen
|
|
24
|
+
Requires-Dist: autogen-core>=0.4; extra == "autogen"
|
|
25
|
+
Provides-Extra: openai-agents
|
|
26
|
+
Requires-Dist: openai-agents>=0.0.1; extra == "openai-agents"
|
|
27
|
+
Provides-Extra: litellm
|
|
28
|
+
Requires-Dist: litellm>=1.40; extra == "litellm"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# homestead-memory
|
|
32
|
+
|
|
33
|
+
**Stop renting your mind.**
|
|
34
|
+
|
|
35
|
+
Local-first, verifiable AI memory. Your notes stay plain markdown you can read,
|
|
36
|
+
`git diff`, and own, and the memory **catches its own rot, tampering, and poisoning.**
|
|
37
|
+
|
|
38
|
+
Every other memory layer asks you to *hope* it remembers. This one lets you
|
|
39
|
+
*watch it catch the rot, live:*
|
|
40
|
+
|
|
41
|
+

|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install homestead-memory # macOS / Linux / Windows (pure Python, zero deps)
|
|
45
|
+
|
|
46
|
+
hsm verify --demo
|
|
47
|
+
# ① a clean vault ✅ MEMORY INTACT — 100/100
|
|
48
|
+
# ② rot is planted… 🔴 ROT DETECTED — 0/100
|
|
49
|
+
# 🔴 [self_contradiction] the note argues with itself about its own status
|
|
50
|
+
# 🔴 [uncited_claim] a distilled claim has no source citation
|
|
51
|
+
# 🔴 [dangling_citation] a cited source no longer exists
|
|
52
|
+
# ⚠️ [broken_link] a reference points at a deleted note
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`hsm verify` exits non-zero on rot — it gates CI and cron like a test suite.
|
|
56
|
+
|
|
57
|
+
## Quickstart (60 seconds)
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
hsm init ./my-vault # scaffold or adopt any markdown folder
|
|
61
|
+
hsm ingest ./my-vault # index it (hybrid BM25+vector via qmd, optional)
|
|
62
|
+
hsm ask "what did I decide about X?"
|
|
63
|
+
hsm verify ./my-vault # the integrity gate — the whole point
|
|
64
|
+
hsm distill ./my-vault # optional: build the cited, verifiable fact layer
|
|
65
|
+
hsm history <note> --as-of 2026-06-01 # what was true THEN (temporal layer)
|
|
66
|
+
hsm serve # local HTTP API (auth'd, loopback-only)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Python agents can use the SDK directly:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from homestead_memory import connect
|
|
73
|
+
|
|
74
|
+
memory = connect("~/my-vault", agent="my-agent")
|
|
75
|
+
memory.remember("user", "city", "Berlin")
|
|
76
|
+
memory.ask("what city is the user in?")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The local HTTP API is documented in [`docs/openapi.yaml`](docs/openapi.yaml).
|
|
80
|
+
|
|
81
|
+
## Memory under the router
|
|
82
|
+
|
|
83
|
+
Routers can swap the served model while homestead-memory keeps the same vault
|
|
84
|
+
underneath. The model name is just the runtime argument; provenance is stamped as
|
|
85
|
+
`name@model` in the `agent` field when a write happens.
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from homestead_memory import connect
|
|
89
|
+
from homestead_memory.adapters.openai_compat import MemoryChat
|
|
90
|
+
|
|
91
|
+
memory = connect("~/my-vault")
|
|
92
|
+
|
|
93
|
+
def remember_reply(response, memory, agent):
|
|
94
|
+
memory.remember(
|
|
95
|
+
"conversation",
|
|
96
|
+
"last_reply",
|
|
97
|
+
response.choices[0].message.content,
|
|
98
|
+
source="chat",
|
|
99
|
+
agent=agent,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
chat = MemoryChat(openai_compatible_client, memory, remember_fn=remember_reply)
|
|
103
|
+
chat.create(model="claude-sonnet-4.7", messages=[{"role": "user", "content": "brief me"}])
|
|
104
|
+
chat.create(model="glm-4.7", messages=[{"role": "user", "content": "continue"}])
|
|
105
|
+
|
|
106
|
+
memory.history("conversation") # agents include assistant@claude-sonnet-4.7 and assistant@glm-4.7
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
LiteLLM can use the same pattern with a pre-call injection helper and a success
|
|
110
|
+
logger:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from homestead_memory import connect
|
|
114
|
+
from homestead_memory.adapters.litellm_memory import MemoryLogger, inject_memory
|
|
115
|
+
|
|
116
|
+
memory = connect("~/my-vault")
|
|
117
|
+
messages = inject_memory([{"role": "user", "content": "brief me"}], memory)
|
|
118
|
+
|
|
119
|
+
# LiteLLM callback registration style depends on your app setup.
|
|
120
|
+
logger = MemoryLogger(memory, agent_name="assistant")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
MCP already sits above harness-level routers. In a `claude-code-router`-style
|
|
124
|
+
setup that swaps the backend model, homestead-memory keeps working with
|
|
125
|
+
zero config because memory is external to the model. `history()` and `verify`
|
|
126
|
+
then attribute every recorded fact to the exact `name@model` that wrote it.
|
|
127
|
+
|
|
128
|
+
## Integrations
|
|
129
|
+
|
|
130
|
+
Adapters target the public framework interfaces listed here as of the current
|
|
131
|
+
releases and may need version bumps as those APIs evolve. Core remains
|
|
132
|
+
stdlib-only; install only the extra for the framework you use.
|
|
133
|
+
|
|
134
|
+
Universal tools work with any orchestrator that can register callables or
|
|
135
|
+
JSON-schema function tools:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from homestead_memory import connect
|
|
139
|
+
from homestead_memory.adapters.tools import recall_tool, remember_tool, tool_specs, verify_tool
|
|
140
|
+
|
|
141
|
+
memory = connect("~/my-vault", agent="my-agent")
|
|
142
|
+
tools = [remember_tool(memory), recall_tool(memory), verify_tool(memory)]
|
|
143
|
+
specs = tool_specs(memory) # name, description, parameters
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
LangGraph `BaseStore` (targets `langgraph>=0.2`):
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from homestead_memory import connect
|
|
150
|
+
from homestead_memory.adapters.langgraph_store import HomesteadStore
|
|
151
|
+
|
|
152
|
+
store = HomesteadStore(connect("~/my-vault", agent="langgraph"))
|
|
153
|
+
graph = builder.compile(checkpointer=checkpointer, store=store)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
CrewAI storage/memory (targets `crewai>=0.70`, storage-style
|
|
157
|
+
`save/search/reset`):
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from homestead_memory import connect
|
|
161
|
+
from homestead_memory.adapters.crewai_memory import HomesteadCrewAIStorage
|
|
162
|
+
|
|
163
|
+
storage = HomesteadCrewAIStorage(connect("~/my-vault", agent="crewai"))
|
|
164
|
+
storage.save("Researcher found the supplier shortlist", metadata={"task": "supplier_shortlist"})
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
AutoGen `autogen_core` Memory protocol (targets `autogen-core>=0.4`):
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from autogen_core.memory import MemoryContent, MemoryMimeType
|
|
171
|
+
from homestead_memory import connect
|
|
172
|
+
from homestead_memory.adapters.autogen_memory import HomesteadAutoGenMemory
|
|
173
|
+
|
|
174
|
+
memory = HomesteadAutoGenMemory(connect("~/my-vault", agent="autogen"))
|
|
175
|
+
await memory.add(MemoryContent(content="Use metric units", mime_type=MemoryMimeType.TEXT))
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
OpenAI Agents SDK Session protocol or function tools (targets
|
|
179
|
+
`openai-agents>=0.0.1`):
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from homestead_memory import connect
|
|
183
|
+
from homestead_memory.adapters.openai_agents import HomesteadSession, function_tools
|
|
184
|
+
|
|
185
|
+
memory = connect("~/my-vault", agent="openai-agents")
|
|
186
|
+
session = HomesteadSession(memory, session_id="user-123")
|
|
187
|
+
agent_tools = function_tools(memory)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Claude Code / Desktop / Cursor** (MCP):
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
claude mcp add homestead-memory -- hsm mcp ~/my-vault
|
|
194
|
+
# tools: memory_ask · memory_search · memory_verify · memory_history ·
|
|
195
|
+
# memory_ingest · memory_distill
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Why this exists
|
|
199
|
+
|
|
200
|
+
"Runs on your device" is table stakes now — every memory tool stores locally.
|
|
201
|
+
**Nobody verifies.** Memory rots quietly: a note contradicts itself, an extracted
|
|
202
|
+
"fact" loses its source, a body drifts past its own changelog, the current value
|
|
203
|
+
gets shadowed by a stale one. You find out weeks later, when your agent confidently
|
|
204
|
+
tells you something that stopped being true in March.
|
|
205
|
+
|
|
206
|
+
And rot is only the *passive* failure. Memory also gets **tampered** with (a fact
|
|
207
|
+
edited after it was written) and **poisoned** (untrusted input injects a "memory"
|
|
208
|
+
that was never true — a named 2026 attack class). homestead-memory catches all
|
|
209
|
+
three mechanically: sign the vault and any edited byte breaks the signature; a
|
|
210
|
+
distilled claim must cite a source that resolves or it's dropped. Recall benchmarks
|
|
211
|
+
measure whether the model *remembers*; **RotBench measures whether the memory can be
|
|
212
|
+
trusted** — see [`benchmarks/ROTBENCH.md`](benchmarks/ROTBENCH.md).
|
|
213
|
+
|
|
214
|
+
homestead-memory is built around three commitments:
|
|
215
|
+
|
|
216
|
+
1. **Markdown-primary.** The human-readable files ARE the memory. Indexes and
|
|
217
|
+
projections are derived and disposable. You can leave any time — it's your folder.
|
|
218
|
+
Import/export Google's **Open Knowledge Format** (`hsm export --format okf`) plus
|
|
219
|
+
Mem0/Zep: we're OKF, but signed and verifiable.
|
|
220
|
+
2. **Verification over trust.** Integrity is a *number* (RotBench, 0–100), computed
|
|
221
|
+
by mechanical checks — no LLM judging its own homework. See
|
|
222
|
+
[`benchmarks/ROTBENCH.md`](benchmarks/ROTBENCH.md).
|
|
223
|
+
3. **Auditable extraction.** The optional distilled layer ([`docs/DISTILL_SPEC.md`](docs/DISTILL_SPEC.md))
|
|
224
|
+
extracts entity facts *with verbatim quotes, checked in code* — a claim either
|
|
225
|
+
cites a real source or it's dropped. Contradictions append a changelog line
|
|
226
|
+
(`update current_crm: "Salesforce" -> "HubSpot" (source: chat-042.md)`) — never a
|
|
227
|
+
silent overwrite. Extraction you can audit is extraction you can trust.
|
|
228
|
+
|
|
229
|
+
## The two camps (where this sits)
|
|
230
|
+
|
|
231
|
+
| | extraction camp (Mem0, Zep) | verbatim camp (MemPalace, **this**) |
|
|
232
|
+
|---|---|---|
|
|
233
|
+
| write cost | LLM call per turn/episode | **$0** (embed only; distill optional) |
|
|
234
|
+
| information | lossy summaries | **lossless** raw text |
|
|
235
|
+
| auditability | trust the extractor | **cite-or-drop, checked mechanically** |
|
|
236
|
+
| integrity score | — | **RotBench, published every run** |
|
|
237
|
+
|
|
238
|
+
## Honest numbers (LongMemEval)
|
|
239
|
+
|
|
240
|
+
Measured on the full 500-question `_s` set (48-session haystacks with distractors),
|
|
241
|
+
scored with the **official per-type judge methodology**, reader `glm-5.2`,
|
|
242
|
+
independent judge `deepseek-v4-pro`. Reproduce: [`benchmarks/README.md`](benchmarks/README.md).
|
|
243
|
+
Full run history including the failures: [`benchmarks/RESULTS.md`](benchmarks/RESULTS.md).
|
|
244
|
+
|
|
245
|
+
| metric | value |
|
|
246
|
+
|---|---|
|
|
247
|
+
| retrieval recall@k | **85%** (evidence surfaced into top-k) |
|
|
248
|
+
| QA accuracy (official methodology) | **52.8%** |
|
|
249
|
+
| context tokens / query | **~5.2k** |
|
|
250
|
+
| RotBench | **99.4 / 100** |
|
|
251
|
+
|
|
252
|
+
What we will and won't claim: recall is elite and *reader-independent*; QA is honest
|
|
253
|
+
and mid — published systems self-report higher on their own harnesses (Mem0 94.4%,
|
|
254
|
+
Zep 63.8% independent); we publish the harness, the judge, and every failed
|
|
255
|
+
experiment instead. No number here is from a harness you can't run yourself.
|
|
256
|
+
|
|
257
|
+
## Design
|
|
258
|
+
|
|
259
|
+
- **Cross-platform.** Pure Python, stdlib-only core. CI: ubuntu / macos / windows.
|
|
260
|
+
- **Degrades gracefully.** qmd (hybrid retrieval) is an optional dependency; without
|
|
261
|
+
it, retrieval falls back to a direct scan. Memory survives its index being down —
|
|
262
|
+
`verify --deep` *tests* that.
|
|
263
|
+
- **Local by default.** The HTTP API binds loopback with bearer auth + DNS-rebind
|
|
264
|
+
protection; the MCP server is stdio (client-spawned). Nothing phones home.
|
|
265
|
+
- **Temporal.** Changelog lines make history queryable: `hsm history note --as-of DATE`.
|
|
266
|
+
|
|
267
|
+
## Status
|
|
268
|
+
|
|
269
|
+
v0.2, building in public. Roadmap: [`ROADMAP.md`](ROADMAP.md). Break our benchmark:
|
|
270
|
+
[`benchmarks/ROTBENCH.md`](benchmarks/ROTBENCH.md) — adversarial fixtures get merged.
|
|
271
|
+
|
|
272
|
+
MIT © Kinetic Labs Inc. · a [FuckBigTech](https://fuckbigtech.ai) / HOMESTEAD project.
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# homestead-memory
|
|
2
|
+
|
|
3
|
+
**Stop renting your mind.**
|
|
4
|
+
|
|
5
|
+
Local-first, verifiable AI memory. Your notes stay plain markdown you can read,
|
|
6
|
+
`git diff`, and own, and the memory **catches its own rot, tampering, and poisoning.**
|
|
7
|
+
|
|
8
|
+
Every other memory layer asks you to *hope* it remembers. This one lets you
|
|
9
|
+
*watch it catch the rot, live:*
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install homestead-memory # macOS / Linux / Windows (pure Python, zero deps)
|
|
15
|
+
|
|
16
|
+
hsm verify --demo
|
|
17
|
+
# ① a clean vault ✅ MEMORY INTACT — 100/100
|
|
18
|
+
# ② rot is planted… 🔴 ROT DETECTED — 0/100
|
|
19
|
+
# 🔴 [self_contradiction] the note argues with itself about its own status
|
|
20
|
+
# 🔴 [uncited_claim] a distilled claim has no source citation
|
|
21
|
+
# 🔴 [dangling_citation] a cited source no longer exists
|
|
22
|
+
# ⚠️ [broken_link] a reference points at a deleted note
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`hsm verify` exits non-zero on rot — it gates CI and cron like a test suite.
|
|
26
|
+
|
|
27
|
+
## Quickstart (60 seconds)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
hsm init ./my-vault # scaffold or adopt any markdown folder
|
|
31
|
+
hsm ingest ./my-vault # index it (hybrid BM25+vector via qmd, optional)
|
|
32
|
+
hsm ask "what did I decide about X?"
|
|
33
|
+
hsm verify ./my-vault # the integrity gate — the whole point
|
|
34
|
+
hsm distill ./my-vault # optional: build the cited, verifiable fact layer
|
|
35
|
+
hsm history <note> --as-of 2026-06-01 # what was true THEN (temporal layer)
|
|
36
|
+
hsm serve # local HTTP API (auth'd, loopback-only)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Python agents can use the SDK directly:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from homestead_memory import connect
|
|
43
|
+
|
|
44
|
+
memory = connect("~/my-vault", agent="my-agent")
|
|
45
|
+
memory.remember("user", "city", "Berlin")
|
|
46
|
+
memory.ask("what city is the user in?")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The local HTTP API is documented in [`docs/openapi.yaml`](docs/openapi.yaml).
|
|
50
|
+
|
|
51
|
+
## Memory under the router
|
|
52
|
+
|
|
53
|
+
Routers can swap the served model while homestead-memory keeps the same vault
|
|
54
|
+
underneath. The model name is just the runtime argument; provenance is stamped as
|
|
55
|
+
`name@model` in the `agent` field when a write happens.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from homestead_memory import connect
|
|
59
|
+
from homestead_memory.adapters.openai_compat import MemoryChat
|
|
60
|
+
|
|
61
|
+
memory = connect("~/my-vault")
|
|
62
|
+
|
|
63
|
+
def remember_reply(response, memory, agent):
|
|
64
|
+
memory.remember(
|
|
65
|
+
"conversation",
|
|
66
|
+
"last_reply",
|
|
67
|
+
response.choices[0].message.content,
|
|
68
|
+
source="chat",
|
|
69
|
+
agent=agent,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
chat = MemoryChat(openai_compatible_client, memory, remember_fn=remember_reply)
|
|
73
|
+
chat.create(model="claude-sonnet-4.7", messages=[{"role": "user", "content": "brief me"}])
|
|
74
|
+
chat.create(model="glm-4.7", messages=[{"role": "user", "content": "continue"}])
|
|
75
|
+
|
|
76
|
+
memory.history("conversation") # agents include assistant@claude-sonnet-4.7 and assistant@glm-4.7
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
LiteLLM can use the same pattern with a pre-call injection helper and a success
|
|
80
|
+
logger:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from homestead_memory import connect
|
|
84
|
+
from homestead_memory.adapters.litellm_memory import MemoryLogger, inject_memory
|
|
85
|
+
|
|
86
|
+
memory = connect("~/my-vault")
|
|
87
|
+
messages = inject_memory([{"role": "user", "content": "brief me"}], memory)
|
|
88
|
+
|
|
89
|
+
# LiteLLM callback registration style depends on your app setup.
|
|
90
|
+
logger = MemoryLogger(memory, agent_name="assistant")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
MCP already sits above harness-level routers. In a `claude-code-router`-style
|
|
94
|
+
setup that swaps the backend model, homestead-memory keeps working with
|
|
95
|
+
zero config because memory is external to the model. `history()` and `verify`
|
|
96
|
+
then attribute every recorded fact to the exact `name@model` that wrote it.
|
|
97
|
+
|
|
98
|
+
## Integrations
|
|
99
|
+
|
|
100
|
+
Adapters target the public framework interfaces listed here as of the current
|
|
101
|
+
releases and may need version bumps as those APIs evolve. Core remains
|
|
102
|
+
stdlib-only; install only the extra for the framework you use.
|
|
103
|
+
|
|
104
|
+
Universal tools work with any orchestrator that can register callables or
|
|
105
|
+
JSON-schema function tools:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from homestead_memory import connect
|
|
109
|
+
from homestead_memory.adapters.tools import recall_tool, remember_tool, tool_specs, verify_tool
|
|
110
|
+
|
|
111
|
+
memory = connect("~/my-vault", agent="my-agent")
|
|
112
|
+
tools = [remember_tool(memory), recall_tool(memory), verify_tool(memory)]
|
|
113
|
+
specs = tool_specs(memory) # name, description, parameters
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
LangGraph `BaseStore` (targets `langgraph>=0.2`):
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from homestead_memory import connect
|
|
120
|
+
from homestead_memory.adapters.langgraph_store import HomesteadStore
|
|
121
|
+
|
|
122
|
+
store = HomesteadStore(connect("~/my-vault", agent="langgraph"))
|
|
123
|
+
graph = builder.compile(checkpointer=checkpointer, store=store)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
CrewAI storage/memory (targets `crewai>=0.70`, storage-style
|
|
127
|
+
`save/search/reset`):
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from homestead_memory import connect
|
|
131
|
+
from homestead_memory.adapters.crewai_memory import HomesteadCrewAIStorage
|
|
132
|
+
|
|
133
|
+
storage = HomesteadCrewAIStorage(connect("~/my-vault", agent="crewai"))
|
|
134
|
+
storage.save("Researcher found the supplier shortlist", metadata={"task": "supplier_shortlist"})
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
AutoGen `autogen_core` Memory protocol (targets `autogen-core>=0.4`):
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from autogen_core.memory import MemoryContent, MemoryMimeType
|
|
141
|
+
from homestead_memory import connect
|
|
142
|
+
from homestead_memory.adapters.autogen_memory import HomesteadAutoGenMemory
|
|
143
|
+
|
|
144
|
+
memory = HomesteadAutoGenMemory(connect("~/my-vault", agent="autogen"))
|
|
145
|
+
await memory.add(MemoryContent(content="Use metric units", mime_type=MemoryMimeType.TEXT))
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
OpenAI Agents SDK Session protocol or function tools (targets
|
|
149
|
+
`openai-agents>=0.0.1`):
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from homestead_memory import connect
|
|
153
|
+
from homestead_memory.adapters.openai_agents import HomesteadSession, function_tools
|
|
154
|
+
|
|
155
|
+
memory = connect("~/my-vault", agent="openai-agents")
|
|
156
|
+
session = HomesteadSession(memory, session_id="user-123")
|
|
157
|
+
agent_tools = function_tools(memory)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Claude Code / Desktop / Cursor** (MCP):
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
claude mcp add homestead-memory -- hsm mcp ~/my-vault
|
|
164
|
+
# tools: memory_ask · memory_search · memory_verify · memory_history ·
|
|
165
|
+
# memory_ingest · memory_distill
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Why this exists
|
|
169
|
+
|
|
170
|
+
"Runs on your device" is table stakes now — every memory tool stores locally.
|
|
171
|
+
**Nobody verifies.** Memory rots quietly: a note contradicts itself, an extracted
|
|
172
|
+
"fact" loses its source, a body drifts past its own changelog, the current value
|
|
173
|
+
gets shadowed by a stale one. You find out weeks later, when your agent confidently
|
|
174
|
+
tells you something that stopped being true in March.
|
|
175
|
+
|
|
176
|
+
And rot is only the *passive* failure. Memory also gets **tampered** with (a fact
|
|
177
|
+
edited after it was written) and **poisoned** (untrusted input injects a "memory"
|
|
178
|
+
that was never true — a named 2026 attack class). homestead-memory catches all
|
|
179
|
+
three mechanically: sign the vault and any edited byte breaks the signature; a
|
|
180
|
+
distilled claim must cite a source that resolves or it's dropped. Recall benchmarks
|
|
181
|
+
measure whether the model *remembers*; **RotBench measures whether the memory can be
|
|
182
|
+
trusted** — see [`benchmarks/ROTBENCH.md`](benchmarks/ROTBENCH.md).
|
|
183
|
+
|
|
184
|
+
homestead-memory is built around three commitments:
|
|
185
|
+
|
|
186
|
+
1. **Markdown-primary.** The human-readable files ARE the memory. Indexes and
|
|
187
|
+
projections are derived and disposable. You can leave any time — it's your folder.
|
|
188
|
+
Import/export Google's **Open Knowledge Format** (`hsm export --format okf`) plus
|
|
189
|
+
Mem0/Zep: we're OKF, but signed and verifiable.
|
|
190
|
+
2. **Verification over trust.** Integrity is a *number* (RotBench, 0–100), computed
|
|
191
|
+
by mechanical checks — no LLM judging its own homework. See
|
|
192
|
+
[`benchmarks/ROTBENCH.md`](benchmarks/ROTBENCH.md).
|
|
193
|
+
3. **Auditable extraction.** The optional distilled layer ([`docs/DISTILL_SPEC.md`](docs/DISTILL_SPEC.md))
|
|
194
|
+
extracts entity facts *with verbatim quotes, checked in code* — a claim either
|
|
195
|
+
cites a real source or it's dropped. Contradictions append a changelog line
|
|
196
|
+
(`update current_crm: "Salesforce" -> "HubSpot" (source: chat-042.md)`) — never a
|
|
197
|
+
silent overwrite. Extraction you can audit is extraction you can trust.
|
|
198
|
+
|
|
199
|
+
## The two camps (where this sits)
|
|
200
|
+
|
|
201
|
+
| | extraction camp (Mem0, Zep) | verbatim camp (MemPalace, **this**) |
|
|
202
|
+
|---|---|---|
|
|
203
|
+
| write cost | LLM call per turn/episode | **$0** (embed only; distill optional) |
|
|
204
|
+
| information | lossy summaries | **lossless** raw text |
|
|
205
|
+
| auditability | trust the extractor | **cite-or-drop, checked mechanically** |
|
|
206
|
+
| integrity score | — | **RotBench, published every run** |
|
|
207
|
+
|
|
208
|
+
## Honest numbers (LongMemEval)
|
|
209
|
+
|
|
210
|
+
Measured on the full 500-question `_s` set (48-session haystacks with distractors),
|
|
211
|
+
scored with the **official per-type judge methodology**, reader `glm-5.2`,
|
|
212
|
+
independent judge `deepseek-v4-pro`. Reproduce: [`benchmarks/README.md`](benchmarks/README.md).
|
|
213
|
+
Full run history including the failures: [`benchmarks/RESULTS.md`](benchmarks/RESULTS.md).
|
|
214
|
+
|
|
215
|
+
| metric | value |
|
|
216
|
+
|---|---|
|
|
217
|
+
| retrieval recall@k | **85%** (evidence surfaced into top-k) |
|
|
218
|
+
| QA accuracy (official methodology) | **52.8%** |
|
|
219
|
+
| context tokens / query | **~5.2k** |
|
|
220
|
+
| RotBench | **99.4 / 100** |
|
|
221
|
+
|
|
222
|
+
What we will and won't claim: recall is elite and *reader-independent*; QA is honest
|
|
223
|
+
and mid — published systems self-report higher on their own harnesses (Mem0 94.4%,
|
|
224
|
+
Zep 63.8% independent); we publish the harness, the judge, and every failed
|
|
225
|
+
experiment instead. No number here is from a harness you can't run yourself.
|
|
226
|
+
|
|
227
|
+
## Design
|
|
228
|
+
|
|
229
|
+
- **Cross-platform.** Pure Python, stdlib-only core. CI: ubuntu / macos / windows.
|
|
230
|
+
- **Degrades gracefully.** qmd (hybrid retrieval) is an optional dependency; without
|
|
231
|
+
it, retrieval falls back to a direct scan. Memory survives its index being down —
|
|
232
|
+
`verify --deep` *tests* that.
|
|
233
|
+
- **Local by default.** The HTTP API binds loopback with bearer auth + DNS-rebind
|
|
234
|
+
protection; the MCP server is stdio (client-spawned). Nothing phones home.
|
|
235
|
+
- **Temporal.** Changelog lines make history queryable: `hsm history note --as-of DATE`.
|
|
236
|
+
|
|
237
|
+
## Status
|
|
238
|
+
|
|
239
|
+
v0.2, building in public. Roadmap: [`ROADMAP.md`](ROADMAP.md). Break our benchmark:
|
|
240
|
+
[`benchmarks/ROTBENCH.md`](benchmarks/ROTBENCH.md) — adversarial fixtures get merged.
|
|
241
|
+
|
|
242
|
+
MIT © Kinetic Labs Inc. · a [FuckBigTech](https://fuckbigtech.ai) / HOMESTEAD project.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "homestead-memory"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Verifiable, local-first AI memory that catches rot, tampering, and poisoning. Plain markdown you own."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "Kinetic Labs" }]
|
|
13
|
+
keywords = ["ai", "memory", "local-first", "agents", "verification", "rag", "homestead"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
# Core is stdlib-only. qmd (hybrid retrieval) is an optional external CLI dependency,
|
|
20
|
+
# invoked as a subprocess; the store degrades to a direct markdown scan without it.
|
|
21
|
+
dependencies = []
|
|
22
|
+
|
|
23
|
+
[project.optional-dependencies]
|
|
24
|
+
api = ["fastapi>=0.110", "uvicorn>=0.27"]
|
|
25
|
+
sign = ["cryptography>=42"]
|
|
26
|
+
langgraph = ["langgraph>=0.2"]
|
|
27
|
+
crewai = ["crewai>=0.70"]
|
|
28
|
+
autogen = ["autogen-core>=0.4"]
|
|
29
|
+
openai-agents = ["openai-agents>=0.0.1"]
|
|
30
|
+
litellm = ["litellm>=1.40"]
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
hsm = "homestead_memory.cli:main"
|
|
34
|
+
fbt = "homestead_memory.cli:main" # legacy alias, removed before 1.0
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.packages.find]
|
|
37
|
+
where = ["src"]
|
|
38
|
+
|
|
39
|
+
[tool.pytest.ini_options]
|
|
40
|
+
# The example scripts under examples/ are imported by their tests
|
|
41
|
+
# (`from examples import hot_swap_demo`). Put the repo root on sys.path so the
|
|
42
|
+
# bare `pytest` console script (as CI runs it) behaves like `python -m pytest`.
|
|
43
|
+
pythonpath = ["."]
|
|
44
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Framework adapters for plugging homestead-memory into agent runtimes."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"tools",
|
|
6
|
+
"langgraph_store",
|
|
7
|
+
"crewai_memory",
|
|
8
|
+
"autogen_memory",
|
|
9
|
+
"openai_agents",
|
|
10
|
+
"openai_compat",
|
|
11
|
+
"litellm_memory",
|
|
12
|
+
"okf",
|
|
13
|
+
]
|