echo-mem 0.3.0__tar.gz → 0.4.1__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.
- echo_mem-0.4.1/PKG-INFO +315 -0
- echo_mem-0.4.1/README.md +293 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/pyproject.toml +1 -1
- echo_mem-0.4.1/src/echo_mem.egg-info/PKG-INFO +315 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_mem.egg-info/SOURCES.txt +9 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/audit/get_audit_log.py +16 -2
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/benchmark.py +67 -6
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/health.py +185 -0
- echo_mem-0.4.1/src/echo_memory/cli/judge.py +250 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/main.py +134 -4
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/queue.py +2 -1
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/quickstart.py +125 -5
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/recall.py +93 -6
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/reindex.py +26 -8
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/stop_gate.py +21 -5
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/trial.py +44 -3
- echo_mem-0.4.1/src/echo_memory/eval/independent.py +683 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/eval/retrieval.py +251 -15
- echo_mem-0.4.1/src/echo_memory/eval/sweep.py +227 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/ingestion/capture.py +11 -3
- echo_mem-0.4.1/src/echo_memory/ingestion/embeddings.py +173 -0
- echo_mem-0.4.1/src/echo_memory/ingestion/neighbourhood.py +194 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/ingestion/resolution.py +87 -26
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/ingestion/write_episode.py +80 -13
- echo_mem-0.4.1/src/echo_memory/migrations/versions/0020_who_closed_a_queued_document.py +44 -0
- echo_mem-0.4.1/src/echo_memory/migrations/versions/0021_delivery_evidence.py +56 -0
- echo_mem-0.4.1/src/echo_memory/migrations/versions/0022_independent_evaluation.py +100 -0
- echo_mem-0.4.1/src/echo_memory/migrations/versions/0023_second_judge.py +61 -0
- echo_mem-0.4.1/src/echo_memory/migrations/versions/0024_node_by_id.py +46 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/retrieval/query_memory.py +193 -17
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/server.py +52 -11
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/trial/check.py +54 -3
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/trial/observations.py +19 -3
- echo_mem-0.4.1/src/echo_memory/trial/reads.py +240 -0
- echo_mem-0.3.0/PKG-INFO +0 -253
- echo_mem-0.3.0/README.md +0 -231
- echo_mem-0.3.0/src/echo_mem.egg-info/PKG-INFO +0 -253
- echo_mem-0.3.0/src/echo_memory/ingestion/embeddings.py +0 -65
- echo_mem-0.3.0/src/echo_memory/trial/reads.py +0 -121
- {echo_mem-0.3.0 → echo_mem-0.4.1}/LICENSE +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/setup.cfg +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_mem.egg-info/dependency_links.txt +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_mem.egg-info/entry_points.txt +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_mem.egg-info/requires.txt +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_mem.egg-info/top_level.txt +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/__init__.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/audit/__init__.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/__init__.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/adopt.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/analyse.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/calibrate.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/connect.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/dashboard.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/dashboard_html.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/export.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/graph.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/hooks.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/initdb.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/install.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/merge.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/reattribute.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/reattribute_cmd.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/reconcile.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/session_start.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/skill.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/status.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/unmerge.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/cli/why.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/client.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/eval/__init__.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/graph/__init__.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/graph/communities.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/infra/__init__.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/infra/config.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/infra/db.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/infra/logging.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/infra/pool.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/infra/project.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/ingestion/__init__.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/ingestion/activity.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/ingestion/bootstrap.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/env.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0001_initial_schema.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0002_trial_observations.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0003_project_and_agent_provenance.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0004_bootstrap_state.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0005_recall_save_guards.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0006_project_analysis.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0007_backfill_unknown_agent.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0008_read_events.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0009_read_event_attribution.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0010_stop_gate_fired.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0011_backfill_absent_agent.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0012_read_event_agent.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0013_active_fact_indexes.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0014_session_activity.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0015_retracted_observations.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0016_one_node_per_name.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0017_writer_version.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0018_trial_runs_are_history.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/migrations/versions/0019_versions_expected_since.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/retrieval/__init__.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/retrieval/fusion.py +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/skill/SKILL.md +0 -0
- {echo_mem-0.3.0 → echo_mem-0.4.1}/src/echo_memory/trial/__init__.py +0 -0
echo_mem-0.4.1/PKG-INFO
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: echo-mem
|
|
3
|
+
Version: 0.4.1
|
|
4
|
+
Summary: A long-horizon memory architecture for AI agents
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: psycopg[binary]<4,>=3.2
|
|
10
|
+
Requires-Dist: psycopg-pool<4,>=3.2
|
|
11
|
+
Requires-Dist: pgvector<1,>=0.3
|
|
12
|
+
Requires-Dist: alembic<2,>=1.13
|
|
13
|
+
Requires-Dist: sqlalchemy<3,>=2
|
|
14
|
+
Requires-Dist: mcp<3,>=2.0
|
|
15
|
+
Requires-Dist: sentence-transformers<4,>=3
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest<9,>=8; extra == "dev"
|
|
18
|
+
Requires-Dist: ruff<1,>=0.6; extra == "dev"
|
|
19
|
+
Requires-Dist: build<2,>=1; extra == "dev"
|
|
20
|
+
Requires-Dist: twine<7,>=5; extra == "dev"
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# Echo Memory
|
|
24
|
+
|
|
25
|
+
[](https://github.com/echo-mem/echo-mem/actions/workflows/ci.yml)
|
|
26
|
+
[](https://pypi.org/project/echo-mem/)
|
|
27
|
+
[](https://pypi.org/project/echo-mem/)
|
|
28
|
+
[](LICENSE)
|
|
29
|
+
|
|
30
|
+
**Shared memory for AI agents, as a graph in your own database.** What Claude Code learns,
|
|
31
|
+
Cursor and Codex can recall. Every fact records who wrote it and when, and the server
|
|
32
|
+
never calls a model to store one.
|
|
33
|
+
|
|
34
|
+
> Your agents start every session from zero. The usual fix is a notes file you paste into
|
|
35
|
+
> context, which grows until it is mostly irrelevant to whatever you are asking. Echo
|
|
36
|
+
> Memory is the other shape: facts connected to each other, and a query that returns the
|
|
37
|
+
> few that matter. On the author's own store that is **96.7% less context** for the same
|
|
38
|
+
> answer, with the answer still present 87.2% of the time across 1,190 questions.
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
write_episode query_memory
|
|
42
|
+
billing ──uses──▸ Razorpay "how do we take payments"
|
|
43
|
+
│ written by claude-code ▸ billing uses Razorpay, not Stripe
|
|
44
|
+
│ supersedes ──▸ Stripe written by claude-code, 3 days ago
|
|
45
|
+
└ no model invoked ▸ 1,372 tokens, not 41,838
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
**Requires:** Python 3.11+ and Docker (for the database).
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pipx install echo-mem
|
|
54
|
+
echo-memory quickstart
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`quickstart` starts the database, applies the schema, and prints the `claude mcp add`
|
|
58
|
+
line that registers it, filled in with the port it actually used. The Postgres image is
|
|
59
|
+
published, so nothing compiles.
|
|
60
|
+
|
|
61
|
+
Or use the hosted service and run no database at all:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pipx install echo-mem
|
|
65
|
+
echo-memory connect <key> # a key from https://app.echo-mem.com
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Then once per machine, so an agent knows *when* to record and recall rather than only
|
|
69
|
+
that the tools exist:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
echo-memory install --global
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
> **Restart your client afterwards.** An MCP server is a long lived process that holds
|
|
76
|
+
> the code and config it started with, and an editable install does not change that.
|
|
77
|
+
|
|
78
|
+
> **The PyPI name is `echo-mem`, not `echo-memory`.** That name belongs to an unrelated
|
|
79
|
+
> hosted product. The import package and the CLI are both `echo_memory` / `echo-memory`;
|
|
80
|
+
> only the distribution name differs.
|
|
81
|
+
|
|
82
|
+
## Usage
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
echo-memory status # what each scope holds, and which agents have written
|
|
86
|
+
echo-memory health # a score, what is weak, and what to do about it
|
|
87
|
+
echo-memory dashboard --serve # the graph, in a browser, localhost only
|
|
88
|
+
|
|
89
|
+
echo-memory why <fact_id> # the full audit trail for one fact
|
|
90
|
+
echo-memory recall "<question>" # query the store from a terminal
|
|
91
|
+
echo-memory export # everything, as JSON
|
|
92
|
+
|
|
93
|
+
echo-memory install --for cursor # wire one client, project scoped
|
|
94
|
+
echo-memory adopt # wire every MCP client on the machine, each with its own id
|
|
95
|
+
|
|
96
|
+
echo-memory eval # retrieval quality against your own store
|
|
97
|
+
echo-memory eval --context # what a recall costs against injecting everything
|
|
98
|
+
echo-memory eval --context --sweep # the same, as a curve across corpus size
|
|
99
|
+
echo-memory calibrate # is entity resolution trustworthy on your data
|
|
100
|
+
echo-memory benchmark # write, query and digest latency
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### The six MCP tools
|
|
104
|
+
|
|
105
|
+
| Tool | What it does |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `write_episode` | Store entities and the facts connecting them. No model call. |
|
|
108
|
+
| `query_memory` | Hybrid vector and full text retrieval, fused by reciprocal rank. |
|
|
109
|
+
| `record_recall_save` | Mark that a recalled fact saved re explaining something. Refuses a fact no read returned. |
|
|
110
|
+
| `get_audit_log` | Every change to memory, with a plain language reason. |
|
|
111
|
+
| `pending_documents` | Memory files this project wrote that the graph has not heard about. |
|
|
112
|
+
| `mark_ingested` | Close one of those out. |
|
|
113
|
+
|
|
114
|
+
## What you get
|
|
115
|
+
|
|
116
|
+
**A graph, not a list.** Entities are nodes and a fact is an edge between two of them.
|
|
117
|
+
Two sessions that never knew about each other resolve onto the same entity by name, so
|
|
118
|
+
the second inherits what the first learned.
|
|
119
|
+
|
|
120
|
+
**Bounded retrieval.** Old, rarely read memory consolidates into higher level summaries
|
|
121
|
+
over time. Nothing is discarded; what changes is how much a query has to walk.
|
|
122
|
+
|
|
123
|
+
**Provenance on every fact.** Who wrote it, which tool, which project, when, and which
|
|
124
|
+
reads returned it. A superseded fact is never deleted. It stops being drawn and stays
|
|
125
|
+
reachable with its history.
|
|
126
|
+
|
|
127
|
+
**Causal typing.** Edges can be tagged `caused_by`, `led_to`, `blocked_by`,
|
|
128
|
+
`contradicts`, set by the agent's own read of the conversation rather than inferred
|
|
129
|
+
statistically.
|
|
130
|
+
|
|
131
|
+
**No inference on the write path.** Extraction happens in the calling agent, so storing
|
|
132
|
+
a memory invokes no model on the server. The cost moved rather than vanished: the agent
|
|
133
|
+
has to arrive with entities and facts already extracted, which is what the
|
|
134
|
+
[tool contract](docs/DEVELOPMENT.md) spells out. The comparison that makes this matter is
|
|
135
|
+
Zep/Graphiti, the closest architectural match, whose own description of ingestion is that
|
|
136
|
+
"every episode triggers multiple LLM calls" and that "write cost scales with volume".
|
|
137
|
+
|
|
138
|
+
The obvious reply is that cheap writes are cheap because they do less, and that reply is
|
|
139
|
+
correct on the mechanism. [`docs/WRITE-COST.md`](docs/WRITE-COST.md) answers it properly,
|
|
140
|
+
including the two measured costs of the choice: the Stop gate fired seven times and
|
|
141
|
+
produced one fact, and a write touching an ambiguous entity is deferred while the call
|
|
142
|
+
returns as though it succeeded.
|
|
143
|
+
|
|
144
|
+
**Any MCP client.** A coding assistant, a chatbot, an ops agent, or something built in
|
|
145
|
+
house. Coding agents are where this is proven, not what it is limited to.
|
|
146
|
+
|
|
147
|
+
## Numbers, and how they were taken
|
|
148
|
+
|
|
149
|
+
Every figure comes from this repository or a live store, on a date, with the command that
|
|
150
|
+
reproduces it on yours. The corpus is small and the noise floor is stated, because a
|
|
151
|
+
difference nobody sized is not a result.
|
|
152
|
+
|
|
153
|
+
| Measure | Value | Reproduce |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| Context per recall vs injecting everything | **96.7% less**, hit@10 0.872 over 1,190 questions | `echo-memory eval --context` |
|
|
156
|
+
| The same saving across 8x of corpus growth | 75.5% at 32 facts rising to **96.4% at 261**, hit@10 0.900 to 0.946 | `echo-memory eval --context --sweep` |
|
|
157
|
+
| LoCoMo retrieval, 1,982 questions, 5,882 turns | recall@10 **0.601**, hit@10 0.658, MRR 0.460 | `scripts/locomo-bench.py` |
|
|
158
|
+
| LongMemEval retrieval, 90 questions, 15 per type | session@10 **0.940**, turn@10 0.727 | `scripts/longmemeval-bench.py --per-type 15` |
|
|
159
|
+
| Server side model calls per write | **0** | `echo-memory benchmark` |
|
|
160
|
+
| Write, query, digest latency (median) | 15ms, 8ms, 1ms | `echo-memory benchmark` |
|
|
161
|
+
| Entity resolution AUC | 0.666, 95% CI [0.421, 0.881] | `echo-memory calibrate` |
|
|
162
|
+
|
|
163
|
+
That last row is the one that went the wrong way, and it is here on purpose. The interval
|
|
164
|
+
includes chance, so the unattended merge is switched off: at the automatic bar precision
|
|
165
|
+
was 50% over two reviewed pairs, and the audit log showed that path had fired once in the
|
|
166
|
+
system's entire history. A near match is now offered for confirmation instead.
|
|
167
|
+
|
|
168
|
+
The LoCoMo row is retrieval, not QA accuracy. Published LoCoMo results have a model write
|
|
169
|
+
an answer and a second model judge it; this asks only whether the turn holding the answer
|
|
170
|
+
came back, which is a ceiling on QA accuracy rather than a substitute for it, and is not
|
|
171
|
+
comparable to anybody's published QA figure. It also feeds raw dialogue turns, which skips
|
|
172
|
+
the extraction step this design pushes to the calling agent, so it is a floor as well as a
|
|
173
|
+
ceiling. The worst row, multi hop at recall@1 0.099, is in
|
|
174
|
+
[`docs/BENCHMARKS.md`](docs/BENCHMARKS.md) with the rest.
|
|
175
|
+
|
|
176
|
+
The context saving is measured against a specific baseline, stated so it cannot be read
|
|
177
|
+
as more than it is. Not "no memory at all", which is however long a human spends re
|
|
178
|
+
explaining and is unmeasurable. It is the thing people do instead: keep the project's
|
|
179
|
+
notes in one file and paste the whole file. On that store the file is 325 facts, about
|
|
180
|
+
41,838 tokens; a recall returned 1,372 on average. The hit rate belongs beside it, because
|
|
181
|
+
a recall that returned nothing would score 100%.
|
|
182
|
+
|
|
183
|
+
## The graph
|
|
184
|
+
|
|
185
|
+
Memory is a graph, not a list of notes. Entities are nodes; a fact is an **edge** between
|
|
186
|
+
two of them. That is the whole data model, and everything else follows from it.
|
|
187
|
+
|
|
188
|
+

|
|
189
|
+
|
|
190
|
+
Three projects here. `checkout-api`, `mobile-app` and `data-pipeline` were recorded in
|
|
191
|
+
separate sessions and never told about each other, yet the picture already separates them,
|
|
192
|
+
because separation is a property of the edges rather than a label anyone applied.
|
|
193
|
+
|
|
194
|
+
**Clusters come from structure.** Densely connected facts are grouped by label propagation
|
|
195
|
+
over the edges, and each cluster is named after its most connected node. That is why
|
|
196
|
+
`data-pipeline` sits apart: nothing it knows touches payments. It is also why
|
|
197
|
+
`checkout-api` and `mobile-app` share a cluster despite being different codebases. They
|
|
198
|
+
genuinely share an idea, and the graph found it rather than being told.
|
|
199
|
+
|
|
200
|
+
**Components are the stronger claim.** Two nodes in different components have no path
|
|
201
|
+
between them at all, which is the strongest statement this graph can make that two
|
|
202
|
+
memories are unrelated.
|
|
203
|
+
|
|
204
|
+
### Click a node: everything it takes part in
|
|
205
|
+
|
|
206
|
+

|
|
207
|
+
|
|
208
|
+
`idempotency keys` is the largest node here and nobody made it large: seventeen facts from
|
|
209
|
+
several services resolved onto one entity by name. The panel lists every one, with which
|
|
210
|
+
agent wrote it and when.
|
|
211
|
+
|
|
212
|
+
### Click a link: why memory believes it
|
|
213
|
+
|
|
214
|
+

|
|
215
|
+
|
|
216
|
+
Not a tooltip. Who wrote the fact, in which project, when, and how each of its entities
|
|
217
|
+
resolved. `echo-memory why <fact_id>` prints the same trail in a terminal.
|
|
218
|
+
|
|
219
|
+
### Seeing your own
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
echo-memory dashboard --serve --open
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
The images above come from a synthetic dataset (`scripts/demo-seed.py`) rather than a real
|
|
226
|
+
store, for the obvious reason: a real memory graph is full of hostnames, account numbers
|
|
227
|
+
and client names.
|
|
228
|
+
|
|
229
|
+
## Wiring more than one tool
|
|
230
|
+
|
|
231
|
+
**Give each client its own `ECHO_MEMORY_AGENT_ID`.** Cursor should say `cursor`, Claude
|
|
232
|
+
Desktop `claude-desktop`. Memory is shared either way, but a fact records which tool
|
|
233
|
+
learned it, and two tools claiming the same id makes cross tool recall impossible to see
|
|
234
|
+
afterwards.
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
echo-memory adopt # every MCP client on the machine, each with its own id
|
|
238
|
+
echo-memory install [path] # one project: MCP config plus a skill, committed with the code
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
`adopt` shows the diff before writing anything. For an agent that does not speak MCP, see
|
|
242
|
+
[`docs/INTEGRATIONS.md`](docs/INTEGRATIONS.md).
|
|
243
|
+
|
|
244
|
+
## Is the graph in good shape?
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
echo-memory health
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
A score, what is strong, what needs attention, and what to do about each, including what
|
|
251
|
+
recall has cost: how often memory was read, how often a read returned anything, roughly
|
|
252
|
+
how many tokens were injected, and how many saves those reads produced. Writes were
|
|
253
|
+
counted from the start; reads were not counted at all, so nothing could answer whether
|
|
254
|
+
recall earns what it costs. It exists to be run when you have no question, because a store
|
|
255
|
+
can look healthy by every other number while most of its facts came from a bulk import,
|
|
256
|
+
the last real write was a week ago, and only one of several wired agents has ever written
|
|
257
|
+
anything. `--json` for machine readable output.
|
|
258
|
+
|
|
259
|
+
Nothing in it is gated. The paid plan sells hosting; diagnostics about your own data are
|
|
260
|
+
not a thing to withhold from the person whose data it is.
|
|
261
|
+
|
|
262
|
+
## Architecture
|
|
263
|
+
|
|
264
|
+
**Storage** PostgreSQL with `pgvector` and Apache AGE, from a single local agent up to an
|
|
265
|
+
organisation wide shared graph, with no forced migration later. The novel work is the
|
|
266
|
+
memory structure and the read/write algorithm on top of it, not a new database engine.
|
|
267
|
+
|
|
268
|
+
**Retrieval** Hybrid vector and full text search fused by reciprocal rank in v1a.
|
|
269
|
+
Personalised PageRank via `networkx` lands in v1b for multi hop associative retrieval.
|
|
270
|
+
|
|
271
|
+
**Interface** [Model Context Protocol](https://modelcontextprotocol.io), so any compliant
|
|
272
|
+
agent reads and writes the same graph.
|
|
273
|
+
|
|
274
|
+
## Status
|
|
275
|
+
|
|
276
|
+
Early and staged, on purpose. See [`docs/designs/`](docs/designs/) for the architecture
|
|
277
|
+
and the v1a to v1b plan.
|
|
278
|
+
|
|
279
|
+
| | |
|
|
280
|
+
|---|---|
|
|
281
|
+
| **v1a, built** | Basic recall. Six MCP tools, thirty CLI commands, on PyPI and in the MCP registry. |
|
|
282
|
+
| **v1b, gated** | Causal typing and multi hop retrieval. 187 questions no single fact answers score MRR 0.212 today; the number to beat exists before the feature does. |
|
|
283
|
+
| **v1.1, planned** | Organisation wide tenancy: per agent, per team, or org wide graphs. |
|
|
284
|
+
|
|
285
|
+
The validated wedge driving v1a is memory shared across coding agents, which is the
|
|
286
|
+
author's own daily pain and the case with the most evidence behind it. Everything else is
|
|
287
|
+
the target this architecture is built toward.
|
|
288
|
+
|
|
289
|
+
## Hosted
|
|
290
|
+
|
|
291
|
+
Running it yourself is free forever under Apache 2.0, with no account and no feature held
|
|
292
|
+
back. [app.echo-mem.com](https://app.echo-mem.com) runs the database for you at $99 a
|
|
293
|
+
month if you would rather not.
|
|
294
|
+
|
|
295
|
+
<details>
|
|
296
|
+
<summary>Contributing</summary>
|
|
297
|
+
|
|
298
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md). Issues and pull requests welcome; please read the
|
|
299
|
+
design docs first so proposals fit the staged build plan. A first pull request is asked to
|
|
300
|
+
sign the [Contributor License Agreement](CLA.md), once, in the PR thread.
|
|
301
|
+
|
|
302
|
+
The most useful contribution is a measurement that disagrees with one of the numbers
|
|
303
|
+
above. Run `echo-memory eval`, `calibrate` or `benchmark` on your own store and open an
|
|
304
|
+
issue with the output.
|
|
305
|
+
|
|
306
|
+
</details>
|
|
307
|
+
|
|
308
|
+
## License
|
|
309
|
+
|
|
310
|
+
Apache License 2.0. See [`LICENSE`](LICENSE).
|
|
311
|
+
|
|
312
|
+
<!-- The MCP registry proves you own a PyPI package by finding this line in the
|
|
313
|
+
package's own description. It has to survive into the built distribution,
|
|
314
|
+
which is why it lives in the README rather than in a workflow. -->
|
|
315
|
+
mcp-name: io.github.ayushcodes10/echo-mem
|
echo_mem-0.4.1/README.md
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# Echo Memory
|
|
2
|
+
|
|
3
|
+
[](https://github.com/echo-mem/echo-mem/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/echo-mem/)
|
|
5
|
+
[](https://pypi.org/project/echo-mem/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
**Shared memory for AI agents, as a graph in your own database.** What Claude Code learns,
|
|
9
|
+
Cursor and Codex can recall. Every fact records who wrote it and when, and the server
|
|
10
|
+
never calls a model to store one.
|
|
11
|
+
|
|
12
|
+
> Your agents start every session from zero. The usual fix is a notes file you paste into
|
|
13
|
+
> context, which grows until it is mostly irrelevant to whatever you are asking. Echo
|
|
14
|
+
> Memory is the other shape: facts connected to each other, and a query that returns the
|
|
15
|
+
> few that matter. On the author's own store that is **96.7% less context** for the same
|
|
16
|
+
> answer, with the answer still present 87.2% of the time across 1,190 questions.
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
write_episode query_memory
|
|
20
|
+
billing ──uses──▸ Razorpay "how do we take payments"
|
|
21
|
+
│ written by claude-code ▸ billing uses Razorpay, not Stripe
|
|
22
|
+
│ supersedes ──▸ Stripe written by claude-code, 3 days ago
|
|
23
|
+
└ no model invoked ▸ 1,372 tokens, not 41,838
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
**Requires:** Python 3.11+ and Docker (for the database).
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pipx install echo-mem
|
|
32
|
+
echo-memory quickstart
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`quickstart` starts the database, applies the schema, and prints the `claude mcp add`
|
|
36
|
+
line that registers it, filled in with the port it actually used. The Postgres image is
|
|
37
|
+
published, so nothing compiles.
|
|
38
|
+
|
|
39
|
+
Or use the hosted service and run no database at all:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pipx install echo-mem
|
|
43
|
+
echo-memory connect <key> # a key from https://app.echo-mem.com
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then once per machine, so an agent knows *when* to record and recall rather than only
|
|
47
|
+
that the tools exist:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
echo-memory install --global
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> **Restart your client afterwards.** An MCP server is a long lived process that holds
|
|
54
|
+
> the code and config it started with, and an editable install does not change that.
|
|
55
|
+
|
|
56
|
+
> **The PyPI name is `echo-mem`, not `echo-memory`.** That name belongs to an unrelated
|
|
57
|
+
> hosted product. The import package and the CLI are both `echo_memory` / `echo-memory`;
|
|
58
|
+
> only the distribution name differs.
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
echo-memory status # what each scope holds, and which agents have written
|
|
64
|
+
echo-memory health # a score, what is weak, and what to do about it
|
|
65
|
+
echo-memory dashboard --serve # the graph, in a browser, localhost only
|
|
66
|
+
|
|
67
|
+
echo-memory why <fact_id> # the full audit trail for one fact
|
|
68
|
+
echo-memory recall "<question>" # query the store from a terminal
|
|
69
|
+
echo-memory export # everything, as JSON
|
|
70
|
+
|
|
71
|
+
echo-memory install --for cursor # wire one client, project scoped
|
|
72
|
+
echo-memory adopt # wire every MCP client on the machine, each with its own id
|
|
73
|
+
|
|
74
|
+
echo-memory eval # retrieval quality against your own store
|
|
75
|
+
echo-memory eval --context # what a recall costs against injecting everything
|
|
76
|
+
echo-memory eval --context --sweep # the same, as a curve across corpus size
|
|
77
|
+
echo-memory calibrate # is entity resolution trustworthy on your data
|
|
78
|
+
echo-memory benchmark # write, query and digest latency
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### The six MCP tools
|
|
82
|
+
|
|
83
|
+
| Tool | What it does |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `write_episode` | Store entities and the facts connecting them. No model call. |
|
|
86
|
+
| `query_memory` | Hybrid vector and full text retrieval, fused by reciprocal rank. |
|
|
87
|
+
| `record_recall_save` | Mark that a recalled fact saved re explaining something. Refuses a fact no read returned. |
|
|
88
|
+
| `get_audit_log` | Every change to memory, with a plain language reason. |
|
|
89
|
+
| `pending_documents` | Memory files this project wrote that the graph has not heard about. |
|
|
90
|
+
| `mark_ingested` | Close one of those out. |
|
|
91
|
+
|
|
92
|
+
## What you get
|
|
93
|
+
|
|
94
|
+
**A graph, not a list.** Entities are nodes and a fact is an edge between two of them.
|
|
95
|
+
Two sessions that never knew about each other resolve onto the same entity by name, so
|
|
96
|
+
the second inherits what the first learned.
|
|
97
|
+
|
|
98
|
+
**Bounded retrieval.** Old, rarely read memory consolidates into higher level summaries
|
|
99
|
+
over time. Nothing is discarded; what changes is how much a query has to walk.
|
|
100
|
+
|
|
101
|
+
**Provenance on every fact.** Who wrote it, which tool, which project, when, and which
|
|
102
|
+
reads returned it. A superseded fact is never deleted. It stops being drawn and stays
|
|
103
|
+
reachable with its history.
|
|
104
|
+
|
|
105
|
+
**Causal typing.** Edges can be tagged `caused_by`, `led_to`, `blocked_by`,
|
|
106
|
+
`contradicts`, set by the agent's own read of the conversation rather than inferred
|
|
107
|
+
statistically.
|
|
108
|
+
|
|
109
|
+
**No inference on the write path.** Extraction happens in the calling agent, so storing
|
|
110
|
+
a memory invokes no model on the server. The cost moved rather than vanished: the agent
|
|
111
|
+
has to arrive with entities and facts already extracted, which is what the
|
|
112
|
+
[tool contract](docs/DEVELOPMENT.md) spells out. The comparison that makes this matter is
|
|
113
|
+
Zep/Graphiti, the closest architectural match, whose own description of ingestion is that
|
|
114
|
+
"every episode triggers multiple LLM calls" and that "write cost scales with volume".
|
|
115
|
+
|
|
116
|
+
The obvious reply is that cheap writes are cheap because they do less, and that reply is
|
|
117
|
+
correct on the mechanism. [`docs/WRITE-COST.md`](docs/WRITE-COST.md) answers it properly,
|
|
118
|
+
including the two measured costs of the choice: the Stop gate fired seven times and
|
|
119
|
+
produced one fact, and a write touching an ambiguous entity is deferred while the call
|
|
120
|
+
returns as though it succeeded.
|
|
121
|
+
|
|
122
|
+
**Any MCP client.** A coding assistant, a chatbot, an ops agent, or something built in
|
|
123
|
+
house. Coding agents are where this is proven, not what it is limited to.
|
|
124
|
+
|
|
125
|
+
## Numbers, and how they were taken
|
|
126
|
+
|
|
127
|
+
Every figure comes from this repository or a live store, on a date, with the command that
|
|
128
|
+
reproduces it on yours. The corpus is small and the noise floor is stated, because a
|
|
129
|
+
difference nobody sized is not a result.
|
|
130
|
+
|
|
131
|
+
| Measure | Value | Reproduce |
|
|
132
|
+
|---|---|---|
|
|
133
|
+
| Context per recall vs injecting everything | **96.7% less**, hit@10 0.872 over 1,190 questions | `echo-memory eval --context` |
|
|
134
|
+
| The same saving across 8x of corpus growth | 75.5% at 32 facts rising to **96.4% at 261**, hit@10 0.900 to 0.946 | `echo-memory eval --context --sweep` |
|
|
135
|
+
| LoCoMo retrieval, 1,982 questions, 5,882 turns | recall@10 **0.601**, hit@10 0.658, MRR 0.460 | `scripts/locomo-bench.py` |
|
|
136
|
+
| LongMemEval retrieval, 90 questions, 15 per type | session@10 **0.940**, turn@10 0.727 | `scripts/longmemeval-bench.py --per-type 15` |
|
|
137
|
+
| Server side model calls per write | **0** | `echo-memory benchmark` |
|
|
138
|
+
| Write, query, digest latency (median) | 15ms, 8ms, 1ms | `echo-memory benchmark` |
|
|
139
|
+
| Entity resolution AUC | 0.666, 95% CI [0.421, 0.881] | `echo-memory calibrate` |
|
|
140
|
+
|
|
141
|
+
That last row is the one that went the wrong way, and it is here on purpose. The interval
|
|
142
|
+
includes chance, so the unattended merge is switched off: at the automatic bar precision
|
|
143
|
+
was 50% over two reviewed pairs, and the audit log showed that path had fired once in the
|
|
144
|
+
system's entire history. A near match is now offered for confirmation instead.
|
|
145
|
+
|
|
146
|
+
The LoCoMo row is retrieval, not QA accuracy. Published LoCoMo results have a model write
|
|
147
|
+
an answer and a second model judge it; this asks only whether the turn holding the answer
|
|
148
|
+
came back, which is a ceiling on QA accuracy rather than a substitute for it, and is not
|
|
149
|
+
comparable to anybody's published QA figure. It also feeds raw dialogue turns, which skips
|
|
150
|
+
the extraction step this design pushes to the calling agent, so it is a floor as well as a
|
|
151
|
+
ceiling. The worst row, multi hop at recall@1 0.099, is in
|
|
152
|
+
[`docs/BENCHMARKS.md`](docs/BENCHMARKS.md) with the rest.
|
|
153
|
+
|
|
154
|
+
The context saving is measured against a specific baseline, stated so it cannot be read
|
|
155
|
+
as more than it is. Not "no memory at all", which is however long a human spends re
|
|
156
|
+
explaining and is unmeasurable. It is the thing people do instead: keep the project's
|
|
157
|
+
notes in one file and paste the whole file. On that store the file is 325 facts, about
|
|
158
|
+
41,838 tokens; a recall returned 1,372 on average. The hit rate belongs beside it, because
|
|
159
|
+
a recall that returned nothing would score 100%.
|
|
160
|
+
|
|
161
|
+
## The graph
|
|
162
|
+
|
|
163
|
+
Memory is a graph, not a list of notes. Entities are nodes; a fact is an **edge** between
|
|
164
|
+
two of them. That is the whole data model, and everything else follows from it.
|
|
165
|
+
|
|
166
|
+

|
|
167
|
+
|
|
168
|
+
Three projects here. `checkout-api`, `mobile-app` and `data-pipeline` were recorded in
|
|
169
|
+
separate sessions and never told about each other, yet the picture already separates them,
|
|
170
|
+
because separation is a property of the edges rather than a label anyone applied.
|
|
171
|
+
|
|
172
|
+
**Clusters come from structure.** Densely connected facts are grouped by label propagation
|
|
173
|
+
over the edges, and each cluster is named after its most connected node. That is why
|
|
174
|
+
`data-pipeline` sits apart: nothing it knows touches payments. It is also why
|
|
175
|
+
`checkout-api` and `mobile-app` share a cluster despite being different codebases. They
|
|
176
|
+
genuinely share an idea, and the graph found it rather than being told.
|
|
177
|
+
|
|
178
|
+
**Components are the stronger claim.** Two nodes in different components have no path
|
|
179
|
+
between them at all, which is the strongest statement this graph can make that two
|
|
180
|
+
memories are unrelated.
|
|
181
|
+
|
|
182
|
+
### Click a node: everything it takes part in
|
|
183
|
+
|
|
184
|
+

|
|
185
|
+
|
|
186
|
+
`idempotency keys` is the largest node here and nobody made it large: seventeen facts from
|
|
187
|
+
several services resolved onto one entity by name. The panel lists every one, with which
|
|
188
|
+
agent wrote it and when.
|
|
189
|
+
|
|
190
|
+
### Click a link: why memory believes it
|
|
191
|
+
|
|
192
|
+

|
|
193
|
+
|
|
194
|
+
Not a tooltip. Who wrote the fact, in which project, when, and how each of its entities
|
|
195
|
+
resolved. `echo-memory why <fact_id>` prints the same trail in a terminal.
|
|
196
|
+
|
|
197
|
+
### Seeing your own
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
echo-memory dashboard --serve --open
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
The images above come from a synthetic dataset (`scripts/demo-seed.py`) rather than a real
|
|
204
|
+
store, for the obvious reason: a real memory graph is full of hostnames, account numbers
|
|
205
|
+
and client names.
|
|
206
|
+
|
|
207
|
+
## Wiring more than one tool
|
|
208
|
+
|
|
209
|
+
**Give each client its own `ECHO_MEMORY_AGENT_ID`.** Cursor should say `cursor`, Claude
|
|
210
|
+
Desktop `claude-desktop`. Memory is shared either way, but a fact records which tool
|
|
211
|
+
learned it, and two tools claiming the same id makes cross tool recall impossible to see
|
|
212
|
+
afterwards.
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
echo-memory adopt # every MCP client on the machine, each with its own id
|
|
216
|
+
echo-memory install [path] # one project: MCP config plus a skill, committed with the code
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
`adopt` shows the diff before writing anything. For an agent that does not speak MCP, see
|
|
220
|
+
[`docs/INTEGRATIONS.md`](docs/INTEGRATIONS.md).
|
|
221
|
+
|
|
222
|
+
## Is the graph in good shape?
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
echo-memory health
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
A score, what is strong, what needs attention, and what to do about each, including what
|
|
229
|
+
recall has cost: how often memory was read, how often a read returned anything, roughly
|
|
230
|
+
how many tokens were injected, and how many saves those reads produced. Writes were
|
|
231
|
+
counted from the start; reads were not counted at all, so nothing could answer whether
|
|
232
|
+
recall earns what it costs. It exists to be run when you have no question, because a store
|
|
233
|
+
can look healthy by every other number while most of its facts came from a bulk import,
|
|
234
|
+
the last real write was a week ago, and only one of several wired agents has ever written
|
|
235
|
+
anything. `--json` for machine readable output.
|
|
236
|
+
|
|
237
|
+
Nothing in it is gated. The paid plan sells hosting; diagnostics about your own data are
|
|
238
|
+
not a thing to withhold from the person whose data it is.
|
|
239
|
+
|
|
240
|
+
## Architecture
|
|
241
|
+
|
|
242
|
+
**Storage** PostgreSQL with `pgvector` and Apache AGE, from a single local agent up to an
|
|
243
|
+
organisation wide shared graph, with no forced migration later. The novel work is the
|
|
244
|
+
memory structure and the read/write algorithm on top of it, not a new database engine.
|
|
245
|
+
|
|
246
|
+
**Retrieval** Hybrid vector and full text search fused by reciprocal rank in v1a.
|
|
247
|
+
Personalised PageRank via `networkx` lands in v1b for multi hop associative retrieval.
|
|
248
|
+
|
|
249
|
+
**Interface** [Model Context Protocol](https://modelcontextprotocol.io), so any compliant
|
|
250
|
+
agent reads and writes the same graph.
|
|
251
|
+
|
|
252
|
+
## Status
|
|
253
|
+
|
|
254
|
+
Early and staged, on purpose. See [`docs/designs/`](docs/designs/) for the architecture
|
|
255
|
+
and the v1a to v1b plan.
|
|
256
|
+
|
|
257
|
+
| | |
|
|
258
|
+
|---|---|
|
|
259
|
+
| **v1a, built** | Basic recall. Six MCP tools, thirty CLI commands, on PyPI and in the MCP registry. |
|
|
260
|
+
| **v1b, gated** | Causal typing and multi hop retrieval. 187 questions no single fact answers score MRR 0.212 today; the number to beat exists before the feature does. |
|
|
261
|
+
| **v1.1, planned** | Organisation wide tenancy: per agent, per team, or org wide graphs. |
|
|
262
|
+
|
|
263
|
+
The validated wedge driving v1a is memory shared across coding agents, which is the
|
|
264
|
+
author's own daily pain and the case with the most evidence behind it. Everything else is
|
|
265
|
+
the target this architecture is built toward.
|
|
266
|
+
|
|
267
|
+
## Hosted
|
|
268
|
+
|
|
269
|
+
Running it yourself is free forever under Apache 2.0, with no account and no feature held
|
|
270
|
+
back. [app.echo-mem.com](https://app.echo-mem.com) runs the database for you at $99 a
|
|
271
|
+
month if you would rather not.
|
|
272
|
+
|
|
273
|
+
<details>
|
|
274
|
+
<summary>Contributing</summary>
|
|
275
|
+
|
|
276
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md). Issues and pull requests welcome; please read the
|
|
277
|
+
design docs first so proposals fit the staged build plan. A first pull request is asked to
|
|
278
|
+
sign the [Contributor License Agreement](CLA.md), once, in the PR thread.
|
|
279
|
+
|
|
280
|
+
The most useful contribution is a measurement that disagrees with one of the numbers
|
|
281
|
+
above. Run `echo-memory eval`, `calibrate` or `benchmark` on your own store and open an
|
|
282
|
+
issue with the output.
|
|
283
|
+
|
|
284
|
+
</details>
|
|
285
|
+
|
|
286
|
+
## License
|
|
287
|
+
|
|
288
|
+
Apache License 2.0. See [`LICENSE`](LICENSE).
|
|
289
|
+
|
|
290
|
+
<!-- The MCP registry proves you own a PyPI package by finding this line in the
|
|
291
|
+
package's own description. It has to survive into the built distribution,
|
|
292
|
+
which is why it lives in the README rather than in a workflow. -->
|
|
293
|
+
mcp-name: io.github.ayushcodes10/echo-mem
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# (`-m echo_memory.server`) and every existing install reference it, and
|
|
7
7
|
# renaming the module would break live configuration to fix a cosmetic mismatch.
|
|
8
8
|
name = "echo-mem"
|
|
9
|
-
version = "0.
|
|
9
|
+
version = "0.4.1"
|
|
10
10
|
description = "A long-horizon memory architecture for AI agents"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
license = "Apache-2.0"
|