amemo 0.1.0a1__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.
- amemo-0.1.0a1/PKG-INFO +759 -0
- amemo-0.1.0a1/README.md +708 -0
- amemo-0.1.0a1/amemo/__init__.py +2 -0
- amemo-0.1.0a1/amemo/agents/__init__.py +1 -0
- amemo-0.1.0a1/amemo/agents/hooks.py +372 -0
- amemo-0.1.0a1/amemo/agents/install.py +786 -0
- amemo-0.1.0a1/amemo/agents/platforms.py +105 -0
- amemo-0.1.0a1/amemo/anthology.py +159 -0
- amemo-0.1.0a1/amemo/api.py +1741 -0
- amemo-0.1.0a1/amemo/backup.py +745 -0
- amemo-0.1.0a1/amemo/bundle.py +1417 -0
- amemo-0.1.0a1/amemo/canon.py +162 -0
- amemo-0.1.0a1/amemo/check/__init__.py +301 -0
- amemo-0.1.0a1/amemo/check/level1.py +588 -0
- amemo-0.1.0a1/amemo/check/level2.py +359 -0
- amemo-0.1.0a1/amemo/check/level3.py +216 -0
- amemo-0.1.0a1/amemo/check/level4.py +204 -0
- amemo-0.1.0a1/amemo/check/numbers.py +129 -0
- amemo-0.1.0a1/amemo/check/report.py +68 -0
- amemo-0.1.0a1/amemo/check/templates.py +86 -0
- amemo-0.1.0a1/amemo/check/units.py +45 -0
- amemo-0.1.0a1/amemo/cli/__init__.py +213 -0
- amemo-0.1.0a1/amemo/cli/__main__.py +4 -0
- amemo-0.1.0a1/amemo/cli/commands/__init__.py +29 -0
- amemo-0.1.0a1/amemo/cli/commands/agents.py +64 -0
- amemo-0.1.0a1/amemo/cli/commands/backup.py +81 -0
- amemo-0.1.0a1/amemo/cli/commands/bundles.py +133 -0
- amemo-0.1.0a1/amemo/cli/commands/read.py +178 -0
- amemo-0.1.0a1/amemo/cli/commands/setup.py +348 -0
- amemo-0.1.0a1/amemo/cli/commands/tasks.py +44 -0
- amemo-0.1.0a1/amemo/cli/commands/tokens.py +184 -0
- amemo-0.1.0a1/amemo/cli/commands/write.py +212 -0
- amemo-0.1.0a1/amemo/cli/context.py +213 -0
- amemo-0.1.0a1/amemo/cli/errors.py +71 -0
- amemo-0.1.0a1/amemo/db.py +257 -0
- amemo-0.1.0a1/amemo/embed.py +292 -0
- amemo-0.1.0a1/amemo/extract.py +223 -0
- amemo-0.1.0a1/amemo/folders.py +292 -0
- amemo-0.1.0a1/amemo/forms.py +211 -0
- amemo-0.1.0a1/amemo/ids.py +55 -0
- amemo-0.1.0a1/amemo/journal.py +48 -0
- amemo-0.1.0a1/amemo/lookup.py +591 -0
- amemo-0.1.0a1/amemo/mine.py +528 -0
- amemo-0.1.0a1/amemo/model_v1.py +2587 -0
- amemo-0.1.0a1/amemo/objects.py +636 -0
- amemo-0.1.0a1/amemo/ocfl_store.py +353 -0
- amemo-0.1.0a1/amemo/packs/core/pack.yaml +9 -0
- amemo-0.1.0a1/amemo/packs/core/skills/amemo-answer/SKILL.md +54 -0
- amemo-0.1.0a1/amemo/packs/core/skills/amemo-remember/SKILL.md +61 -0
- amemo-0.1.0a1/amemo/packs/core/skills/amemo-verify/SKILL.md +58 -0
- amemo-0.1.0a1/amemo/packs/core/templates/decision.yaml +11 -0
- amemo-0.1.0a1/amemo/packs/core/templates/has_label.yaml +10 -0
- amemo-0.1.0a1/amemo/packs/core/templates/has_source.yaml +15 -0
- amemo-0.1.0a1/amemo/packs/core/templates/has_value.yaml +20 -0
- amemo-0.1.0a1/amemo/packs/core/templates/note.yaml +8 -0
- amemo-0.1.0a1/amemo/packs/core/templates/parameter.yaml +12 -0
- amemo-0.1.0a1/amemo/packs/core/vocab/entity_class.yaml +56 -0
- amemo-0.1.0a1/amemo/packs/core/vocab/kind.yaml +66 -0
- amemo-0.1.0a1/amemo/packs/core/vocab/rel.yaml +145 -0
- amemo-0.1.0a1/amemo/packs/core/vocab/selector_type.yaml +81 -0
- amemo-0.1.0a1/amemo/packs.py +454 -0
- amemo-0.1.0a1/amemo/private.py +99 -0
- amemo-0.1.0a1/amemo/profile.py +235 -0
- amemo-0.1.0a1/amemo/recipes.py +823 -0
- amemo-0.1.0a1/amemo/records.py +681 -0
- amemo-0.1.0a1/amemo/review.py +584 -0
- amemo-0.1.0a1/amemo/schema/amemo_v1.schema.json +3213 -0
- amemo-0.1.0a1/amemo/schema/amemo_v1.yaml +1258 -0
- amemo-0.1.0a1/amemo/search.py +454 -0
- amemo-0.1.0a1/amemo/service/__init__.py +8 -0
- amemo-0.1.0a1/amemo/service/__main__.py +4 -0
- amemo-0.1.0a1/amemo/service/app.py +151 -0
- amemo-0.1.0a1/amemo/service/http-v1.openapi.yaml +1259 -0
- amemo-0.1.0a1/amemo/service/http_v1.py +718 -0
- amemo-0.1.0a1/amemo/service/mcp.py +659 -0
- amemo-0.1.0a1/amemo/service/problems.py +179 -0
- amemo-0.1.0a1/amemo/service/server.py +101 -0
- amemo-0.1.0a1/amemo/service/session.py +198 -0
- amemo-0.1.0a1/amemo/service/ui.py +151 -0
- amemo-0.1.0a1/amemo/settings.py +362 -0
- amemo-0.1.0a1/amemo/sources.py +471 -0
- amemo-0.1.0a1/amemo/sql/0001_core.sql +287 -0
- amemo-0.1.0a1/amemo/sql/0002_objects.sql +137 -0
- amemo-0.1.0a1/amemo/sql/0003_packs.sql +26 -0
- amemo-0.1.0a1/amemo/sql/0004_hub.sql +6 -0
- amemo-0.1.0a1/amemo/sql/0005_search.sql +99 -0
- amemo-0.1.0a1/amemo/sql/0006_check.sql +110 -0
- amemo-0.1.0a1/amemo/sql/0007_ontology.sql +42 -0
- amemo-0.1.0a1/amemo/sql/0008_folders.sql +43 -0
- amemo-0.1.0a1/amemo/sql/0009_record_list.sql +11 -0
- amemo-0.1.0a1/amemo/sql/0010_graph_overview.sql +10 -0
- amemo-0.1.0a1/amemo/sql/0011_object_text_ocr.sql +15 -0
- amemo-0.1.0a1/amemo/sql/0012_tasks.sql +32 -0
- amemo-0.1.0a1/amemo/sql/0013_rename_amemo.sql +98 -0
- amemo-0.1.0a1/amemo/tasks.py +274 -0
- amemo-0.1.0a1/amemo/usefulness.py +155 -0
- amemo-0.1.0a1/amemo/vocab.py +52 -0
- amemo-0.1.0a1/amemo.egg-info/PKG-INFO +759 -0
- amemo-0.1.0a1/amemo.egg-info/SOURCES.txt +174 -0
- amemo-0.1.0a1/amemo.egg-info/dependency_links.txt +1 -0
- amemo-0.1.0a1/amemo.egg-info/entry_points.txt +2 -0
- amemo-0.1.0a1/amemo.egg-info/requires.txt +34 -0
- amemo-0.1.0a1/amemo.egg-info/top_level.txt +1 -0
- amemo-0.1.0a1/pyproject.toml +84 -0
- amemo-0.1.0a1/setup.cfg +4 -0
- amemo-0.1.0a1/tests/test_anthology.py +85 -0
- amemo-0.1.0a1/tests/test_backup.py +344 -0
- amemo-0.1.0a1/tests/test_backup_restore.py +448 -0
- amemo-0.1.0a1/tests/test_bundle_export.py +413 -0
- amemo-0.1.0a1/tests/test_bundle_import.py +979 -0
- amemo-0.1.0a1/tests/test_bundle_lowlevel.py +235 -0
- amemo-0.1.0a1/tests/test_canon.py +193 -0
- amemo-0.1.0a1/tests/test_check_commits.py +70 -0
- amemo-0.1.0a1/tests/test_check_level1.py +460 -0
- amemo-0.1.0a1/tests/test_check_level2.py +527 -0
- amemo-0.1.0a1/tests/test_check_level3.py +220 -0
- amemo-0.1.0a1/tests/test_check_level4.py +218 -0
- amemo-0.1.0a1/tests/test_check_numbers.py +124 -0
- amemo-0.1.0a1/tests/test_check_schema.py +85 -0
- amemo-0.1.0a1/tests/test_check_sql.py +252 -0
- amemo-0.1.0a1/tests/test_check_submit.py +517 -0
- amemo-0.1.0a1/tests/test_check_templates.py +229 -0
- amemo-0.1.0a1/tests/test_check_units.py +38 -0
- amemo-0.1.0a1/tests/test_cli.py +772 -0
- amemo-0.1.0a1/tests/test_db.py +406 -0
- amemo-0.1.0a1/tests/test_embed.py +325 -0
- amemo-0.1.0a1/tests/test_extract.py +186 -0
- amemo-0.1.0a1/tests/test_folders.py +571 -0
- amemo-0.1.0a1/tests/test_forms.py +172 -0
- amemo-0.1.0a1/tests/test_hooks.py +300 -0
- amemo-0.1.0a1/tests/test_hub.py +50 -0
- amemo-0.1.0a1/tests/test_ids.py +76 -0
- amemo-0.1.0a1/tests/test_install.py +517 -0
- amemo-0.1.0a1/tests/test_lookup_lists.py +429 -0
- amemo-0.1.0a1/tests/test_mcp.py +565 -0
- amemo-0.1.0a1/tests/test_mine.py +322 -0
- amemo-0.1.0a1/tests/test_object_view.py +208 -0
- amemo-0.1.0a1/tests/test_objects.py +446 -0
- amemo-0.1.0a1/tests/test_ocfl_store.py +384 -0
- amemo-0.1.0a1/tests/test_ontology_core.py +39 -0
- amemo-0.1.0a1/tests/test_ontology_level1.py +248 -0
- amemo-0.1.0a1/tests/test_ontology_packs.py +111 -0
- amemo-0.1.0a1/tests/test_ontology_profile_rules.py +94 -0
- amemo-0.1.0a1/tests/test_ontology_schema.py +66 -0
- amemo-0.1.0a1/tests/test_ontology_sql.py +68 -0
- amemo-0.1.0a1/tests/test_packs.py +175 -0
- amemo-0.1.0a1/tests/test_private_secrets.py +90 -0
- amemo-0.1.0a1/tests/test_profile_card.py +349 -0
- amemo-0.1.0a1/tests/test_profile_projection.py +90 -0
- amemo-0.1.0a1/tests/test_recipes_graph.py +582 -0
- amemo-0.1.0a1/tests/test_recipes_history.py +231 -0
- amemo-0.1.0a1/tests/test_recipes_search.py +395 -0
- amemo-0.1.0a1/tests/test_records.py +791 -0
- amemo-0.1.0a1/tests/test_review.py +647 -0
- amemo-0.1.0a1/tests/test_review_sources.py +283 -0
- amemo-0.1.0a1/tests/test_review_stats.py +187 -0
- amemo-0.1.0a1/tests/test_rrf.py +127 -0
- amemo-0.1.0a1/tests/test_search_lexical.py +154 -0
- amemo-0.1.0a1/tests/test_search_projection.py +129 -0
- amemo-0.1.0a1/tests/test_search_schema.py +69 -0
- amemo-0.1.0a1/tests/test_search_text.py +92 -0
- amemo-0.1.0a1/tests/test_service_4d.py +336 -0
- amemo-0.1.0a1/tests/test_service_bundles.py +596 -0
- amemo-0.1.0a1/tests/test_service_contract.py +501 -0
- amemo-0.1.0a1/tests/test_service_core.py +617 -0
- amemo-0.1.0a1/tests/test_service_lib.py +456 -0
- amemo-0.1.0a1/tests/test_service_read.py +719 -0
- amemo-0.1.0a1/tests/test_service_ui.py +181 -0
- amemo-0.1.0a1/tests/test_service_write.py +894 -0
- amemo-0.1.0a1/tests/test_sources.py +403 -0
- amemo-0.1.0a1/tests/test_sql_schema.py +161 -0
- amemo-0.1.0a1/tests/test_tasks.py +223 -0
- amemo-0.1.0a1/tests/test_usefulness.py +298 -0
- amemo-0.1.0a1/tests/test_v1_generated.py +12 -0
- amemo-0.1.0a1/tests/test_v1_schema.py +189 -0
- amemo-0.1.0a1/tests/test_v1_templates.py +119 -0
amemo-0.1.0a1/PKG-INFO
ADDED
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: amemo
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Summary: Verifiable shared memory for AI agents and people: atoms with checked source quotes, revisions and review on PostgreSQL + pgvector
|
|
5
|
+
Author: empty-complete
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/empty-complete/amemo
|
|
8
|
+
Project-URL: Source, https://github.com/empty-complete/amemo
|
|
9
|
+
Project-URL: Issues, https://github.com/empty-complete/amemo/issues
|
|
10
|
+
Keywords: agent-memory,claude-memory,knowledge-base,mcp-server,pgvector,postgresql
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Database
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Requires-Dist: pymupdf>=1.24
|
|
21
|
+
Requires-Dist: rapidfuzz>=3.0
|
|
22
|
+
Requires-Dist: pydantic<3,>=2.13
|
|
23
|
+
Requires-Dist: pyyaml>=6
|
|
24
|
+
Requires-Dist: psycopg[binary]<4,>=3.2
|
|
25
|
+
Requires-Dist: pgvector<1,>=0.5.1
|
|
26
|
+
Requires-Dist: rfc8785==0.1.4
|
|
27
|
+
Requires-Dist: ucumvert==0.3.2
|
|
28
|
+
Requires-Dist: pint<0.26,>=0.25
|
|
29
|
+
Requires-Dist: openpyxl>=3.1
|
|
30
|
+
Requires-Dist: packaging>=23
|
|
31
|
+
Requires-Dist: starlette<2,>=1.10
|
|
32
|
+
Requires-Dist: anyio>=4
|
|
33
|
+
Requires-Dist: uvicorn>=0.30
|
|
34
|
+
Requires-Dist: psycopg-pool<4,>=3.2
|
|
35
|
+
Requires-Dist: python-multipart>=0.0.20
|
|
36
|
+
Requires-Dist: mcp<2.4,>=2.3
|
|
37
|
+
Requires-Dist: mcp-types<2.4,>=2.3
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
40
|
+
Requires-Dist: ruff>=0.14; extra == "dev"
|
|
41
|
+
Requires-Dist: linkml==1.12.1; extra == "dev"
|
|
42
|
+
Requires-Dist: linkml-runtime==1.12.1; extra == "dev"
|
|
43
|
+
Requires-Dist: jsonschema>=4.20; extra == "dev"
|
|
44
|
+
Requires-Dist: ocfl-py==2.1.0; extra == "dev"
|
|
45
|
+
Requires-Dist: httpx2>=2.0; extra == "dev"
|
|
46
|
+
Requires-Dist: openapi-spec-validator>=0.8.4; extra == "dev"
|
|
47
|
+
Provides-Extra: embed
|
|
48
|
+
Requires-Dist: fastembed<0.10,>=0.9.1; extra == "embed"
|
|
49
|
+
Provides-Extra: web
|
|
50
|
+
Requires-Dist: amemo-web==0.1.0a1; extra == "web"
|
|
51
|
+
|
|
52
|
+
<p align="center">
|
|
53
|
+
<picture>
|
|
54
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/empty-complete/amemo/v0.1.0a1/docs/assets/readme/banner-dark.svg">
|
|
55
|
+
<img alt="amemo — another memory" src="https://raw.githubusercontent.com/empty-complete/amemo/v0.1.0a1/docs/assets/readme/banner-light.svg" width="100%">
|
|
56
|
+
</picture>
|
|
57
|
+
</p>
|
|
58
|
+
|
|
59
|
+
<h3 align="center">Shared memory for people and AI agents, where every claim comes with its proof.</h3>
|
|
60
|
+
|
|
61
|
+
<p align="center">
|
|
62
|
+
An open knowledge base of small, sourced statements. Agents propose, automatic checks find
|
|
63
|
+
the quote in the actual file, and a human has the final word.
|
|
64
|
+
</p>
|
|
65
|
+
|
|
66
|
+
<p align="center">
|
|
67
|
+
<a href="https://github.com/empty-complete/amemo/actions/workflows/ci.yml"><img alt="Tests" src="https://img.shields.io/github/actions/workflow/status/empty-complete/amemo/ci.yml?branch=main&label=tests&style=flat-square"></a>
|
|
68
|
+
<a href="https://github.com/empty-complete/amemo/actions/workflows/web.yml"><img alt="Web UI" src="https://img.shields.io/github/actions/workflow/status/empty-complete/amemo/web.yml?branch=main&label=web%20ui&style=flat-square"></a>
|
|
69
|
+
<a href="https://github.com/empty-complete/amemo/blob/v0.1.0a1/LICENSE.txt"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-2a78d6?style=flat-square"></a>
|
|
70
|
+
<img alt="Python 3.11+" src="https://img.shields.io/badge/python-3.11%2B-3776ab?style=flat-square&logo=python&logoColor=white">
|
|
71
|
+
<img alt="PostgreSQL 16+ with pgvector" src="https://img.shields.io/badge/PostgreSQL-16%2B%20%C2%B7%20pgvector-4169e1?style=flat-square&logo=postgresql&logoColor=white">
|
|
72
|
+
<img alt="Status: early development" src="https://img.shields.io/badge/status-early%20development-eb6834?style=flat-square">
|
|
73
|
+
</p>
|
|
74
|
+
|
|
75
|
+
<p align="center">
|
|
76
|
+
<a href="#quickstart"><b>Quickstart</b></a> ·
|
|
77
|
+
<a href="#features"><b>Features</b></a> ·
|
|
78
|
+
<a href="#how-it-works"><b>How it works</b></a> ·
|
|
79
|
+
<a href="#roadmap"><b>Roadmap</b></a> ·
|
|
80
|
+
<a href="https://github.com/empty-complete/amemo/blob/v0.1.0a1/docs/library.md"><b>Docs</b></a> ·
|
|
81
|
+
<a href="https://github.com/empty-complete/amemo/blob/v0.1.0a1/docs/MANIFESTO.md"><b>Manifesto</b></a> ·
|
|
82
|
+
<a href="https://github.com/empty-complete/amemo/blob/v0.1.0a1/README.ru.md"><b>Русский</b></a>
|
|
83
|
+
</p>
|
|
84
|
+
|
|
85
|
+
<p align="center">
|
|
86
|
+
<picture>
|
|
87
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/empty-complete/amemo/v0.1.0a1/docs/assets/readme/en/sky-dark.png">
|
|
88
|
+
<img alt="The sky: the whole sample base as a constellation of records and objects, with folders and family filters" src="https://raw.githubusercontent.com/empty-complete/amemo/v0.1.0a1/docs/assets/readme/en/sky-light.png" width="100%">
|
|
89
|
+
</picture>
|
|
90
|
+
</p>
|
|
91
|
+
|
|
92
|
+
> [!IMPORTANT]
|
|
93
|
+
> **amemo is young.** The core, the HTTP service with the web UI, the CLI, the MCP server and agent memory for
|
|
94
|
+
> Claude Code and Codex work today and are tested in CI on PostgreSQL 18 and 16. The schema and the HTTP
|
|
95
|
+
> contract may still change. The screenshots on this page come from the real service on a sample base
|
|
96
|
+
> (`tools/seed_sample_en.py`, a fictional network of weather stations).
|
|
97
|
+
|
|
98
|
+
## Why amemo
|
|
99
|
+
|
|
100
|
+
Agents forget everything between sessions, and what they do remember is text without proof. Notes and
|
|
101
|
+
wikis keep what people wrote, but not where it came from or who decided it was right.
|
|
102
|
+
|
|
103
|
+
amemo keeps something smaller and sturdier: an **atom**. An atom is one statement, the exact place in the
|
|
104
|
+
file it came from, the full history of its changes, and the decision of a person.
|
|
105
|
+
|
|
106
|
+
<table>
|
|
107
|
+
<tr>
|
|
108
|
+
<td width="50%" valign="top">
|
|
109
|
+
<b>Every claim has a source</b><br>
|
|
110
|
+
A quote is checked against the real text of the file: a page in a PDF, a heading in Markdown,
|
|
111
|
+
a cell range in a spreadsheet.
|
|
112
|
+
</td>
|
|
113
|
+
<td width="50%" valign="top">
|
|
114
|
+
<b>Nothing is overwritten</b><br>
|
|
115
|
+
Every change is a new revision with a reason, an author and a content hash. Mistakes are
|
|
116
|
+
superseded, not erased.
|
|
117
|
+
</td>
|
|
118
|
+
</tr>
|
|
119
|
+
<tr>
|
|
120
|
+
<td width="50%" valign="top">
|
|
121
|
+
<b>Checks run on every write</b><br>
|
|
122
|
+
Schema, units, the quote, common sense and duplicates are checked before anything lands.
|
|
123
|
+
A failed batch writes nothing.
|
|
124
|
+
</td>
|
|
125
|
+
<td width="50%" valign="top">
|
|
126
|
+
<b>The human decides</b><br>
|
|
127
|
+
Agents propose and pre-verify. Accepting a claim into the canon is a person's call, and the
|
|
128
|
+
system shows who made it.
|
|
129
|
+
</td>
|
|
130
|
+
</tr>
|
|
131
|
+
</table>
|
|
132
|
+
|
|
133
|
+
## Features
|
|
134
|
+
|
|
135
|
+
<table>
|
|
136
|
+
<tr>
|
|
137
|
+
<td width="50%" valign="top">
|
|
138
|
+
<picture>
|
|
139
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/empty-complete/amemo/v0.1.0a1/docs/assets/readme/en/record-dark.png">
|
|
140
|
+
<img alt="A record page with the quote highlighted on the source page" src="https://raw.githubusercontent.com/empty-complete/amemo/v0.1.0a1/docs/assets/readme/en/record-light.png">
|
|
141
|
+
</picture>
|
|
142
|
+
<h3>A claim next to its proof</h3>
|
|
143
|
+
A record reads like a note: properties, a Markdown body and the quote highlighted on the real page
|
|
144
|
+
of the source file. Cited files are stored once by content hash and kept forever.
|
|
145
|
+
</td>
|
|
146
|
+
<td width="50%" valign="top">
|
|
147
|
+
<picture>
|
|
148
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/empty-complete/amemo/v0.1.0a1/docs/assets/readme/en/review-dark.png">
|
|
149
|
+
<img alt="The review queue with the claim, the quote and the decision buttons" src="https://raw.githubusercontent.com/empty-complete/amemo/v0.1.0a1/docs/assets/readme/en/review-light.png">
|
|
150
|
+
</picture>
|
|
151
|
+
<h3>Review in one keystroke</h3>
|
|
152
|
+
Anything doubtful, risky or changed lands in a queue. Each item shows the claim, the quote and
|
|
153
|
+
its neighbours. <kbd>Y</kbd> confirms, <kbd>N</kbd> rejects, <kbd>J</kbd>/<kbd>K</kbd> move on, and
|
|
154
|
+
parallel reviewers never get the same item.
|
|
155
|
+
</td>
|
|
156
|
+
</tr>
|
|
157
|
+
<tr>
|
|
158
|
+
<td width="50%" valign="top">
|
|
159
|
+
<picture>
|
|
160
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/empty-complete/amemo/v0.1.0a1/docs/assets/readme/en/card-dark.png">
|
|
161
|
+
<img alt="An entity card: completeness, grouped parameters with known, missing and conflicting values" src="https://raw.githubusercontent.com/empty-complete/amemo/v0.1.0a1/docs/assets/readme/en/card-light.png">
|
|
162
|
+
</picture>
|
|
163
|
+
<h3>Entity cards</h3>
|
|
164
|
+
A profile says which parameters an object needs. The card shows what is known, missing or in
|
|
165
|
+
conflict, and it is built live from atoms, so nothing goes stale.
|
|
166
|
+
</td>
|
|
167
|
+
<td width="50%" valign="top">
|
|
168
|
+
<picture>
|
|
169
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/empty-complete/amemo/v0.1.0a1/docs/assets/readme/en/agent-dark.png">
|
|
170
|
+
<img alt="An agent session over MCP: amemo_search, amemo_get with the source quote, amemo_helpful" src="https://raw.githubusercontent.com/empty-complete/amemo/v0.1.0a1/docs/assets/readme/en/agent-light.png">
|
|
171
|
+
</picture>
|
|
172
|
+
<h3>Memory for agents</h3>
|
|
173
|
+
Claude Code and Codex ask amemo first. They read short cards, fetch only the records they need, and
|
|
174
|
+
mark what helped, which costs fewer tokens than reading raw files.
|
|
175
|
+
</td>
|
|
176
|
+
</tr>
|
|
177
|
+
</table>
|
|
178
|
+
|
|
179
|
+
**Under the hood**
|
|
180
|
+
|
|
181
|
+
- **Hybrid search.** Exact codes, full text and local vectors are merged with RRF. An exact code always
|
|
182
|
+
comes first, and an ambiguous query returns every candidate instead of a guess.
|
|
183
|
+
- **History you can query.** Bodies are stored as canonical JSON (JCS) with a `sha256-c1:` hash, and
|
|
184
|
+
`what_changed` answers "what changed since Monday" for one record or the whole base.
|
|
185
|
+
- **Files that stay put.** Sources live in an OCFL object store by content digest. When a new version
|
|
186
|
+
of a file arrives, every record that cites an older version gets a review item.
|
|
187
|
+
- **Domain packs.** Vocabularies, templates and entity profiles are plug-in packages with versions
|
|
188
|
+
and dependencies. The core itself is a pack.
|
|
189
|
+
- **Bundles.** You can share a branch of knowledge between bases as a folder or a zip, with or without
|
|
190
|
+
the files. Ids survive the trip, and anything that differs from the local copy goes to review.
|
|
191
|
+
- **Backups with proof.** A backup is a `pg_dump` plus a snapshot of the object store. It is verified
|
|
192
|
+
by a real trial restore into a temporary database.
|
|
193
|
+
- **Boring storage.** Data lives in plain PostgreSQL 16+ with pgvector. The schema is written in
|
|
194
|
+
LinkML, with Pydantic models and JSON Schema generated from it.
|
|
195
|
+
|
|
196
|
+
## Quickstart
|
|
197
|
+
|
|
198
|
+
amemo needs Python 3.11+, Docker (for PostgreSQL) and Linux or macOS. On Windows, use WSL2.
|
|
199
|
+
|
|
200
|
+
**1. Start PostgreSQL and install the library**
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
git clone https://github.com/empty-complete/amemo.git
|
|
204
|
+
cd amemo
|
|
205
|
+
docker compose up -d --wait db # PostgreSQL 18 + pgvector on 127.0.0.1:5435
|
|
206
|
+
python3 -m venv .venv
|
|
207
|
+
.venv/bin/pip install -e . # or -e ".[embed]" for local multilingual vectors
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**2. Write a fact with its source**
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
from pathlib import Path
|
|
214
|
+
|
|
215
|
+
from amemo import check, db, recipes
|
|
216
|
+
from amemo.check import Proposal
|
|
217
|
+
from amemo.ocfl_store import ObjectStore
|
|
218
|
+
|
|
219
|
+
conn = db.connect("postgresql://amemo:amemo@127.0.0.1:5435/amemo")
|
|
220
|
+
db.prepare(conn) # migrations and the core vocabulary
|
|
221
|
+
|
|
222
|
+
docs = Path("my-docs") # the folder with your source files
|
|
223
|
+
store = ObjectStore(Path("amemo-objects")) # cited files, kept by content hash
|
|
224
|
+
(docs / "pgvector").mkdir(parents=True, exist_ok=True)
|
|
225
|
+
(docs / "pgvector" / "README.md").write_text(
|
|
226
|
+
"## HNSW\n\nvector - up to 2,000 dimensions, halfvec - up to 4,000 dimensions\n")
|
|
227
|
+
|
|
228
|
+
fact = {
|
|
229
|
+
"kind": "fact",
|
|
230
|
+
"label": {"en": "pgvector indexes vector with HNSW up to 2,000 dimensions",
|
|
231
|
+
"ru": "pgvector: индекс HNSW по vector — до 2000 измерений"},
|
|
232
|
+
"summary_plain": {"en": "Longer embeddings need halfvec, up to 4,000.",
|
|
233
|
+
"ru": "Длинные векторы индексируются через halfvec, до 4000."},
|
|
234
|
+
"sources": [{"path": "pgvector/README.md",
|
|
235
|
+
"selectors": [{"type": "heading", "value": "HNSW"}],
|
|
236
|
+
"quote": "vector - up to 2,000 dimensions"}],
|
|
237
|
+
}
|
|
238
|
+
result = check.submit(conn, store, docs, [Proposal("atom", fact)], agent="model:my-agent")
|
|
239
|
+
print(result.state, result.records[0]["status"]) # approved draft
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Levels 1–4 passed and the file was copied into the store. The atom stays a `draft` until the semantic
|
|
243
|
+
review (level 5) or a person confirms it.
|
|
244
|
+
|
|
245
|
+
**3. Try to cite something the file doesn't say**
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
wrong = {**fact, "sources": [{**fact["sources"][0], "quote": "vector - up to 16,000 dimensions"}]}
|
|
249
|
+
result = check.submit(conn, store, docs, [Proposal("atom", wrong)], agent="model:my-agent")
|
|
250
|
+
print(result.state, result.report["issues"][0]["code"]) # rejected quote_fuzzy
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
The whole batch is rejected and nothing is written. The report shows the closest real text in the file:
|
|
254
|
+
`vector - up to 2,000 dimensions`.
|
|
255
|
+
|
|
256
|
+
**4. Search**
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
hits = recipes.exact_fact(conn, "halfvec", agent="model:my-agent")
|
|
260
|
+
print(hits["items"][0]["label"]["en"]) # pgvector indexes vector with HNSW up to 2,000 dimensions
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The [library reference](https://github.com/empty-complete/amemo/blob/v0.1.0a1/docs/library.md) covers the rest: the review queue, entity cards, bundles,
|
|
264
|
+
folders and backups.
|
|
265
|
+
|
|
266
|
+
### Try the web UI on sample data
|
|
267
|
+
|
|
268
|
+
The web UI runs on the service, or on mock data of the same HTTP contract. What is there:
|
|
269
|
+
|
|
270
|
+
- **Sky.** The home page is a constellation of records, colored by family (source, work, object); a new
|
|
271
|
+
record flashes as it lands. A 3D view loads on request.
|
|
272
|
+
- **Reading.** A record shows its properties and body next to the source: the quote highlighted on the real
|
|
273
|
+
PDF page, a spreadsheet range or a text fragment, with the file version. Entity cards, objects and a
|
|
274
|
+
feed of changes.
|
|
275
|
+
- **Writing.** Quick write in one line (`N`), a full editor with Markdown, formulas and sources, folders
|
|
276
|
+
with drag and drop, and a kind constructor. Every action has a 6-second undo.
|
|
277
|
+
- **Review.** The queue in two panes with `J` `K` `Y` `N` `S`: the claim, its source, the reviewer's notes,
|
|
278
|
+
a word diff for edits and both versions when a source changed. **Base** shows the queue, statuses and
|
|
279
|
+
backups; **Bundles** exports and imports `amem.bundle/1` and anthologies.
|
|
280
|
+
|
|
281
|
+
Light and dark themes, Russian and English, phone layouts. The design system is described in
|
|
282
|
+
[`web/DESIGN.md`](https://github.com/empty-complete/amemo/blob/v0.1.0a1/web/DESIGN.md).
|
|
283
|
+
|
|
284
|
+
On the service, with a sample base (a weather station: sensors, a PDF manual with quotes, notes and an
|
|
285
|
+
open review queue), in an empty database:
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
.venv/bin/pip install -e . -e web/python # the service and the web UI package
|
|
289
|
+
(cd web && npm ci && npm run build) # Node 24+; the build goes into the amemo-web package
|
|
290
|
+
export AMEMO_DSN=postgresql://amemo:amemo@127.0.0.1:5435/amemo AMEMO_DOCS=~/amemo-sample/docs
|
|
291
|
+
.venv/bin/python tools/seed_sample.py # the sample: 22 records, 2 open review items
|
|
292
|
+
.venv/bin/python -m amemo.service # then open http://127.0.0.1:8767/ui/
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
On mock data, without a database:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
cd web && npm ci && npm run dev:mock # then open http://127.0.0.1:5173/ui/
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### One-command install
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
pipx install 'amemo[web]' # or pip install / uv tool install; [web] adds the web UI at /ui
|
|
305
|
+
amemo init --dsn postgresql://amemo:amemo@127.0.0.1:5435/amemo # database, object store; the DSN goes to amemo.toml
|
|
306
|
+
amemo install claude-code # MCP server, hooks and skills; `amemo install codex` for Codex CLI
|
|
307
|
+
amemo doctor # what works and what to fix
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
`amemo init --dsn` remembers the connection string in `~/.config/amemo/amemo.toml` (mode `0600`), so agents, hooks
|
|
311
|
+
and the CLI find the database without `AMEMO_DSN` in their environment. PostgreSQL with pgvector comes from
|
|
312
|
+
`docker compose up -d --wait db` or your own server. CI runs these steps on every push.
|
|
313
|
+
|
|
314
|
+
## Service
|
|
315
|
+
|
|
316
|
+
The HTTP service serves the [v1 contract](https://github.com/empty-complete/amemo/blob/v0.1.0a1/docs/api/http-v1.openapi.yaml) at `/api/v1` and the web UI at `/ui`.
|
|
317
|
+
Start it with `amemo serve` (or `python -m amemo.service`):
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
export AMEMO_DSN=postgresql://amemo:amemo@127.0.0.1:5435/amemo
|
|
321
|
+
.venv/bin/amemo serve # http://127.0.0.1:8767; --host and --port change the address
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
- **Local trust.** By default the service listens on `127.0.0.1` only. It answers only to its own name in `Host`
|
|
325
|
+
(`127.0.0.1`, `localhost`, `[::1]`, the bind address) and only to its own page or `AMEMO_UI_ORIGINS` in `Origin`;
|
|
326
|
+
writes take JSON only. The web UI gets a person cookie, new on every start. When you type the address into the
|
|
327
|
+
browser, the page itself gets no cookie: the UI's first `fetch` to `/api/v1/session` receives it. Requests
|
|
328
|
+
without the cookie (scripts, agents) write as a model, and their writes go through the checks.
|
|
329
|
+
- **Shared service.** With `AMEMO_TOKENS` (a `0600` file with token hashes) every API request needs
|
|
330
|
+
`Authorization: Bearer <token>`. A token without `write = true` is read-only: writes answer `403 read_only`.
|
|
331
|
+
`/health` and the UI's static files stay public. A `--host` other than loopback without tokens is refused;
|
|
332
|
+
`--no-auth` allows it for a closed network and prints a warning.
|
|
333
|
+
- **Behind a name.** With `--host 0.0.0.0` (for example, in docker compose) list the names clients use in
|
|
334
|
+
`AMEMO_ALLOWED_HOSTS`: `AMEMO_ALLOWED_HOSTS=amemo` accepts `Host: amemo:8767`. List only names you control: a name a browser
|
|
335
|
+
can resolve to someone else's address opens the service to DNS rebinding.
|
|
336
|
+
- **State.** Bundles and uploads live in `AMEMO_STATE` (`~/.local/state/amemo`) for 24 hours, up to
|
|
337
|
+
`AMEMO_STATE_QUOTA` per owner (10 GiB). The query log is `AMEMO_STATE/queries.jsonl`; `AMEMO_QUERY_LOG=off` turns
|
|
338
|
+
it off. The service writes its own log to stderr.
|
|
339
|
+
- **Web UI.** `/ui` needs the web package: `pip install 'amemo[web]'`. Without it `/ui` answers 404 with a hint,
|
|
340
|
+
and the API works as usual.
|
|
341
|
+
|
|
342
|
+
## Writing from the web
|
|
343
|
+
|
|
344
|
+
A person writes in the web UI: a form for each kind, their own kinds, file uploads and tasks for agents.
|
|
345
|
+
|
|
346
|
+
- **Forms from templates.** `GET /templates` gives every atom kind and entity class a form, with or without its
|
|
347
|
+
own template. Fields come from the template's rules, so the form asks for what `check.submit` will check. An
|
|
348
|
+
optional `ui` block in a pack template sets the body, the widgets and the labels of fields; it does not change
|
|
349
|
+
the checks:
|
|
350
|
+
|
|
351
|
+
```yaml
|
|
352
|
+
code: reading
|
|
353
|
+
version: 1.0.0
|
|
354
|
+
target: atom
|
|
355
|
+
applies_to: reading
|
|
356
|
+
fields:
|
|
357
|
+
- {path: about, required: true, entity_classes: [artifact]}
|
|
358
|
+
- {path: content.values, required: true}
|
|
359
|
+
ui:
|
|
360
|
+
body: none # wanted | required | none
|
|
361
|
+
widgets: [sources, values_table] # sources, links, around, values_table, similar, children
|
|
362
|
+
fields:
|
|
363
|
+
- {path: "content.values[property=humidity]", label: {ru: Влажность, en: Air humidity}}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
- **My kinds.** A person adds a kind of their own from the web (`PUT /packs/mine/templates/{code}`). It lives in
|
|
367
|
+
the database as the `mine` pack and passes the same checks as a pack from files, and so do its records. Every
|
|
368
|
+
edit is a new version of the template; a code taken by a file pack is refused (`409 template_exists`). A kind
|
|
369
|
+
can be deleted only while it has no records (`409 template_in_use`). Only a person edits and deletes kinds; a
|
|
370
|
+
model gets `403 forbidden`. `POST /bundles/export` with `with_packs` takes your kinds along; the import adds
|
|
371
|
+
the missing ones, and the same code with other content is a `bundle_pack_conflict`.
|
|
372
|
+
- **Uploads and search.** `POST /objects` (multipart `file`, optional `path`) puts a file into the documents
|
|
373
|
+
folder `AMEMO_DOCS`, by default as `Загрузки/<file name>`, and parses it. The same file at that path answers
|
|
374
|
+
`200`, another file there is `409 object_path_taken` and stays untouched, and without `AMEMO_DOCS` the answer is
|
|
375
|
+
`503 docs_unavailable`. `GET /objects?q=` finds objects by part of the name or path.
|
|
376
|
+
- **Tasks for agents.** In the web a person asks an agent to parse a file (`POST /tasks`); a repeat returns the
|
|
377
|
+
same task. The agent picks it up over MCP: `amemo_tasks` lists open tasks and its own, `amemo_task_claim` takes one
|
|
378
|
+
with a 30-minute lease, `amemo_task_release` gives it back, and `amemo_task_done` closes it with the result
|
|
379
|
+
(links to the records it wrote), and `amemo_task_cancel` cancels a task it set. An expired lease frees the task for
|
|
380
|
+
another agent. `GET /tasks` and `amemo tasks list` show open and claimed tasks, newest first (`--state done` for
|
|
381
|
+
the closed ones); `amemo tasks cancel ID` cancels one (the one who set it, or a person).
|
|
382
|
+
|
|
383
|
+
## Docker compose
|
|
384
|
+
|
|
385
|
+
`docker-compose.yml` runs the database and the service; settings live in `.env` (copy `.env.example`), the file
|
|
386
|
+
itself holds no secrets.
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
cp .env.example .env # set AMEMO_PG_PASSWORD and AMEMO_AGENT
|
|
390
|
+
docker compose up -d --wait amemo # PostgreSQL 18 + pgvector and the service on http://127.0.0.1:8767
|
|
391
|
+
docker compose exec amemo amemo doctor
|
|
392
|
+
docker compose --profile web up -d --wait amemo-web # the same with the web UI at /ui (instead of amemo)
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
- **Inside and outside.** The service listens on `0.0.0.0:8767` inside the compose network and is published only
|
|
396
|
+
on `127.0.0.1:8767`. `AMEMO_ALLOWED_HOSTS=amemo` lets other containers call it as `http://amemo:8767`.
|
|
397
|
+
- **Login.** Without `AMEMO_TOKENS` the service runs with `--no-auth`: anyone who reaches the port writes as a model,
|
|
398
|
+
so keep it on `127.0.0.1`. For a shared service issue a token
|
|
399
|
+
(`docker compose exec amemo amemo token create person:me --file /var/lib/amemo/state/tokens`), set
|
|
400
|
+
`AMEMO_TOKENS=/var/lib/amemo/state/tokens` in `.env` and restart.
|
|
401
|
+
- **Volumes.** `amemo-pg` (database), `amemo-objects` (object store), `amemo-state` (bundles, uploads, query log,
|
|
402
|
+
tokens), `amemo-backups` (`docker compose exec amemo amemo backup create`; the image has the PostgreSQL 18 client).
|
|
403
|
+
`AMEMO_DOCS_DIR` mounts your documents folder read-only.
|
|
404
|
+
- **Health.** Both the image and compose check `/health`; `docker compose up --wait` waits for it.
|
|
405
|
+
- **Profile `web`.** `amemo-web` is the same service built with `amemo[web]`. It shares the port and the volumes, so
|
|
406
|
+
run either `amemo` or `amemo-web`.
|
|
407
|
+
- **Image.** `docker build -t amemo .` (target `service`) or `--target web`; `--build-arg AMEMO_EXTRAS=embed` adds
|
|
408
|
+
the local embedding model.
|
|
409
|
+
|
|
410
|
+
## CLI
|
|
411
|
+
|
|
412
|
+
`pip install -e .` puts the `amemo` command in the venv. It talks to the database directly (`AMEMO_DSN` or
|
|
413
|
+
`amemo init --dsn …`, which remembers the DSN in `~/.config/amemo/amemo.toml`) and does not need the service.
|
|
414
|
+
|
|
415
|
+
```
|
|
416
|
+
amemo init [--dsn DSN] database and object store
|
|
417
|
+
amemo serve [--host --port --no-auth] HTTP API, /mcp and the web UI
|
|
418
|
+
amemo mcp [--agent model:NAME] MCP server on stdio, for an agent
|
|
419
|
+
amemo search QUERY [--kind --limit] search, step 1
|
|
420
|
+
amemo timeline REF what is around a record, step 2
|
|
421
|
+
amemo get REF... full records, step 3
|
|
422
|
+
amemo card REF entity card
|
|
423
|
+
amemo submit FILE.json write a batch (same body as POST /submit; "-" reads stdin)
|
|
424
|
+
amemo review list|claim|release|resolve|stats
|
|
425
|
+
amemo folders tree|create|delete|rename|move
|
|
426
|
+
amemo export|inspect|import FILE.zip bundles (import is a dry run without --apply)
|
|
427
|
+
amemo backup create|list|restore|verify
|
|
428
|
+
amemo tasks list|cancel tasks for agents
|
|
429
|
+
amemo token create|list|revoke tokens of a shared service
|
|
430
|
+
amemo base [name [NAME]] database summary and name
|
|
431
|
+
amemo doctor [--project DIR] database, store, model, documents, agents, service
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
- **Output.** Text for people, or `--json` with the same object the HTTP API returns for that operation.
|
|
435
|
+
An error names what to do next: «set AMEMO_DSN or run amemo init».
|
|
436
|
+
- **Exit codes.** `0` success, `1` environment (no database, no store), `2` wrong call, `3` ambiguous text
|
|
437
|
+
instead of a reference (the candidates are printed), `4` not found, `5` the check rejected the batch (the
|
|
438
|
+
report is printed), `6` conflict (item already resolved, folder taken, bundle collision).
|
|
439
|
+
- **Who writes.** In a plain terminal it is the person `AMEMO_AGENT` (by default `person:` and your OS user name).
|
|
440
|
+
When the environment belongs to an agent, the command runs as a model: `CLAUDECODE=1` — `model:claude-code`,
|
|
441
|
+
the Codex sandbox variables — `model:codex`, `AMEMO_AS_MODEL=name` — `model:name`. `--agent` sets the agent
|
|
442
|
+
explicitly, but never `person:` inside an agent's environment.
|
|
443
|
+
- **Tokens.** `amemo token create person:ann` (or `model:…`, `--read-only`) prints the token once and stores only
|
|
444
|
+
its hash in `AMEMO_TOKENS`. Only a person issues and revokes tokens.
|
|
445
|
+
- **Backups.** A model may run `amemo backup create`, but without rotation: `--keep` and `restore` are for people.
|
|
446
|
+
- **Debugging.** `AMEMO_DEBUG=1` adds the traceback of an internal error to stderr.
|
|
447
|
+
|
|
448
|
+
## MCP
|
|
449
|
+
|
|
450
|
+
The MCP server gives an agent 19 tools over the same operations as the HTTP API:
|
|
451
|
+
|
|
452
|
+
| Tool | What it does |
|
|
453
|
+
|---|---|
|
|
454
|
+
| `amemo_search(query, kind?, limit?)` | Step 1: short hits `{ref, label, kind, status, snippet}` and a `retrieval_id` |
|
|
455
|
+
| `amemo_similar(query \| ref, limit?)` | Similar records, in the same form |
|
|
456
|
+
| `amemo_timeline(ref)` | Step 2: recent events of the record and its neighbors |
|
|
457
|
+
| `amemo_get(refs)` | Step 3: full records with body, sources, status and open items |
|
|
458
|
+
| `amemo_card(ref)` | Entity card `amem.card/1` |
|
|
459
|
+
| `amemo_submit(proposals, links?, reason?)` | Write a batch through `check.submit` |
|
|
460
|
+
| `amemo_helpful(retrieval_id, refs)` | Mark the atoms that helped |
|
|
461
|
+
| `amemo_move(ref, folder)` | Move a record to an existing folder |
|
|
462
|
+
| `amemo_object_status(path)`, `amemo_mark_object(path, status, note?)` | What has been atomized from a document |
|
|
463
|
+
| `amemo_review_claim`, `amemo_review_release`, `amemo_review_resolve` | For the semantic reviewer |
|
|
464
|
+
| `amemo_tasks(object?, limit?)` | Agent tasks: open ones and those you hold |
|
|
465
|
+
| `amemo_task_claim(task?, kind?)`, `amemo_task_release(task)`, `amemo_task_done(task, result?)`, `amemo_task_cancel(task)` | Take a task with a lease, give it back, close it with the result, cancel your own |
|
|
466
|
+
| `amemo_vocab(scheme)` | Vocabulary codes with labels |
|
|
467
|
+
|
|
468
|
+
Search goes in three steps so the agent reads only what it needs: `amemo_search` gives short cards,
|
|
469
|
+
`amemo_timeline` shows what is around a hit, and `amemo_get` returns the full records it picked. Text that matches
|
|
470
|
+
several records without a clear leader comes back as `ambiguous: true` with candidates, not as the first match.
|
|
471
|
+
|
|
472
|
+
**Over stdio.** `amemo mcp` runs one session per process with its own small connection pool. Claude Code:
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
claude mcp add amemo --env AMEMO_DSN=postgresql://amemo:amemo@127.0.0.1:5435/amemo -- amemo mcp --agent model:claude-code
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
or in the project's `.mcp.json`:
|
|
479
|
+
|
|
480
|
+
```json
|
|
481
|
+
{
|
|
482
|
+
"mcpServers": {
|
|
483
|
+
"amemo": {
|
|
484
|
+
"command": "amemo",
|
|
485
|
+
"args": ["mcp", "--agent", "model:claude-code"],
|
|
486
|
+
"env": { "AMEMO_DSN": "postgresql://amemo:amemo@127.0.0.1:5435/amemo" }
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
Codex CLI, in `~/.codex/config.toml` (the DSN stays in `amemo.toml` or in the environment, not in this file):
|
|
493
|
+
|
|
494
|
+
```toml
|
|
495
|
+
[mcp_servers.amemo]
|
|
496
|
+
command = "amemo"
|
|
497
|
+
args = ["mcp", "--agent", "model:codex"]
|
|
498
|
+
env_vars = ["AMEMO_DSN", "AMEMO_CONFIG"]
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
**Over HTTP.** A running service serves the same tools at `/mcp` (Streamable HTTP):
|
|
502
|
+
|
|
503
|
+
```bash
|
|
504
|
+
claude mcp add --transport http amemo http://127.0.0.1:8767/mcp --header "X-Amemo-Agent: model:claude-code"
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
```toml
|
|
508
|
+
[mcp_servers.amemo]
|
|
509
|
+
url = "http://127.0.0.1:8767/mcp"
|
|
510
|
+
http_headers = { "X-Amemo-Agent" = "model:codex" }
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
- **Always a model.** An MCP client writes as `model:…`, and its writes go through the checks. Over stdio the
|
|
514
|
+
agent comes from `--agent model:…` or the agent's environment; over `/mcp` from the `X-Amemo-Agent` header,
|
|
515
|
+
otherwise from the token's model agent, otherwise `AMEMO_MODEL_AGENT` (`model:local`). `person:` is refused.
|
|
516
|
+
- **Shared service.** With `AMEMO_TOKENS`, `/mcp` needs `Authorization: Bearer <token>` like the API; a read-only
|
|
517
|
+
token can search, and write tools answer `read_only`. A header that disagrees with the token's model agent is
|
|
518
|
+
refused.
|
|
519
|
+
- **Usefulness.** `amemo_helpful` marks atoms only from a search of the same session (or, for clients without
|
|
520
|
+
sessions, of the same agent and login); someone else's `retrieval_id` answers `forbidden`.
|
|
521
|
+
- **Query log.** Explicit searches (`amemo search`, `amemo_search`, `amemo_similar`) add a line to
|
|
522
|
+
`AMEMO_STATE/queries.jsonl`, like the service's `/search`; reading steps do not.
|
|
523
|
+
|
|
524
|
+
## Agent memory
|
|
525
|
+
|
|
526
|
+
`amemo install claude-code` and `amemo install codex` connect amemo to an agent, globally or with `--project DIR`.
|
|
527
|
+
`amemo uninstall …` removes exactly what was installed; `--dry-run` shows the changes first.
|
|
528
|
+
|
|
529
|
+
| | Claude Code | Codex CLI |
|
|
530
|
+
|---|---|---|
|
|
531
|
+
| MCP server | `claude mcp add --scope user` (or `~/.claude.json`); project — `.mcp.json` | `[mcp_servers.amemo]` in `~/.codex/config.toml`; project — `.codex/config.toml` |
|
|
532
|
+
| Hooks | `SessionStart`, `UserPromptSubmit`, `PreCompact` in `settings.json` | `SessionStart`, `UserPromptSubmit` in `hooks.json` |
|
|
533
|
+
| Skills | `~/.claude/skills/amemo-*`; project — `.claude/skills/` | `~/.agents/skills/`; project — `.agents/skills/`, plus a block in `AGENTS.md` |
|
|
534
|
+
|
|
535
|
+
- **Hooks.** `amemo hook SessionStart` adds 3–5 verified atoms about the project (by the git root name) and a
|
|
536
|
+
reminder; `UserPromptSubmit` adds confident hits for the prompt («amemo first»); `PreCompact` asks the summary
|
|
537
|
+
to list findings worth saving. A hook reads the database directly, fits in 1.5 s, and on any failure answers
|
|
538
|
+
nothing with exit code 0. Text from memory comes framed as data with its review status, not as instructions.
|
|
539
|
+
- **Skills.** `amemo-remember` (write 1–3 atoms at the end of a task), `amemo-answer` (answer from memory with
|
|
540
|
+
sources), `amemo-verify` (the semantic reviewer, level 5).
|
|
541
|
+
- **Who writes.** The installer sets `AMEMO_AS_MODEL` for the MCP server and the hooks, so the agent writes as
|
|
542
|
+
`model:claude-code` or `model:codex`, never as you.
|
|
543
|
+
- **Edits only its own.** JSON keys `amemo` and hook entries with `amemo hook`, a `# amemo:begin … # amemo:end` block in
|
|
544
|
+
TOML, a `<!-- amemo:begin … -->` block in Markdown. Before the first edit a file is copied to `<file>.amemo-backup`.
|
|
545
|
+
- **Codex.** Codex passes an MCP server only the variables listed in `env_vars`; the installer lists `AMEMO_DSN`,
|
|
546
|
+
`AMEMO_CONFIG`, `AMEMO_STATE`, `PGPASSFILE` and `PGSERVICE` and never writes a connection string into
|
|
547
|
+
`config.toml`. Put the database into `amemo.toml` with `amemo init --dsn …` (or into those variables). Codex reads
|
|
548
|
+
project files only in a trusted project.
|
|
549
|
+
- **Project files.** With `--project` the files get a bare `amemo` command, without this machine's path, so they
|
|
550
|
+
can be committed; `amemo` must be on the agent's `PATH`. A global install uses the full path.
|
|
551
|
+
- **Windows.** Hook commands are POSIX shell lines (`AMEMO_AS_MODEL=… amemo hook …`): they work on Linux, macOS and
|
|
552
|
+
WSL2, not in native Windows shells. Use WSL2.
|
|
553
|
+
- **Check.** `amemo doctor` shows what is installed (globally and for the project of the current folder) and whether
|
|
554
|
+
its command still exists; then `claude mcp list` or `codex mcp list` confirms the agent sees the server.
|
|
555
|
+
|
|
556
|
+
**Scheduling `amemo-verify`.** The semantic reviewer is an agent run on a schedule of your choice, for example
|
|
557
|
+
every night with cron:
|
|
558
|
+
|
|
559
|
+
```cron
|
|
560
|
+
# m h dom mon dow
|
|
561
|
+
30 3 * * * cd ~/notes && claude -p "Use the amemo-verify skill: review up to 20 open semantic items." >> ~/.local/state/amemo/verify.log 2>&1
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
or a systemd user timer:
|
|
565
|
+
|
|
566
|
+
```ini
|
|
567
|
+
# ~/.config/systemd/user/amemo-verify.service
|
|
568
|
+
[Service]
|
|
569
|
+
Type=oneshot
|
|
570
|
+
WorkingDirectory=%h/notes
|
|
571
|
+
ExecStart=/usr/bin/env claude -p "Use the amemo-verify skill: review up to 20 open semantic items."
|
|
572
|
+
|
|
573
|
+
# ~/.config/systemd/user/amemo-verify.timer
|
|
574
|
+
[Timer]
|
|
575
|
+
OnCalendar=*-*-* 03:30
|
|
576
|
+
Persistent=true
|
|
577
|
+
[Install]
|
|
578
|
+
WantedBy=timers.target
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
`systemctl --user enable --now amemo-verify.timer`. The agent needs the amemo MCP server (`amemo install …`).
|
|
582
|
+
|
|
583
|
+
## How it works
|
|
584
|
+
|
|
585
|
+
```mermaid
|
|
586
|
+
flowchart LR
|
|
587
|
+
agents["Agents<br/>MCP · CLI · hooks"] -- propose --> check
|
|
588
|
+
people["People<br/>web UI · CLI"] -- write --> check
|
|
589
|
+
check["check.submit<br/>1 schema and templates<br/>2 quote found in the file<br/>3 common sense<br/>4 duplicates"]
|
|
590
|
+
check -- one transaction --> base[("PostgreSQL + pgvector<br/>atoms · entities · links · revisions")]
|
|
591
|
+
check -. cited files by digest .-> files[("OCFL object store")]
|
|
592
|
+
base --> queue{{"review queue"}}
|
|
593
|
+
queue -- confirm or reject --> people
|
|
594
|
+
base --> search["search recipes<br/>exact_fact · similar<br/>what_changed · around_entity"]
|
|
595
|
+
search -- short cards, then full records --> agents
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
Every write goes through `check.submit`. It checks the whole batch, then writes it in one transaction:
|
|
599
|
+
|
|
600
|
+
1. **Schema and templates:** fields, types, link rules and UCUM units.
|
|
601
|
+
2. **Binding to the source:** the quote is found in the file, the page matches, and the numbers of the
|
|
602
|
+
claim appear in the fragment. Works for PDF, Markdown, xlsx and docx. A scanned PDF page (an image
|
|
603
|
+
without a text layer) is OCR'd with Tesseract through PyMuPDF when its language data is installed
|
|
604
|
+
(`sudo apt install tesseract-ocr tesseract-ocr-rus` on Debian/Ubuntu, `brew install tesseract tesseract-lang`
|
|
605
|
+
on macOS; languages `rus+eng` by default, `AMEMO_OCR_LANGUAGES` to change). A quote matched against OCR
|
|
606
|
+
text gets an `info` finding `quote_ocr` and a softer fuzzy threshold; without Tesseract such pages are
|
|
607
|
+
reported as having no text layer.
|
|
608
|
+
3. **Common sense:** value ranges, units, and contradictions with atoms already in the base.
|
|
609
|
+
4. **Duplicates:** by text and by vector.
|
|
610
|
+
|
|
611
|
+
An error at level 1 or 2 rejects the batch with a report. Otherwise each record gets a status:
|
|
612
|
+
|
|
613
|
+
| Status | Meaning |
|
|
614
|
+
|---|---|
|
|
615
|
+
| `draft` | Passed levels 1–4 without remarks; waits for the semantic review (level 5) |
|
|
616
|
+
| `needs_review` | Has a warning or a high risk; waits for a person |
|
|
617
|
+
| `auto_verified` | Passed all five levels with low risk |
|
|
618
|
+
| `verified` | Confirmed by a person against the source. A person's own record is verified at once |
|
|
619
|
+
| `rejected` | Rejected by a person |
|
|
620
|
+
| `deprecated`, `superseded` | Retired; kept for history |
|
|
621
|
+
|
|
622
|
+
**What an atom looks like** (`amem.atom/1`):
|
|
623
|
+
|
|
624
|
+
```yaml
|
|
625
|
+
kind: fact
|
|
626
|
+
key: pgvector.hnsw_max_dimensions
|
|
627
|
+
label:
|
|
628
|
+
en: pgvector indexes vector with HNSW up to 2,000 dimensions
|
|
629
|
+
ru: "pgvector: индекс HNSW по vector — до 2000 измерений"
|
|
630
|
+
summary_plain:
|
|
631
|
+
en: Longer embeddings need halfvec, up to 4,000.
|
|
632
|
+
ru: Длинные векторы индексируются через halfvec, до 4000.
|
|
633
|
+
content:
|
|
634
|
+
values:
|
|
635
|
+
- {property: max_indexed_dimensions, amount: 2000, unit: "1"}
|
|
636
|
+
sources:
|
|
637
|
+
- path: pgvector/README.md
|
|
638
|
+
selectors: [{type: heading, value: HNSW}]
|
|
639
|
+
quote: vector - up to 2,000 dimensions
|
|
640
|
+
role: primary
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
The agent never sets the status; the checks and people do.
|
|
644
|
+
|
|
645
|
+
**Core concepts**
|
|
646
|
+
|
|
647
|
+
| Concept | What it is |
|
|
648
|
+
|---|---|
|
|
649
|
+
| **Atom** | A small typed claim: fact, term, method, decision, question, lesson, requirement or parameter |
|
|
650
|
+
| **Entity** | What atoms are about: project, document, edition, organization, person, place, software, topic |
|
|
651
|
+
| **Object** | A source file, web page or message. Files are stored once by content digest |
|
|
652
|
+
| **Link** | A typed relation between records, with rules for its ends. Links are retired, never deleted |
|
|
653
|
+
| **Revision** | One change of a record: a canonical body, a content hash, an author and a reason |
|
|
654
|
+
| **Pack** | A domain vocabulary with templates and entity profiles. The core is a pack too |
|
|
655
|
+
| **Bundle** | A portable selection of records (`amem.bundle/1`), with or without files |
|
|
656
|
+
|
|
657
|
+
**Search recipes.** Five named recipes return plain JSON, so agents can work with them directly:
|
|
658
|
+
|
|
659
|
+
| Recipe | Answers |
|
|
660
|
+
|---|---|
|
|
661
|
+
| `exact_fact` | *What exactly is X?* Codes, marks, full text and vectors merged with RRF |
|
|
662
|
+
| `similar` | *What else is like this?* Semantic neighbours of a text or a record |
|
|
663
|
+
| `what_changed` | *What changed since Monday?* Revisions, decisions, statuses, moves and links |
|
|
664
|
+
| `around_entity` | *What do we know around X?* Shortest paths over links, rated by the weakest link; stops at `limit` (300 by default, was unlimited before), `total` counts nodes without it |
|
|
665
|
+
| `overview` | *What does the whole base look like?* Hubs by number of links (`degree`), their neighbours, the edges between them; filter by `project` |
|
|
666
|
+
|
|
667
|
+
Agents mark the atoms that helped them. These marks decay over time and give a small, capped boost in
|
|
668
|
+
ranking. They never change a status or skip a check.
|
|
669
|
+
|
|
670
|
+
## Roadmap
|
|
671
|
+
|
|
672
|
+
- [x] **Core and storage:** LinkML schema, revisions, objects by digest, domain packs
|
|
673
|
+
- [x] **Search engine:** four recipes, RRF ranking, usefulness marks, backups
|
|
674
|
+
- [x] **Automatic checks** (levels 1–4) and a **review queue** with leases, audit and metrics
|
|
675
|
+
- [x] **Ontology:** entity profiles, live cards, portable bundles
|
|
676
|
+
- [x] **Library for the service:** folders, record lists, references by key, object view
|
|
677
|
+
- [x] **HTTP service:** `amemo serve` with the [v1 contract](https://github.com/empty-complete/amemo/blob/v0.1.0a1/docs/api/http-v1.openapi.yaml)
|
|
678
|
+
- [x] **CLI and MCP server:** three-step retrieval, usefulness marks, `<private>` stripping
|
|
679
|
+
- [x] **Agent memory:** Claude Code hooks, Codex CLI, `amemo install`, core skills
|
|
680
|
+
- [x] **Writing from the web:** forms from templates, my kinds, uploads, tasks for agents
|
|
681
|
+
- [x] **Web UI:** all screens, on the service and on mock data; checked end to end against the service
|
|
682
|
+
- [ ] **asearch:** file search as an amemo plugin
|
|
683
|
+
|
|
684
|
+
## Documentation
|
|
685
|
+
|
|
686
|
+
| | |
|
|
687
|
+
|---|---|
|
|
688
|
+
| [Library reference](https://github.com/empty-complete/amemo/blob/v0.1.0a1/docs/library.md) | Data model, search, checks, review queue, cards, bundles, folders, backups |
|
|
689
|
+
| [HTTP API v1](https://github.com/empty-complete/amemo/blob/v0.1.0a1/docs/api/) | The OpenAPI contract between the service and the web UI |
|
|
690
|
+
| [Manifesto](https://github.com/empty-complete/amemo/blob/v0.1.0a1/docs/MANIFESTO.md) | Why amemo exists and what it stands for |
|
|
691
|
+
| [Design specs and plans](https://github.com/empty-complete/amemo/blob/v0.1.0a1/docs/superpowers/) | How each subproject was decided and built |
|
|
692
|
+
|
|
693
|
+
**Good to know**
|
|
694
|
+
|
|
695
|
+
- Schema v1 requires the `ru` slot of every label; other languages are optional. A base kept in English
|
|
696
|
+
writes its English text there, as the sample does. Full-text search is tuned for Russian, and the optional
|
|
697
|
+
local vector model is multilingual.
|
|
698
|
+
- Vectors are local and optional: `pip install -e ".[embed]"` adds fastembed, and a model is downloaded
|
|
699
|
+
only with explicit permission. `AMEMO_BACKEND=none` turns the semantic channel off, and lexical search
|
|
700
|
+
keeps working.
|
|
701
|
+
- Backups need the PostgreSQL 18 client (`pg_dump`, `pg_restore`, `psql`) in `PATH` or in `AMEMO_PG_BIN`.
|
|
702
|
+
|
|
703
|
+
## Development
|
|
704
|
+
|
|
705
|
+
```bash
|
|
706
|
+
python3 -m venv .venv
|
|
707
|
+
.venv/bin/pip install -e ".[dev]"
|
|
708
|
+
|
|
709
|
+
# test databases: PostgreSQL 18 (port 5433) and 16 (port 5434)
|
|
710
|
+
docker compose --profile test up -d --wait db-test db-test-pg16
|
|
711
|
+
|
|
712
|
+
# run the tests (backup tests need the PostgreSQL 18 client)
|
|
713
|
+
.venv/bin/pytest -q
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
To use other ports or servers, set `AMEMO_TEST_DSNS="postgresql://…/test,postgresql://…/test"`. A missing
|
|
717
|
+
test database or client counts as a failure, not a skip. If your environment exports a `PYTHONPATH` from
|
|
718
|
+
other tooling (ROS, for example), it breaks pytest plugin autoloading, so run `env -u PYTHONPATH .venv/bin/pytest -q`.
|
|
719
|
+
|
|
720
|
+
After editing `amemo/schema/amemo_v1.yaml`, regenerate the models:
|
|
721
|
+
|
|
722
|
+
```bash
|
|
723
|
+
.venv/bin/python tools/gen_schema.py # writes amemo/model_v1.py and the JSON Schema
|
|
724
|
+
.venv/bin/python tools/gen_schema.py --check # CI check: generated files are up to date
|
|
725
|
+
```
|
|
726
|
+
Before pushing, enable the privacy check: `git config core.hooksPath tools/hooks`; the hook reads its patterns from `~/.config/amemo/private-patterns.txt` (outside the repository).
|
|
727
|
+
|
|
728
|
+
Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) in English:
|
|
729
|
+
`type(scope): subject`, at most 100 characters, e.g. `fix(service): reject NUL in parameters`. The same
|
|
730
|
+
`core.hooksPath` enables a `commit-msg` hook that checks this when you commit;
|
|
731
|
+
`python3 tools/check_commits.py --revs origin/main..HEAD` checks a range by hand.
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
The web UI lives in [`web/`](https://github.com/empty-complete/amemo/blob/v0.1.0a1/web/) (React 19, HeroUI v3, TanStack). Run `npm run lint`, `npm test` and
|
|
735
|
+
`npm run e2e` there.
|
|
736
|
+
|
|
737
|
+
## Contributing
|
|
738
|
+
|
|
739
|
+
amemo is built in the open. Issues and discussions are welcome. For a larger change, please open an issue
|
|
740
|
+
first so we can agree on the approach. Every pull request runs the `ci` workflow (tests on PostgreSQL 18 and 16)
|
|
741
|
+
and the `commits` workflow (Conventional Commits in English); changes to `web/` also run the `web` workflow.
|
|
742
|
+
|
|
743
|
+
## Built with
|
|
744
|
+
|
|
745
|
+
[PostgreSQL](https://www.postgresql.org/) ·
|
|
746
|
+
[pgvector](https://github.com/pgvector/pgvector) ·
|
|
747
|
+
[LinkML](https://linkml.io/) ·
|
|
748
|
+
[OCFL](https://ocfl.io/) ·
|
|
749
|
+
[PyMuPDF](https://pymupdf.readthedocs.io/) ·
|
|
750
|
+
[fastembed](https://github.com/qdrant/fastembed) ·
|
|
751
|
+
[React](https://react.dev/) ·
|
|
752
|
+
[HeroUI](https://www.heroui.com/) ·
|
|
753
|
+
[force-graph](https://github.com/vasturiano/force-graph)
|
|
754
|
+
|
|
755
|
+
## License
|
|
756
|
+
|
|
757
|
+
MIT, see [LICENSE.txt](https://github.com/empty-complete/amemo/blob/v0.1.0a1/LICENSE.txt).
|
|
758
|
+
|
|
759
|
+
<p align="center"><sub>amemo is a tool for remembering well.</sub></p>
|