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.
Files changed (176) hide show
  1. amemo-0.1.0a1/PKG-INFO +759 -0
  2. amemo-0.1.0a1/README.md +708 -0
  3. amemo-0.1.0a1/amemo/__init__.py +2 -0
  4. amemo-0.1.0a1/amemo/agents/__init__.py +1 -0
  5. amemo-0.1.0a1/amemo/agents/hooks.py +372 -0
  6. amemo-0.1.0a1/amemo/agents/install.py +786 -0
  7. amemo-0.1.0a1/amemo/agents/platforms.py +105 -0
  8. amemo-0.1.0a1/amemo/anthology.py +159 -0
  9. amemo-0.1.0a1/amemo/api.py +1741 -0
  10. amemo-0.1.0a1/amemo/backup.py +745 -0
  11. amemo-0.1.0a1/amemo/bundle.py +1417 -0
  12. amemo-0.1.0a1/amemo/canon.py +162 -0
  13. amemo-0.1.0a1/amemo/check/__init__.py +301 -0
  14. amemo-0.1.0a1/amemo/check/level1.py +588 -0
  15. amemo-0.1.0a1/amemo/check/level2.py +359 -0
  16. amemo-0.1.0a1/amemo/check/level3.py +216 -0
  17. amemo-0.1.0a1/amemo/check/level4.py +204 -0
  18. amemo-0.1.0a1/amemo/check/numbers.py +129 -0
  19. amemo-0.1.0a1/amemo/check/report.py +68 -0
  20. amemo-0.1.0a1/amemo/check/templates.py +86 -0
  21. amemo-0.1.0a1/amemo/check/units.py +45 -0
  22. amemo-0.1.0a1/amemo/cli/__init__.py +213 -0
  23. amemo-0.1.0a1/amemo/cli/__main__.py +4 -0
  24. amemo-0.1.0a1/amemo/cli/commands/__init__.py +29 -0
  25. amemo-0.1.0a1/amemo/cli/commands/agents.py +64 -0
  26. amemo-0.1.0a1/amemo/cli/commands/backup.py +81 -0
  27. amemo-0.1.0a1/amemo/cli/commands/bundles.py +133 -0
  28. amemo-0.1.0a1/amemo/cli/commands/read.py +178 -0
  29. amemo-0.1.0a1/amemo/cli/commands/setup.py +348 -0
  30. amemo-0.1.0a1/amemo/cli/commands/tasks.py +44 -0
  31. amemo-0.1.0a1/amemo/cli/commands/tokens.py +184 -0
  32. amemo-0.1.0a1/amemo/cli/commands/write.py +212 -0
  33. amemo-0.1.0a1/amemo/cli/context.py +213 -0
  34. amemo-0.1.0a1/amemo/cli/errors.py +71 -0
  35. amemo-0.1.0a1/amemo/db.py +257 -0
  36. amemo-0.1.0a1/amemo/embed.py +292 -0
  37. amemo-0.1.0a1/amemo/extract.py +223 -0
  38. amemo-0.1.0a1/amemo/folders.py +292 -0
  39. amemo-0.1.0a1/amemo/forms.py +211 -0
  40. amemo-0.1.0a1/amemo/ids.py +55 -0
  41. amemo-0.1.0a1/amemo/journal.py +48 -0
  42. amemo-0.1.0a1/amemo/lookup.py +591 -0
  43. amemo-0.1.0a1/amemo/mine.py +528 -0
  44. amemo-0.1.0a1/amemo/model_v1.py +2587 -0
  45. amemo-0.1.0a1/amemo/objects.py +636 -0
  46. amemo-0.1.0a1/amemo/ocfl_store.py +353 -0
  47. amemo-0.1.0a1/amemo/packs/core/pack.yaml +9 -0
  48. amemo-0.1.0a1/amemo/packs/core/skills/amemo-answer/SKILL.md +54 -0
  49. amemo-0.1.0a1/amemo/packs/core/skills/amemo-remember/SKILL.md +61 -0
  50. amemo-0.1.0a1/amemo/packs/core/skills/amemo-verify/SKILL.md +58 -0
  51. amemo-0.1.0a1/amemo/packs/core/templates/decision.yaml +11 -0
  52. amemo-0.1.0a1/amemo/packs/core/templates/has_label.yaml +10 -0
  53. amemo-0.1.0a1/amemo/packs/core/templates/has_source.yaml +15 -0
  54. amemo-0.1.0a1/amemo/packs/core/templates/has_value.yaml +20 -0
  55. amemo-0.1.0a1/amemo/packs/core/templates/note.yaml +8 -0
  56. amemo-0.1.0a1/amemo/packs/core/templates/parameter.yaml +12 -0
  57. amemo-0.1.0a1/amemo/packs/core/vocab/entity_class.yaml +56 -0
  58. amemo-0.1.0a1/amemo/packs/core/vocab/kind.yaml +66 -0
  59. amemo-0.1.0a1/amemo/packs/core/vocab/rel.yaml +145 -0
  60. amemo-0.1.0a1/amemo/packs/core/vocab/selector_type.yaml +81 -0
  61. amemo-0.1.0a1/amemo/packs.py +454 -0
  62. amemo-0.1.0a1/amemo/private.py +99 -0
  63. amemo-0.1.0a1/amemo/profile.py +235 -0
  64. amemo-0.1.0a1/amemo/recipes.py +823 -0
  65. amemo-0.1.0a1/amemo/records.py +681 -0
  66. amemo-0.1.0a1/amemo/review.py +584 -0
  67. amemo-0.1.0a1/amemo/schema/amemo_v1.schema.json +3213 -0
  68. amemo-0.1.0a1/amemo/schema/amemo_v1.yaml +1258 -0
  69. amemo-0.1.0a1/amemo/search.py +454 -0
  70. amemo-0.1.0a1/amemo/service/__init__.py +8 -0
  71. amemo-0.1.0a1/amemo/service/__main__.py +4 -0
  72. amemo-0.1.0a1/amemo/service/app.py +151 -0
  73. amemo-0.1.0a1/amemo/service/http-v1.openapi.yaml +1259 -0
  74. amemo-0.1.0a1/amemo/service/http_v1.py +718 -0
  75. amemo-0.1.0a1/amemo/service/mcp.py +659 -0
  76. amemo-0.1.0a1/amemo/service/problems.py +179 -0
  77. amemo-0.1.0a1/amemo/service/server.py +101 -0
  78. amemo-0.1.0a1/amemo/service/session.py +198 -0
  79. amemo-0.1.0a1/amemo/service/ui.py +151 -0
  80. amemo-0.1.0a1/amemo/settings.py +362 -0
  81. amemo-0.1.0a1/amemo/sources.py +471 -0
  82. amemo-0.1.0a1/amemo/sql/0001_core.sql +287 -0
  83. amemo-0.1.0a1/amemo/sql/0002_objects.sql +137 -0
  84. amemo-0.1.0a1/amemo/sql/0003_packs.sql +26 -0
  85. amemo-0.1.0a1/amemo/sql/0004_hub.sql +6 -0
  86. amemo-0.1.0a1/amemo/sql/0005_search.sql +99 -0
  87. amemo-0.1.0a1/amemo/sql/0006_check.sql +110 -0
  88. amemo-0.1.0a1/amemo/sql/0007_ontology.sql +42 -0
  89. amemo-0.1.0a1/amemo/sql/0008_folders.sql +43 -0
  90. amemo-0.1.0a1/amemo/sql/0009_record_list.sql +11 -0
  91. amemo-0.1.0a1/amemo/sql/0010_graph_overview.sql +10 -0
  92. amemo-0.1.0a1/amemo/sql/0011_object_text_ocr.sql +15 -0
  93. amemo-0.1.0a1/amemo/sql/0012_tasks.sql +32 -0
  94. amemo-0.1.0a1/amemo/sql/0013_rename_amemo.sql +98 -0
  95. amemo-0.1.0a1/amemo/tasks.py +274 -0
  96. amemo-0.1.0a1/amemo/usefulness.py +155 -0
  97. amemo-0.1.0a1/amemo/vocab.py +52 -0
  98. amemo-0.1.0a1/amemo.egg-info/PKG-INFO +759 -0
  99. amemo-0.1.0a1/amemo.egg-info/SOURCES.txt +174 -0
  100. amemo-0.1.0a1/amemo.egg-info/dependency_links.txt +1 -0
  101. amemo-0.1.0a1/amemo.egg-info/entry_points.txt +2 -0
  102. amemo-0.1.0a1/amemo.egg-info/requires.txt +34 -0
  103. amemo-0.1.0a1/amemo.egg-info/top_level.txt +1 -0
  104. amemo-0.1.0a1/pyproject.toml +84 -0
  105. amemo-0.1.0a1/setup.cfg +4 -0
  106. amemo-0.1.0a1/tests/test_anthology.py +85 -0
  107. amemo-0.1.0a1/tests/test_backup.py +344 -0
  108. amemo-0.1.0a1/tests/test_backup_restore.py +448 -0
  109. amemo-0.1.0a1/tests/test_bundle_export.py +413 -0
  110. amemo-0.1.0a1/tests/test_bundle_import.py +979 -0
  111. amemo-0.1.0a1/tests/test_bundle_lowlevel.py +235 -0
  112. amemo-0.1.0a1/tests/test_canon.py +193 -0
  113. amemo-0.1.0a1/tests/test_check_commits.py +70 -0
  114. amemo-0.1.0a1/tests/test_check_level1.py +460 -0
  115. amemo-0.1.0a1/tests/test_check_level2.py +527 -0
  116. amemo-0.1.0a1/tests/test_check_level3.py +220 -0
  117. amemo-0.1.0a1/tests/test_check_level4.py +218 -0
  118. amemo-0.1.0a1/tests/test_check_numbers.py +124 -0
  119. amemo-0.1.0a1/tests/test_check_schema.py +85 -0
  120. amemo-0.1.0a1/tests/test_check_sql.py +252 -0
  121. amemo-0.1.0a1/tests/test_check_submit.py +517 -0
  122. amemo-0.1.0a1/tests/test_check_templates.py +229 -0
  123. amemo-0.1.0a1/tests/test_check_units.py +38 -0
  124. amemo-0.1.0a1/tests/test_cli.py +772 -0
  125. amemo-0.1.0a1/tests/test_db.py +406 -0
  126. amemo-0.1.0a1/tests/test_embed.py +325 -0
  127. amemo-0.1.0a1/tests/test_extract.py +186 -0
  128. amemo-0.1.0a1/tests/test_folders.py +571 -0
  129. amemo-0.1.0a1/tests/test_forms.py +172 -0
  130. amemo-0.1.0a1/tests/test_hooks.py +300 -0
  131. amemo-0.1.0a1/tests/test_hub.py +50 -0
  132. amemo-0.1.0a1/tests/test_ids.py +76 -0
  133. amemo-0.1.0a1/tests/test_install.py +517 -0
  134. amemo-0.1.0a1/tests/test_lookup_lists.py +429 -0
  135. amemo-0.1.0a1/tests/test_mcp.py +565 -0
  136. amemo-0.1.0a1/tests/test_mine.py +322 -0
  137. amemo-0.1.0a1/tests/test_object_view.py +208 -0
  138. amemo-0.1.0a1/tests/test_objects.py +446 -0
  139. amemo-0.1.0a1/tests/test_ocfl_store.py +384 -0
  140. amemo-0.1.0a1/tests/test_ontology_core.py +39 -0
  141. amemo-0.1.0a1/tests/test_ontology_level1.py +248 -0
  142. amemo-0.1.0a1/tests/test_ontology_packs.py +111 -0
  143. amemo-0.1.0a1/tests/test_ontology_profile_rules.py +94 -0
  144. amemo-0.1.0a1/tests/test_ontology_schema.py +66 -0
  145. amemo-0.1.0a1/tests/test_ontology_sql.py +68 -0
  146. amemo-0.1.0a1/tests/test_packs.py +175 -0
  147. amemo-0.1.0a1/tests/test_private_secrets.py +90 -0
  148. amemo-0.1.0a1/tests/test_profile_card.py +349 -0
  149. amemo-0.1.0a1/tests/test_profile_projection.py +90 -0
  150. amemo-0.1.0a1/tests/test_recipes_graph.py +582 -0
  151. amemo-0.1.0a1/tests/test_recipes_history.py +231 -0
  152. amemo-0.1.0a1/tests/test_recipes_search.py +395 -0
  153. amemo-0.1.0a1/tests/test_records.py +791 -0
  154. amemo-0.1.0a1/tests/test_review.py +647 -0
  155. amemo-0.1.0a1/tests/test_review_sources.py +283 -0
  156. amemo-0.1.0a1/tests/test_review_stats.py +187 -0
  157. amemo-0.1.0a1/tests/test_rrf.py +127 -0
  158. amemo-0.1.0a1/tests/test_search_lexical.py +154 -0
  159. amemo-0.1.0a1/tests/test_search_projection.py +129 -0
  160. amemo-0.1.0a1/tests/test_search_schema.py +69 -0
  161. amemo-0.1.0a1/tests/test_search_text.py +92 -0
  162. amemo-0.1.0a1/tests/test_service_4d.py +336 -0
  163. amemo-0.1.0a1/tests/test_service_bundles.py +596 -0
  164. amemo-0.1.0a1/tests/test_service_contract.py +501 -0
  165. amemo-0.1.0a1/tests/test_service_core.py +617 -0
  166. amemo-0.1.0a1/tests/test_service_lib.py +456 -0
  167. amemo-0.1.0a1/tests/test_service_read.py +719 -0
  168. amemo-0.1.0a1/tests/test_service_ui.py +181 -0
  169. amemo-0.1.0a1/tests/test_service_write.py +894 -0
  170. amemo-0.1.0a1/tests/test_sources.py +403 -0
  171. amemo-0.1.0a1/tests/test_sql_schema.py +161 -0
  172. amemo-0.1.0a1/tests/test_tasks.py +223 -0
  173. amemo-0.1.0a1/tests/test_usefulness.py +298 -0
  174. amemo-0.1.0a1/tests/test_v1_generated.py +12 -0
  175. amemo-0.1.0a1/tests/test_v1_schema.py +189 -0
  176. 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>