hermes-mnemostack 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hermes_mnemostack-0.9.0/LICENSE +21 -0
- hermes_mnemostack-0.9.0/PKG-INFO +205 -0
- hermes_mnemostack-0.9.0/README.md +176 -0
- hermes_mnemostack-0.9.0/pyproject.toml +81 -0
- hermes_mnemostack-0.9.0/setup.cfg +4 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack/__init__.py +5 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack/cli.py +790 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack/client.py +458 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack/config.py +193 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack/install.py +512 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack/plugin/README.md +20 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack/plugin/__init__.py +39 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack/plugin/plugin.yaml +12 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack/provider.py +1034 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack.egg-info/PKG-INFO +205 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack.egg-info/SOURCES.txt +25 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack.egg-info/dependency_links.txt +1 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack.egg-info/entry_points.txt +5 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack.egg-info/requires.txt +7 -0
- hermes_mnemostack-0.9.0/src/hermes_mnemostack.egg-info/top_level.txt +1 -0
- hermes_mnemostack-0.9.0/tests/test_cli.py +735 -0
- hermes_mnemostack-0.9.0/tests/test_client_local.py +113 -0
- hermes_mnemostack-0.9.0/tests/test_client_remote.py +157 -0
- hermes_mnemostack-0.9.0/tests/test_config.py +82 -0
- hermes_mnemostack-0.9.0/tests/test_discovery.py +78 -0
- hermes_mnemostack-0.9.0/tests/test_install.py +982 -0
- hermes_mnemostack-0.9.0/tests/test_provider.py +1322 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 hermes-mnemostack contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hermes-mnemostack
|
|
3
|
+
Version: 0.9.0
|
|
4
|
+
Summary: mnemostack memory provider for hermes-agent: persistent agent memory over a mnemostack service or a local Qdrant
|
|
5
|
+
Author: hermes-mnemostack contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/udjin-labs/hermes-mnemostack
|
|
8
|
+
Project-URL: Repository, https://github.com/udjin-labs/hermes-mnemostack
|
|
9
|
+
Project-URL: Issues, https://github.com/udjin-labs/hermes-mnemostack/issues
|
|
10
|
+
Keywords: hermes-agent,mnemostack,memory,agent,rag,recall
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: hermes-agent>=0.19
|
|
23
|
+
Requires-Dist: mnemostack>=2.2
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff>=0.1; extra == "dev"
|
|
27
|
+
Requires-Dist: mypy>=1.7; extra == "dev"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# hermes-mnemostack
|
|
31
|
+
|
|
32
|
+
**Status: first public release (0.9.0).** The provider is wired end to end (recall
|
|
33
|
+
injection, turn capture, tools, configuration, CLI), covered by tests, and
|
|
34
|
+
hermes-agent's own discovery loads it from a clean install — but it has not yet been
|
|
35
|
+
driven through a live agent session, which is why this is 0.9 and not 1.0.
|
|
36
|
+
|
|
37
|
+
[mnemostack](https://github.com/udjin-labs/mnemostack) memory provider for
|
|
38
|
+
[hermes-agent](https://github.com/NousResearch/hermes-agent): persistent agent memory
|
|
39
|
+
over either transport.
|
|
40
|
+
|
|
41
|
+
- **remote HTTP** — a shared mnemostack service (`mnemostack serve --auth`). The
|
|
42
|
+
tenant is resolved from the service key, never asserted by this client, so it is a
|
|
43
|
+
real authorization boundary. Recall uses whatever the deployment configures — vector,
|
|
44
|
+
lexical, temporal, graph — and the full write lifecycle is available: remember,
|
|
45
|
+
invalidate (soft retraction), delete.
|
|
46
|
+
- **local SDK** — mnemostack as a library against your own Qdrant. Recall is
|
|
47
|
+
vector-only in this mode today; profile/user scoping rides the library's tenant
|
|
48
|
+
mechanism, which is isolation but **not** an authorization boundary (same machine,
|
|
49
|
+
same trust domain — see [SECURITY.md](SECURITY.md)).
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
Requires mnemostack >= 2.2 and hermes-agent >= 0.19.
|
|
54
|
+
|
|
55
|
+
**hermes-agent 0.19** discovers memory providers by scanning
|
|
56
|
+
`$HERMES_HOME/plugins/<name>/`; it does not read pip entry points. So the package
|
|
57
|
+
ships a small directory shim and a command that puts it where Hermes looks:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install hermes-mnemostack
|
|
61
|
+
hermes-mnemostack install # copies the shim into the ACTIVE Hermes profile
|
|
62
|
+
hermes memory setup mnemostack
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`install` resolves the Hermes home the way Hermes does (so it lands in the active
|
|
66
|
+
profile, not a hardcoded `~/.hermes`), refuses to overwrite a different plugin of the
|
|
67
|
+
same name, and — because 0.19 logs a failed plugin import at debug level and then
|
|
68
|
+
silently omits the provider — finishes by asking Hermes's own discovery whether it
|
|
69
|
+
can actually see and load it. `--dry-run` shows what it would write.
|
|
70
|
+
|
|
71
|
+
**hermes-agent 0.20 and later** discovers the pip entry point on its own:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install hermes-mnemostack
|
|
75
|
+
hermes memory setup mnemostack
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
If the shim is already there it stays harmless: it resolves to the same provider the
|
|
79
|
+
entry point does. (Which of the two Hermes prefers when both are present is untested
|
|
80
|
+
here — 0.20 is git-only at the time of writing — so if you want only one, remove
|
|
81
|
+
`$HERMES_HOME/plugins/mnemostack/`.)
|
|
82
|
+
|
|
83
|
+
`hermes-mnemostack doctor` tells you which situation you are in by asking hermes's own
|
|
84
|
+
discovery rather than by comparing version numbers.
|
|
85
|
+
|
|
86
|
+
## Configure
|
|
87
|
+
|
|
88
|
+
Behavioral settings live in `$HERMES_HOME/mnemostack.json` (written by
|
|
89
|
+
`hermes memory setup`); **secrets live only in the environment**. Environment variables
|
|
90
|
+
provide defaults and the JSON file overrides them. An unknown key in the JSON file is
|
|
91
|
+
rejected loudly — a typo that silently does nothing is the worst failure mode for
|
|
92
|
+
memory configuration.
|
|
93
|
+
|
|
94
|
+
| Key | Env | Default | Meaning |
|
|
95
|
+
| --- | --- | --- | --- |
|
|
96
|
+
| `mode` | `MNEMOSTACK_MODE` | `remote` | `remote` (HTTP service) or `local` (library) |
|
|
97
|
+
| `base_url` | `MNEMOSTACK_BASE_URL` | — | remote: the service root, e.g. `https://memory.example:8080` |
|
|
98
|
+
| `timeout` | `MNEMOSTACK_TIMEOUT` | `30.0` | remote: HTTP timeout in seconds |
|
|
99
|
+
| `qdrant_url` | `MNEMOSTACK_QDRANT_URL` | `http://localhost:6333` | local: Qdrant endpoint |
|
|
100
|
+
| `collection` | `MNEMOSTACK_COLLECTION` | `hermes-memory` | local: collection name |
|
|
101
|
+
| `embedding_provider` | `MNEMOSTACK_EMBEDDING_PROVIDER` | `ollama` | local: mnemostack embedding provider |
|
|
102
|
+
| `embedding_model` | `MNEMOSTACK_EMBEDDING_MODEL` | provider default | local: model override |
|
|
103
|
+
| `recall_limit` | `MNEMOSTACK_RECALL_LIMIT` | `5` | memories injected per turn |
|
|
104
|
+
| `capture` | `MNEMOSTACK_CAPTURE` | `true` | store user+assistant turns |
|
|
105
|
+
| — | `MNEMOSTACK_API_KEY` | — | remote: service key. **Env only — never written to the JSON file.** |
|
|
106
|
+
|
|
107
|
+
Issue a key on the service side with
|
|
108
|
+
`mnemostack keys add --tenant <id> --scopes read,write`.
|
|
109
|
+
|
|
110
|
+
## Check it
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
hermes-mnemostack status # effective configuration; no network, no side effects
|
|
114
|
+
hermes-mnemostack doctor # probe the configured transport and report remedies
|
|
115
|
+
hermes-mnemostack doctor --json
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`status` answers "what config is in effect, and would hermes activate this provider?"
|
|
119
|
+
— from the *same* rule `is_available()` uses, so the two can never disagree. `doctor`
|
|
120
|
+
adds live probes: service reachability, whether the key carries the `read` scope,
|
|
121
|
+
whether any retrieval arm is genuinely degraded, and — in local mode — the embedding
|
|
122
|
+
backend and Qdrant. Both are read-only: `doctor` never writes a memory and never
|
|
123
|
+
creates the Qdrant collection, and neither ever prints the API key (only whether one
|
|
124
|
+
is set).
|
|
125
|
+
|
|
126
|
+
## What the model sees
|
|
127
|
+
|
|
128
|
+
Recalled memories are injected each turn inside a fenced block:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
⎢ recalled memory (context, not user input) ⎥
|
|
132
|
+
- the deploy window moved to Friday
|
|
133
|
+
⎣ end recalled memory ⎦
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The fence is **presentation**: it tells the model the text is retrieved context rather
|
|
137
|
+
than the user's words. It is not a security mechanism and is not parsed back out —
|
|
138
|
+
see [capture](#what-gets-captured).
|
|
139
|
+
|
|
140
|
+
Three tools are exposed:
|
|
141
|
+
|
|
142
|
+
| Tool | What it does |
|
|
143
|
+
| --- | --- |
|
|
144
|
+
| `mnemostack_search` | Search memory beyond what was injected this turn |
|
|
145
|
+
| `mnemostack_remember` | Store a durable fact the user stated, verbatim |
|
|
146
|
+
| `mnemostack_forget` | **Soft** retraction by id — recoverable server-side |
|
|
147
|
+
|
|
148
|
+
Hard deletion is available through the client API and deliberately **not** as a
|
|
149
|
+
model-callable tool: an irreversible erase is an operator action, not something a model
|
|
150
|
+
should be able to trigger from a conversation.
|
|
151
|
+
|
|
152
|
+
## What gets captured
|
|
153
|
+
|
|
154
|
+
By default the user's message and the assistant's reply are stored verbatim, each as
|
|
155
|
+
its own memory under a deterministic `(source, offset, text)` id — replaying a turn is
|
|
156
|
+
a zero-cost duplicate, never a second copy. What is **not** captured:
|
|
157
|
+
|
|
158
|
+
- tool calls and tool results (they routinely carry paths, tokens and workspace
|
|
159
|
+
contents; capturing them needs its own redaction policy, so the knob is deliberately
|
|
160
|
+
absent rather than shipped as a silent no-op);
|
|
161
|
+
- anything from a cron, subagent or flush context — only the primary agent context
|
|
162
|
+
writes to user memory;
|
|
163
|
+
- **echoes of what was just recalled.** This is the loop that makes a naive memory
|
|
164
|
+
provider eat itself: a memory is injected, the model repeats it, the repeat is
|
|
165
|
+
captured as a new memory, and it compounds. It is closed by capture-side
|
|
166
|
+
**provenance** — the provider knows the exact spans it displayed over the last few
|
|
167
|
+
turns — and NOT by parsing the fence markers back out, which both paraphrase and a
|
|
168
|
+
stray marker inside stored content defeat. The contract: a turn is a pure echo when
|
|
169
|
+
**every word** in it came from recalled content (for a wordless turn, when everything
|
|
170
|
+
non-space did), with quote/bullet framing absorbed.
|
|
171
|
+
|
|
172
|
+
Two residuals are documented rather than papered over: a **paraphrase** of a recalled
|
|
173
|
+
memory is new text and is captured, and a **wordless addition** (an emoji reaction)
|
|
174
|
+
sent alongside an otherwise complete echo is dropped with it.
|
|
175
|
+
|
|
176
|
+
Capture is asynchronous: turns go onto a bounded queue (128) drained by one worker in
|
|
177
|
+
order, so a slow service never blocks the turn loop. A full queue drops the pending
|
|
178
|
+
turn with a loud log line rather than growing without bound. Shutdown drains what is
|
|
179
|
+
already queued.
|
|
180
|
+
|
|
181
|
+
Set `capture: false` to make the provider read-only.
|
|
182
|
+
|
|
183
|
+
## Degradation
|
|
184
|
+
|
|
185
|
+
mnemostack 2.2 reports two lists on a recall: `notes` (routine "this stage did not
|
|
186
|
+
apply" signals — e.g. a query with no parseable date) and `degraded`, which still
|
|
187
|
+
duplicates those routine tags for back-compat until mnemostack 3.0. A real fault is
|
|
188
|
+
therefore an entry present in `degraded` and **absent** from `notes`. This provider
|
|
189
|
+
computes exactly that difference and logs only real faults, so a healthy recall stays
|
|
190
|
+
quiet; `hermes-mnemostack doctor` reports the same difference.
|
|
191
|
+
|
|
192
|
+
## Limitations
|
|
193
|
+
|
|
194
|
+
- Not yet exercised against a live hermes-agent session (pre-alpha).
|
|
195
|
+
- On hermes-agent 0.19 the provider is found through the directory shim
|
|
196
|
+
(`hermes-mnemostack install`); entry-point discovery arrives with 0.20.
|
|
197
|
+
- Local mode: recall is vector-only, and scoping is isolation, not authorization.
|
|
198
|
+
- `mnemostack_answer` (server-side generation) is deliberately not wired: it costs an
|
|
199
|
+
LLM call per question and the agent already has a model.
|
|
200
|
+
- `on_pre_compress` performs no extra extraction — the evicted turns were already
|
|
201
|
+
captured by `sync_turn` under the same deterministic ids.
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# hermes-mnemostack
|
|
2
|
+
|
|
3
|
+
**Status: first public release (0.9.0).** The provider is wired end to end (recall
|
|
4
|
+
injection, turn capture, tools, configuration, CLI), covered by tests, and
|
|
5
|
+
hermes-agent's own discovery loads it from a clean install — but it has not yet been
|
|
6
|
+
driven through a live agent session, which is why this is 0.9 and not 1.0.
|
|
7
|
+
|
|
8
|
+
[mnemostack](https://github.com/udjin-labs/mnemostack) memory provider for
|
|
9
|
+
[hermes-agent](https://github.com/NousResearch/hermes-agent): persistent agent memory
|
|
10
|
+
over either transport.
|
|
11
|
+
|
|
12
|
+
- **remote HTTP** — a shared mnemostack service (`mnemostack serve --auth`). The
|
|
13
|
+
tenant is resolved from the service key, never asserted by this client, so it is a
|
|
14
|
+
real authorization boundary. Recall uses whatever the deployment configures — vector,
|
|
15
|
+
lexical, temporal, graph — and the full write lifecycle is available: remember,
|
|
16
|
+
invalidate (soft retraction), delete.
|
|
17
|
+
- **local SDK** — mnemostack as a library against your own Qdrant. Recall is
|
|
18
|
+
vector-only in this mode today; profile/user scoping rides the library's tenant
|
|
19
|
+
mechanism, which is isolation but **not** an authorization boundary (same machine,
|
|
20
|
+
same trust domain — see [SECURITY.md](SECURITY.md)).
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
Requires mnemostack >= 2.2 and hermes-agent >= 0.19.
|
|
25
|
+
|
|
26
|
+
**hermes-agent 0.19** discovers memory providers by scanning
|
|
27
|
+
`$HERMES_HOME/plugins/<name>/`; it does not read pip entry points. So the package
|
|
28
|
+
ships a small directory shim and a command that puts it where Hermes looks:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install hermes-mnemostack
|
|
32
|
+
hermes-mnemostack install # copies the shim into the ACTIVE Hermes profile
|
|
33
|
+
hermes memory setup mnemostack
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`install` resolves the Hermes home the way Hermes does (so it lands in the active
|
|
37
|
+
profile, not a hardcoded `~/.hermes`), refuses to overwrite a different plugin of the
|
|
38
|
+
same name, and — because 0.19 logs a failed plugin import at debug level and then
|
|
39
|
+
silently omits the provider — finishes by asking Hermes's own discovery whether it
|
|
40
|
+
can actually see and load it. `--dry-run` shows what it would write.
|
|
41
|
+
|
|
42
|
+
**hermes-agent 0.20 and later** discovers the pip entry point on its own:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install hermes-mnemostack
|
|
46
|
+
hermes memory setup mnemostack
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
If the shim is already there it stays harmless: it resolves to the same provider the
|
|
50
|
+
entry point does. (Which of the two Hermes prefers when both are present is untested
|
|
51
|
+
here — 0.20 is git-only at the time of writing — so if you want only one, remove
|
|
52
|
+
`$HERMES_HOME/plugins/mnemostack/`.)
|
|
53
|
+
|
|
54
|
+
`hermes-mnemostack doctor` tells you which situation you are in by asking hermes's own
|
|
55
|
+
discovery rather than by comparing version numbers.
|
|
56
|
+
|
|
57
|
+
## Configure
|
|
58
|
+
|
|
59
|
+
Behavioral settings live in `$HERMES_HOME/mnemostack.json` (written by
|
|
60
|
+
`hermes memory setup`); **secrets live only in the environment**. Environment variables
|
|
61
|
+
provide defaults and the JSON file overrides them. An unknown key in the JSON file is
|
|
62
|
+
rejected loudly — a typo that silently does nothing is the worst failure mode for
|
|
63
|
+
memory configuration.
|
|
64
|
+
|
|
65
|
+
| Key | Env | Default | Meaning |
|
|
66
|
+
| --- | --- | --- | --- |
|
|
67
|
+
| `mode` | `MNEMOSTACK_MODE` | `remote` | `remote` (HTTP service) or `local` (library) |
|
|
68
|
+
| `base_url` | `MNEMOSTACK_BASE_URL` | — | remote: the service root, e.g. `https://memory.example:8080` |
|
|
69
|
+
| `timeout` | `MNEMOSTACK_TIMEOUT` | `30.0` | remote: HTTP timeout in seconds |
|
|
70
|
+
| `qdrant_url` | `MNEMOSTACK_QDRANT_URL` | `http://localhost:6333` | local: Qdrant endpoint |
|
|
71
|
+
| `collection` | `MNEMOSTACK_COLLECTION` | `hermes-memory` | local: collection name |
|
|
72
|
+
| `embedding_provider` | `MNEMOSTACK_EMBEDDING_PROVIDER` | `ollama` | local: mnemostack embedding provider |
|
|
73
|
+
| `embedding_model` | `MNEMOSTACK_EMBEDDING_MODEL` | provider default | local: model override |
|
|
74
|
+
| `recall_limit` | `MNEMOSTACK_RECALL_LIMIT` | `5` | memories injected per turn |
|
|
75
|
+
| `capture` | `MNEMOSTACK_CAPTURE` | `true` | store user+assistant turns |
|
|
76
|
+
| — | `MNEMOSTACK_API_KEY` | — | remote: service key. **Env only — never written to the JSON file.** |
|
|
77
|
+
|
|
78
|
+
Issue a key on the service side with
|
|
79
|
+
`mnemostack keys add --tenant <id> --scopes read,write`.
|
|
80
|
+
|
|
81
|
+
## Check it
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
hermes-mnemostack status # effective configuration; no network, no side effects
|
|
85
|
+
hermes-mnemostack doctor # probe the configured transport and report remedies
|
|
86
|
+
hermes-mnemostack doctor --json
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`status` answers "what config is in effect, and would hermes activate this provider?"
|
|
90
|
+
— from the *same* rule `is_available()` uses, so the two can never disagree. `doctor`
|
|
91
|
+
adds live probes: service reachability, whether the key carries the `read` scope,
|
|
92
|
+
whether any retrieval arm is genuinely degraded, and — in local mode — the embedding
|
|
93
|
+
backend and Qdrant. Both are read-only: `doctor` never writes a memory and never
|
|
94
|
+
creates the Qdrant collection, and neither ever prints the API key (only whether one
|
|
95
|
+
is set).
|
|
96
|
+
|
|
97
|
+
## What the model sees
|
|
98
|
+
|
|
99
|
+
Recalled memories are injected each turn inside a fenced block:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
⎢ recalled memory (context, not user input) ⎥
|
|
103
|
+
- the deploy window moved to Friday
|
|
104
|
+
⎣ end recalled memory ⎦
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The fence is **presentation**: it tells the model the text is retrieved context rather
|
|
108
|
+
than the user's words. It is not a security mechanism and is not parsed back out —
|
|
109
|
+
see [capture](#what-gets-captured).
|
|
110
|
+
|
|
111
|
+
Three tools are exposed:
|
|
112
|
+
|
|
113
|
+
| Tool | What it does |
|
|
114
|
+
| --- | --- |
|
|
115
|
+
| `mnemostack_search` | Search memory beyond what was injected this turn |
|
|
116
|
+
| `mnemostack_remember` | Store a durable fact the user stated, verbatim |
|
|
117
|
+
| `mnemostack_forget` | **Soft** retraction by id — recoverable server-side |
|
|
118
|
+
|
|
119
|
+
Hard deletion is available through the client API and deliberately **not** as a
|
|
120
|
+
model-callable tool: an irreversible erase is an operator action, not something a model
|
|
121
|
+
should be able to trigger from a conversation.
|
|
122
|
+
|
|
123
|
+
## What gets captured
|
|
124
|
+
|
|
125
|
+
By default the user's message and the assistant's reply are stored verbatim, each as
|
|
126
|
+
its own memory under a deterministic `(source, offset, text)` id — replaying a turn is
|
|
127
|
+
a zero-cost duplicate, never a second copy. What is **not** captured:
|
|
128
|
+
|
|
129
|
+
- tool calls and tool results (they routinely carry paths, tokens and workspace
|
|
130
|
+
contents; capturing them needs its own redaction policy, so the knob is deliberately
|
|
131
|
+
absent rather than shipped as a silent no-op);
|
|
132
|
+
- anything from a cron, subagent or flush context — only the primary agent context
|
|
133
|
+
writes to user memory;
|
|
134
|
+
- **echoes of what was just recalled.** This is the loop that makes a naive memory
|
|
135
|
+
provider eat itself: a memory is injected, the model repeats it, the repeat is
|
|
136
|
+
captured as a new memory, and it compounds. It is closed by capture-side
|
|
137
|
+
**provenance** — the provider knows the exact spans it displayed over the last few
|
|
138
|
+
turns — and NOT by parsing the fence markers back out, which both paraphrase and a
|
|
139
|
+
stray marker inside stored content defeat. The contract: a turn is a pure echo when
|
|
140
|
+
**every word** in it came from recalled content (for a wordless turn, when everything
|
|
141
|
+
non-space did), with quote/bullet framing absorbed.
|
|
142
|
+
|
|
143
|
+
Two residuals are documented rather than papered over: a **paraphrase** of a recalled
|
|
144
|
+
memory is new text and is captured, and a **wordless addition** (an emoji reaction)
|
|
145
|
+
sent alongside an otherwise complete echo is dropped with it.
|
|
146
|
+
|
|
147
|
+
Capture is asynchronous: turns go onto a bounded queue (128) drained by one worker in
|
|
148
|
+
order, so a slow service never blocks the turn loop. A full queue drops the pending
|
|
149
|
+
turn with a loud log line rather than growing without bound. Shutdown drains what is
|
|
150
|
+
already queued.
|
|
151
|
+
|
|
152
|
+
Set `capture: false` to make the provider read-only.
|
|
153
|
+
|
|
154
|
+
## Degradation
|
|
155
|
+
|
|
156
|
+
mnemostack 2.2 reports two lists on a recall: `notes` (routine "this stage did not
|
|
157
|
+
apply" signals — e.g. a query with no parseable date) and `degraded`, which still
|
|
158
|
+
duplicates those routine tags for back-compat until mnemostack 3.0. A real fault is
|
|
159
|
+
therefore an entry present in `degraded` and **absent** from `notes`. This provider
|
|
160
|
+
computes exactly that difference and logs only real faults, so a healthy recall stays
|
|
161
|
+
quiet; `hermes-mnemostack doctor` reports the same difference.
|
|
162
|
+
|
|
163
|
+
## Limitations
|
|
164
|
+
|
|
165
|
+
- Not yet exercised against a live hermes-agent session (pre-alpha).
|
|
166
|
+
- On hermes-agent 0.19 the provider is found through the directory shim
|
|
167
|
+
(`hermes-mnemostack install`); entry-point discovery arrives with 0.20.
|
|
168
|
+
- Local mode: recall is vector-only, and scoping is isolation, not authorization.
|
|
169
|
+
- `mnemostack_answer` (server-side generation) is deliberately not wired: it costs an
|
|
170
|
+
LLM call per question and the agent already has a model.
|
|
171
|
+
- `on_pre_compress` performs no extra extraction — the evicted turns were already
|
|
172
|
+
captured by `sync_turn` under the same deterministic ids.
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
MIT.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hermes-mnemostack"
|
|
7
|
+
version = "0.9.0"
|
|
8
|
+
description = "mnemostack memory provider for hermes-agent: persistent agent memory over a mnemostack service or a local Qdrant"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "hermes-mnemostack contributors" }]
|
|
13
|
+
keywords = ["hermes-agent", "mnemostack", "memory", "agent", "rag", "recall"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
23
|
+
]
|
|
24
|
+
# hermes-agent is a true runtime dependency: the provider subclasses its
|
|
25
|
+
# MemoryProvider ABC at import time. hermes-agent caps python at <3.14,
|
|
26
|
+
# which pip resolves transitively — no need to repeat the cap here.
|
|
27
|
+
# Floor notes:
|
|
28
|
+
# - hermes-agent 0.19 (latest on PyPI) carries the ABC but NOT the pip
|
|
29
|
+
# entry-point discovery (landed in 0.20, git-only for now) — on 0.19
|
|
30
|
+
# the provider installs as a $HERMES_HOME/plugins/ directory instead.
|
|
31
|
+
# - mnemostack 2.2 (on PyPI) ships the remote write/lifecycle surface
|
|
32
|
+
# AND the library helpers this package uses (ingest_remote_items,
|
|
33
|
+
# expand_remote_items, coerce_point_ids) — 2.1 lacks them entirely.
|
|
34
|
+
dependencies = [
|
|
35
|
+
"hermes-agent>=0.19",
|
|
36
|
+
"mnemostack>=2.2",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
dev = [
|
|
41
|
+
"pytest>=7.4",
|
|
42
|
+
"ruff>=0.1",
|
|
43
|
+
"mypy>=1.7",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[project.scripts]
|
|
47
|
+
hermes-mnemostack = "hermes_mnemostack.cli:main"
|
|
48
|
+
|
|
49
|
+
[project.entry-points."hermes_agent.memory_providers"]
|
|
50
|
+
mnemostack = "hermes_mnemostack.provider:MnemostackProvider"
|
|
51
|
+
|
|
52
|
+
[project.urls]
|
|
53
|
+
Homepage = "https://github.com/udjin-labs/hermes-mnemostack"
|
|
54
|
+
Repository = "https://github.com/udjin-labs/hermes-mnemostack"
|
|
55
|
+
Issues = "https://github.com/udjin-labs/hermes-mnemostack/issues"
|
|
56
|
+
|
|
57
|
+
[tool.setuptools.packages.find]
|
|
58
|
+
where = ["src"]
|
|
59
|
+
|
|
60
|
+
# The 0.19 directory shim ships INSIDE the package so `hermes-mnemostack
|
|
61
|
+
# install` has something to copy; plugin.yaml and its README are data files
|
|
62
|
+
# and would otherwise be left out of the wheel.
|
|
63
|
+
[tool.setuptools.package-data]
|
|
64
|
+
hermes_mnemostack = ["plugin/plugin.yaml", "plugin/README.md"]
|
|
65
|
+
|
|
66
|
+
[tool.ruff]
|
|
67
|
+
line-length = 100
|
|
68
|
+
target-version = "py311"
|
|
69
|
+
|
|
70
|
+
[tool.ruff.lint]
|
|
71
|
+
select = ["E", "F", "W", "I", "B", "UP"]
|
|
72
|
+
ignore = ["E501"]
|
|
73
|
+
|
|
74
|
+
[tool.mypy]
|
|
75
|
+
python_version = "3.11"
|
|
76
|
+
ignore_missing_imports = true
|
|
77
|
+
no_implicit_optional = true
|
|
78
|
+
|
|
79
|
+
[tool.pytest.ini_options]
|
|
80
|
+
pythonpath = ["src"]
|
|
81
|
+
testpaths = ["tests"]
|