hermes-plugin-qdrant-memory 0.2.0__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/PKG-INFO +52 -31
  2. hermes_plugin_qdrant_memory-0.2.0/hermes_plugin_qdrant_memory.egg-info/PKG-INFO → hermes_plugin_qdrant_memory-0.2.1/README.md +49 -49
  3. hermes_plugin_qdrant_memory-0.2.0/README.md → hermes_plugin_qdrant_memory-0.2.1/hermes_plugin_qdrant_memory.egg-info/PKG-INFO +70 -30
  4. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/hermes_plugin_qdrant_memory.egg-info/SOURCES.txt +1 -0
  5. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/pyproject.toml +3 -1
  6. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/dedupe.py +8 -0
  7. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/plugin.yaml +1 -1
  8. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/runtime.py +3 -5
  9. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/version.py +1 -1
  10. hermes_plugin_qdrant_memory-0.2.1/tests/test_explicit_dedupe.py +80 -0
  11. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_manifest_contract.py +7 -0
  12. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/LICENSE +0 -0
  13. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/MANIFEST.in +0 -0
  14. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/hermes_plugin_qdrant_memory.egg-info/dependency_links.txt +0 -0
  15. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/hermes_plugin_qdrant_memory.egg-info/entry_points.txt +0 -0
  16. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/hermes_plugin_qdrant_memory.egg-info/requires.txt +0 -0
  17. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/hermes_plugin_qdrant_memory.egg-info/top_level.txt +0 -0
  18. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/__init__.py +0 -0
  19. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/cli.py +0 -0
  20. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/config.py +0 -0
  21. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/config_schema.py +0 -0
  22. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/embedding.py +0 -0
  23. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/extraction.py +0 -0
  24. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/inventory.py +0 -0
  25. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/ledger.py +0 -0
  26. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/mcp_server.py +0 -0
  27. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/migration.py +0 -0
  28. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/models.py +0 -0
  29. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/ownership.py +0 -0
  30. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/progress.py +0 -0
  31. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/provider.py +0 -0
  32. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/qdrant_store.py +0 -0
  33. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/reset.py +0 -0
  34. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/retry.py +0 -0
  35. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/standalone.py +0 -0
  36. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/tools.py +0 -0
  37. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/scripts/demo.sh +0 -0
  38. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/setup.cfg +0 -0
  39. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_backend_contracts.py +0 -0
  40. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_bot_scope.py +0 -0
  41. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_bounded_recovery.py +0 -0
  42. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_checkpoint_lifecycle.py +0 -0
  43. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_ci_gate.py +0 -0
  44. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_cli_output.py +0 -0
  45. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_config_embedding.py +0 -0
  46. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_deletion_ordering.py +0 -0
  47. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_discovery_cli.py +0 -0
  48. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_host_lifecycle.py +0 -0
  49. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_input_validation.py +0 -0
  50. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_inventory_export.py +0 -0
  51. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_live_metrics.py +0 -0
  52. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_maintenance.py +0 -0
  53. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_migration.py +0 -0
  54. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_migration_endpoints.py +0 -0
  55. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_migration_progress.py +0 -0
  56. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_migration_resume.py +0 -0
  57. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_migration_superseded.py +0 -0
  58. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_profile_scope.py +0 -0
  59. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_provider.py +0 -0
  60. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_recovery_benchmark.py +0 -0
  61. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_recovery_evaluation.py +0 -0
  62. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_remote_qdrant.py +0 -0
  63. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_reset_recovery.py +0 -0
  64. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_retention.py +0 -0
  65. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_review_regressions.py +0 -0
  66. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_session_provenance.py +0 -0
  67. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_setup_contract.py +0 -0
  68. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_shutdown_contract.py +0 -0
  69. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_store_runtime.py +0 -0
  70. {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_writer_ownership.py +0 -0
@@ -1,12 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hermes-plugin-qdrant-memory
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Durable Qdrant memory for Hermes, standalone CLI and local MCP
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/cnkang/hermes-plugin-qdrant-memory
7
7
  Project-URL: Repository, https://github.com/cnkang/hermes-plugin-qdrant-memory
8
8
  Project-URL: Issues, https://github.com/cnkang/hermes-plugin-qdrant-memory/issues
9
9
  Project-URL: Changelog, https://github.com/cnkang/hermes-plugin-qdrant-memory/blob/main/CHANGELOG.md
10
+ Project-URL: MCP, https://github.com/cnkang/qdrant-memory-mcp
11
+ Project-URL: Skill, https://github.com/cnkang/skills/tree/main/qdrant-memory
10
12
  Requires-Python: >=3.11
11
13
  Description-Content-Type: text/markdown
12
14
  License-File: LICENSE
@@ -26,12 +28,11 @@ Dynamic: license-file
26
28
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
27
29
  [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
28
30
 
29
- > **v0.1.0 — limited technical preview.** Give Hermes Agent durable, private
30
- > long-term memory: your conversations become searchable memories in your own
31
- > Qdrant store, with recall, update and scoped deletion under your control.
32
- > The preview supports one writer per destination, with the documented
33
- > durability limits. See the [validation record](docs/validation.md) for the
34
- > current certification.
31
+ > **Runtime 0.2.1** provides the standalone CLI/MCP and fixes explicit writes of
32
+ > distinct, highly similar texts. MCP distribution 0.1.1 pins Runtime 0.2.1.
33
+ > The Hermes Plugin Catalog has its own reviewed immutable pin. A PyPI release
34
+ > does not upgrade it. One writer per destination remains required.
35
+ > See [release notes](docs/releases/0.2.1.md) and [validation](docs/validation.md).
35
36
 
36
37
  [English](README.md) | [简体中文](README.zh-CN.md)
37
38
 
@@ -45,6 +46,16 @@ as a community-maintained plugin.
45
46
 
46
47
  ## How it works
47
48
 
49
+ For non-Hermes users, install in a private Python 3.11+
50
+ environment: `pip install 'hermes-plugin-qdrant-memory[mcp]==0.2.1'` (omit `[mcp]`
51
+ for CLI only). `qdrant-memory` is the CLI and `mcp` is its server subcommand.
52
+ Standalone does not require Hermes. See [setup and quick start](docs/standalone.md).
53
+ MCP-focused agents may instead use the [metadata-only distribution](https://github.com/cnkang/qdrant-memory-mcp).
54
+ Public releases: [Runtime on PyPI](https://pypi.org/project/hermes-plugin-qdrant-memory/)
55
+ and [MCP distribution on PyPI](https://pypi.org/project/qdrant-memory-mcp/).
56
+ For native Hermes use its official plugin installation mechanism below. Before
57
+ an authorized upgrade, verify the same profile, collection and embedding pipeline.
58
+
48
59
  ```mermaid
49
60
  flowchart LR
50
61
  turn["Hermes turn"] --> admit["Provider callback"]
@@ -66,7 +77,7 @@ flowchart LR
66
77
  - A fully local setup works: embedded Qdrant and local Ollama keep the data
67
78
  on your machine.
68
79
  - Data locality follows the destination. Extraction uses the LLM configured
69
- in Hermes, and embedding uses the configured embedding service; either can
80
+ in Hermes, and embedding uses the configured embedding service. Either can
70
81
  be remote. See [Data flow and privacy](#data-flow-and-privacy).
71
82
  - Not supported in this preview: multi-writer Server/Cloud, hybrid search and
72
83
  a web UI.
@@ -75,9 +86,9 @@ flowchart LR
75
86
 
76
87
  The plugin primarily provides Hermes Agent's durable memory integration. As an
77
88
  additional capability, its shared backend can serve other agents through the
78
- Runtime 0.2.0's `qdrant-memory --home PATH` JSON CLI and optional local stdio MCP
79
- interface, without requiring Hermes. These interfaces support explicit scoped
80
- memory operations and maintenance/migration; automatic extraction and prefetch
89
+ Runtime's `qdrant-memory --home PATH` JSON CLI and optional local stdio MCP
90
+ interface. Standalone use does not require Hermes. These interfaces support explicit scoped
91
+ memory operations and maintenance/migration. Automatic extraction and prefetch
81
92
  remain Hermes lifecycle features. See [standalone setup](docs/standalone.md) for
82
93
  configuration and boundaries, and the agent-neutral
83
94
  [qdrant-memory skill](https://github.com/cnkang/skills/tree/main/qdrant-memory)
@@ -85,19 +96,20 @@ for multi-agent installation and usage guidance.
85
96
 
86
97
  The optional MCP server exposes four scoped tools: `qdrant_memory_search`,
87
98
  `qdrant_memory_add`, `qdrant_memory_update` and `qdrant_memory_delete`. Compatible
88
- clients launch it as a local stdio process and discover/call those tools;
89
- maintenance and migration use the operator CLI. See the
99
+ clients launch it as a local stdio process and discover/call those tools.
100
+ Maintenance and migration use the operator CLI. See the
90
101
  [MCP capabilities and connection guide](docs/mcp.md) for setup, shared-memory
91
- boundaries and the verified agent matrix. This is a tested source preview;
92
- the published v0.1.0 plugin release does not include the MCP entrypoint.
102
+ boundaries and the verified agent matrix. Runtime 0.2.0 published these interfaces.
103
+ The published v0.1.0 plugin release does not include the MCP entrypoint.
93
104
 
94
105
  ## See it in action
95
106
 
96
107
  ![Terminal recording: install the plugin, store a preference, and recall it in a new session](https://raw.githubusercontent.com/cnkang/hermes-plugin-qdrant-memory/main/assets/demo.gif)
97
108
 
98
- The recording shows the full flow on a clean profile: install from the
99
- official Hermes Plugin Catalog, teach a preference, wait for the background
100
- extraction, recall it in a new session, and clean up the demo scope. All steps
109
+ The recording shows the full flow on a clean profile. It installs from the
110
+ official Hermes Plugin Catalog, teaches a preference, and waits for the
111
+ background extraction. Then it recalls the preference in a new session and
112
+ cleans up the demo scope. All steps
101
113
  run in embedded mode. `scripts/demo.sh` reproduces the same walkthrough with
102
114
  the pinned release.
103
115
 
@@ -128,14 +140,17 @@ The install resolves the catalog's reviewed commit and prints the entry's declar
128
140
  tools, requirements and known issues before it clones anything.
129
141
  `hermes plugins enable` prepares the declared dependencies through Hermes PM.
130
142
 
131
- **Version-locked: install the pinned release through the Hermes CLI.**
143
+ **Version-locked: install the verified Runtime 0.2.1 source through the Hermes CLI.**
144
+
145
+ This immutable source revision includes the explicit-write fix. It does not
146
+ depend on a PyPI release or a catalog update.
132
147
 
133
148
  ```bash
134
149
  export HERMES_HOME="/absolute/path/to/your/hermes-profile"
135
150
  ollama pull qwen3-embedding:4b
136
151
  # Start Ollama if it is not running. Keep its service available.
137
152
  hermes plugins install https://github.com/cnkang/hermes-plugin-qdrant-memory \
138
- --ref 5babd3e4b4c022aad38a2b6db82cbd5b601f1ef8 # v0.1.0 release commit
153
+ --ref 8929693fcccd1efa91bfa062145e35d2369ca9cc # verified Runtime 0.2.1 source
139
154
  hermes plugins enable qdrant-memory
140
155
  hermes memory setup
141
156
  hermes config set memory.provider qdrant-memory
@@ -144,12 +159,15 @@ hermes qdrant-memory init
144
159
 
145
160
  For this version-locked form, `hermes plugins install` checks out the given ref, and
146
161
  `hermes plugins enable` prepares the declared dependencies through Hermes PM.
147
- `--ref` takes a full 40-character commit SHA; the
162
+ `--ref` takes a full 40-character commit SHA. The
148
163
  [releases page](https://github.com/cnkang/hermes-plugin-qdrant-memory/releases)
149
164
  lists the release commits. Do not pip-install into a managed Hermes
150
165
  environment. Omit `--ref` to track `main`, the development line.
151
166
 
152
- **Alternative: clone the release tag manually.**
167
+ **Historical release: clone a published tag manually.**
168
+
169
+ The v0.1.0 tag below predates the standalone interfaces and the explicit-write
170
+ fix. Use the pinned source above for Runtime 0.2.1.
153
171
 
154
172
  ```bash
155
173
  export HERMES_HOME="/absolute/path/to/your/hermes-profile"
@@ -178,7 +196,7 @@ import and registration. For development installs, see
178
196
  [development setup](docs/development.md).
179
197
 
180
198
  The release notes, supported Hermes/Python versions and the current
181
- limitations live in [docs/releases/v0.1.0.md](docs/releases/v0.1.0.md);
199
+ limitations live in [docs/releases/v0.1.0.md](docs/releases/v0.1.0.md).
182
200
  `main` stays the development line.
183
201
 
184
202
  For local development, symlink the repository into a disposable profile:
@@ -223,7 +241,10 @@ mappings) stays in the local Hermes home. Back up that home together with your
223
241
  Qdrant data. Migration from Mem0 is one-way, and version 0.1 has no vector
224
242
  reuse or hybrid retrieval.
225
243
 
226
- Similarity is a candidate-selection signal, not proof that two facts are identical.
244
+ Explicit add deduplicates only canonical text in the same scope and never calls
245
+ an LLM. Different texts are saved regardless of similarity. Update replaces only
246
+ the authorized exact ID. For automatic Hermes extraction,
247
+ similarity is a candidate-selection signal, not proof that two facts are identical.
227
248
  `SAME` skips, `SUPERSEDES` updates, `CONFLICT` creates a linked record, and
228
249
  `UNRELATED` adds a record. Migration uses source IDs instead of semantic merging.
229
250
 
@@ -305,7 +326,7 @@ default profile scope uses an agent (`hermes`). Pass `--agent hermes` to
305
326
  target it, or `--all-agents` to delete every agent scope of the user. Without
306
327
  `--all-agents`, the result reports `other_agent_scopes` when other agent
307
328
  scopes still hold memories. A literal `*` is an ordinary agent id, not a
308
- wildcard. `--dry-run` previews the per-scope counts;
329
+ wildcard. `--dry-run` previews the per-scope counts.
309
330
  `--confirm` authorizes deletion. An interrupted deletion fails closed: other
310
331
  commands and provider startup return `scope_delete_recovery_required` until
311
332
  `delete-all --confirm` resumes it. Deletion intents are versioned and explicit.
@@ -313,8 +334,8 @@ A pre-versioned intent cannot show whether `*` is a single agent or all agents,
313
334
  so the plugin refuses it (`scope_delete_legacy_intent`). Resolve it with
314
335
  `--resolve-legacy single_agent`, or with `--resolve-legacy all_agents` when the
315
336
  record's agent is `*` or absent. Turns admitted after the deletion can still
316
- add new memories; everything admitted before it — stored memories, prepared
317
- writes and unprocessed turn events — is removed or invalidated. `list`,
337
+ add new memories. Everything admitted before it, including stored memories, prepared
338
+ writes and unprocessed turn events, is removed or invalidated. `list`,
318
339
  `export` and `delete-all` do not require the embedding service.
319
340
 
320
341
  ## Data flow and privacy
@@ -325,10 +346,10 @@ carries and where it can go:
325
346
  | Component | Carries | Destination |
326
347
  | --- | --- | --- |
327
348
  | Chat / agent turns | Your conversation | The model configured for the Hermes session |
328
- | Memory extraction (`llm.mode: inherit`) | The turn's user and assistant text | The same session model — with a cloud model, turn text reaches that cloud |
329
- | Relation checks | The candidate memory and the similar stored memory | Same routing as extraction; runs only when similarity selects a review candidate |
349
+ | Memory extraction (`llm.mode: inherit`) | The turn's user and assistant text | The same session model. With a cloud model, turn text reaches that cloud |
350
+ | Relation checks | The candidate memory and the similar stored memory | Same routing as extraction. It runs only when similarity selects a review candidate |
330
351
  | Embeddings (`embedding`) | Memory and search text | Ollama by default, or your OpenAI-compatible endpoint |
331
- | Storage (`qdrant`) | Memory payloads and provenance; the ledger stays local | Embedded local path, self-hosted server, or Qdrant Cloud |
352
+ | Storage (`qdrant`) | Memory payloads and provenance. The ledger stays local | Embedded local path, self-hosted server, or Qdrant Cloud |
332
353
 
333
354
  Three common deployments:
334
355
 
@@ -483,7 +504,7 @@ contains `memories` objects with `id`/`text`, and `queries` with `query`/`releva
483
504
  The default fixture now contains 24 labeled queries, including Chinese, English,
484
505
  mixed-language and hard-negative cases. The
485
506
  [original 12-query recording](docs/retrieval-baseline.json) is a historical
486
- synthetic regression baseline; rerun the current fixture for current measurements.
507
+ synthetic regression baseline. Rerun the current fixture for current measurements.
487
508
  These fixtures do not establish production recall quality. See the [recovery benchmark](docs/recovery-benchmark.md)
488
509
  for a separate synthetic replay and retained-storage measurement.
489
510
 
@@ -1,22 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: hermes-plugin-qdrant-memory
3
- Version: 0.2.0
4
- Summary: Durable Qdrant memory for Hermes, standalone CLI and local MCP
5
- License-Expression: MIT
6
- Project-URL: Homepage, https://github.com/cnkang/hermes-plugin-qdrant-memory
7
- Project-URL: Repository, https://github.com/cnkang/hermes-plugin-qdrant-memory
8
- Project-URL: Issues, https://github.com/cnkang/hermes-plugin-qdrant-memory/issues
9
- Project-URL: Changelog, https://github.com/cnkang/hermes-plugin-qdrant-memory/blob/main/CHANGELOG.md
10
- Requires-Python: >=3.11
11
- Description-Content-Type: text/markdown
12
- License-File: LICENSE
13
- Requires-Dist: qdrant-client<2,>=1.15
14
- Requires-Dist: httpx<1,>=0.28
15
- Requires-Dist: portalocker<4,>=3.2
16
- Provides-Extra: mcp
17
- Requires-Dist: mcp<2,>=1.30; extra == "mcp"
18
- Dynamic: license-file
19
-
20
1
  # Qdrant Memory for Hermes
21
2
 
22
3
  [![CI](https://github.com/cnkang/hermes-plugin-qdrant-memory/actions/workflows/ci.yml/badge.svg)](https://github.com/cnkang/hermes-plugin-qdrant-memory/actions/workflows/ci.yml)
@@ -26,12 +7,11 @@ Dynamic: license-file
26
7
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
27
8
  [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
28
9
 
29
- > **v0.1.0 — limited technical preview.** Give Hermes Agent durable, private
30
- > long-term memory: your conversations become searchable memories in your own
31
- > Qdrant store, with recall, update and scoped deletion under your control.
32
- > The preview supports one writer per destination, with the documented
33
- > durability limits. See the [validation record](docs/validation.md) for the
34
- > current certification.
10
+ > **Runtime 0.2.1** provides the standalone CLI/MCP and fixes explicit writes of
11
+ > distinct, highly similar texts. MCP distribution 0.1.1 pins Runtime 0.2.1.
12
+ > The Hermes Plugin Catalog has its own reviewed immutable pin. A PyPI release
13
+ > does not upgrade it. One writer per destination remains required.
14
+ > See [release notes](docs/releases/0.2.1.md) and [validation](docs/validation.md).
35
15
 
36
16
  [English](README.md) | [简体中文](README.zh-CN.md)
37
17
 
@@ -45,6 +25,16 @@ as a community-maintained plugin.
45
25
 
46
26
  ## How it works
47
27
 
28
+ For non-Hermes users, install in a private Python 3.11+
29
+ environment: `pip install 'hermes-plugin-qdrant-memory[mcp]==0.2.1'` (omit `[mcp]`
30
+ for CLI only). `qdrant-memory` is the CLI and `mcp` is its server subcommand.
31
+ Standalone does not require Hermes. See [setup and quick start](docs/standalone.md).
32
+ MCP-focused agents may instead use the [metadata-only distribution](https://github.com/cnkang/qdrant-memory-mcp).
33
+ Public releases: [Runtime on PyPI](https://pypi.org/project/hermes-plugin-qdrant-memory/)
34
+ and [MCP distribution on PyPI](https://pypi.org/project/qdrant-memory-mcp/).
35
+ For native Hermes use its official plugin installation mechanism below. Before
36
+ an authorized upgrade, verify the same profile, collection and embedding pipeline.
37
+
48
38
  ```mermaid
49
39
  flowchart LR
50
40
  turn["Hermes turn"] --> admit["Provider callback"]
@@ -66,7 +56,7 @@ flowchart LR
66
56
  - A fully local setup works: embedded Qdrant and local Ollama keep the data
67
57
  on your machine.
68
58
  - Data locality follows the destination. Extraction uses the LLM configured
69
- in Hermes, and embedding uses the configured embedding service; either can
59
+ in Hermes, and embedding uses the configured embedding service. Either can
70
60
  be remote. See [Data flow and privacy](#data-flow-and-privacy).
71
61
  - Not supported in this preview: multi-writer Server/Cloud, hybrid search and
72
62
  a web UI.
@@ -75,9 +65,9 @@ flowchart LR
75
65
 
76
66
  The plugin primarily provides Hermes Agent's durable memory integration. As an
77
67
  additional capability, its shared backend can serve other agents through the
78
- Runtime 0.2.0's `qdrant-memory --home PATH` JSON CLI and optional local stdio MCP
79
- interface, without requiring Hermes. These interfaces support explicit scoped
80
- memory operations and maintenance/migration; automatic extraction and prefetch
68
+ Runtime's `qdrant-memory --home PATH` JSON CLI and optional local stdio MCP
69
+ interface. Standalone use does not require Hermes. These interfaces support explicit scoped
70
+ memory operations and maintenance/migration. Automatic extraction and prefetch
81
71
  remain Hermes lifecycle features. See [standalone setup](docs/standalone.md) for
82
72
  configuration and boundaries, and the agent-neutral
83
73
  [qdrant-memory skill](https://github.com/cnkang/skills/tree/main/qdrant-memory)
@@ -85,19 +75,20 @@ for multi-agent installation and usage guidance.
85
75
 
86
76
  The optional MCP server exposes four scoped tools: `qdrant_memory_search`,
87
77
  `qdrant_memory_add`, `qdrant_memory_update` and `qdrant_memory_delete`. Compatible
88
- clients launch it as a local stdio process and discover/call those tools;
89
- maintenance and migration use the operator CLI. See the
78
+ clients launch it as a local stdio process and discover/call those tools.
79
+ Maintenance and migration use the operator CLI. See the
90
80
  [MCP capabilities and connection guide](docs/mcp.md) for setup, shared-memory
91
- boundaries and the verified agent matrix. This is a tested source preview;
92
- the published v0.1.0 plugin release does not include the MCP entrypoint.
81
+ boundaries and the verified agent matrix. Runtime 0.2.0 published these interfaces.
82
+ The published v0.1.0 plugin release does not include the MCP entrypoint.
93
83
 
94
84
  ## See it in action
95
85
 
96
86
  ![Terminal recording: install the plugin, store a preference, and recall it in a new session](https://raw.githubusercontent.com/cnkang/hermes-plugin-qdrant-memory/main/assets/demo.gif)
97
87
 
98
- The recording shows the full flow on a clean profile: install from the
99
- official Hermes Plugin Catalog, teach a preference, wait for the background
100
- extraction, recall it in a new session, and clean up the demo scope. All steps
88
+ The recording shows the full flow on a clean profile. It installs from the
89
+ official Hermes Plugin Catalog, teaches a preference, and waits for the
90
+ background extraction. Then it recalls the preference in a new session and
91
+ cleans up the demo scope. All steps
101
92
  run in embedded mode. `scripts/demo.sh` reproduces the same walkthrough with
102
93
  the pinned release.
103
94
 
@@ -128,14 +119,17 @@ The install resolves the catalog's reviewed commit and prints the entry's declar
128
119
  tools, requirements and known issues before it clones anything.
129
120
  `hermes plugins enable` prepares the declared dependencies through Hermes PM.
130
121
 
131
- **Version-locked: install the pinned release through the Hermes CLI.**
122
+ **Version-locked: install the verified Runtime 0.2.1 source through the Hermes CLI.**
123
+
124
+ This immutable source revision includes the explicit-write fix. It does not
125
+ depend on a PyPI release or a catalog update.
132
126
 
133
127
  ```bash
134
128
  export HERMES_HOME="/absolute/path/to/your/hermes-profile"
135
129
  ollama pull qwen3-embedding:4b
136
130
  # Start Ollama if it is not running. Keep its service available.
137
131
  hermes plugins install https://github.com/cnkang/hermes-plugin-qdrant-memory \
138
- --ref 5babd3e4b4c022aad38a2b6db82cbd5b601f1ef8 # v0.1.0 release commit
132
+ --ref 8929693fcccd1efa91bfa062145e35d2369ca9cc # verified Runtime 0.2.1 source
139
133
  hermes plugins enable qdrant-memory
140
134
  hermes memory setup
141
135
  hermes config set memory.provider qdrant-memory
@@ -144,12 +138,15 @@ hermes qdrant-memory init
144
138
 
145
139
  For this version-locked form, `hermes plugins install` checks out the given ref, and
146
140
  `hermes plugins enable` prepares the declared dependencies through Hermes PM.
147
- `--ref` takes a full 40-character commit SHA; the
141
+ `--ref` takes a full 40-character commit SHA. The
148
142
  [releases page](https://github.com/cnkang/hermes-plugin-qdrant-memory/releases)
149
143
  lists the release commits. Do not pip-install into a managed Hermes
150
144
  environment. Omit `--ref` to track `main`, the development line.
151
145
 
152
- **Alternative: clone the release tag manually.**
146
+ **Historical release: clone a published tag manually.**
147
+
148
+ The v0.1.0 tag below predates the standalone interfaces and the explicit-write
149
+ fix. Use the pinned source above for Runtime 0.2.1.
153
150
 
154
151
  ```bash
155
152
  export HERMES_HOME="/absolute/path/to/your/hermes-profile"
@@ -178,7 +175,7 @@ import and registration. For development installs, see
178
175
  [development setup](docs/development.md).
179
176
 
180
177
  The release notes, supported Hermes/Python versions and the current
181
- limitations live in [docs/releases/v0.1.0.md](docs/releases/v0.1.0.md);
178
+ limitations live in [docs/releases/v0.1.0.md](docs/releases/v0.1.0.md).
182
179
  `main` stays the development line.
183
180
 
184
181
  For local development, symlink the repository into a disposable profile:
@@ -223,7 +220,10 @@ mappings) stays in the local Hermes home. Back up that home together with your
223
220
  Qdrant data. Migration from Mem0 is one-way, and version 0.1 has no vector
224
221
  reuse or hybrid retrieval.
225
222
 
226
- Similarity is a candidate-selection signal, not proof that two facts are identical.
223
+ Explicit add deduplicates only canonical text in the same scope and never calls
224
+ an LLM. Different texts are saved regardless of similarity. Update replaces only
225
+ the authorized exact ID. For automatic Hermes extraction,
226
+ similarity is a candidate-selection signal, not proof that two facts are identical.
227
227
  `SAME` skips, `SUPERSEDES` updates, `CONFLICT` creates a linked record, and
228
228
  `UNRELATED` adds a record. Migration uses source IDs instead of semantic merging.
229
229
 
@@ -305,7 +305,7 @@ default profile scope uses an agent (`hermes`). Pass `--agent hermes` to
305
305
  target it, or `--all-agents` to delete every agent scope of the user. Without
306
306
  `--all-agents`, the result reports `other_agent_scopes` when other agent
307
307
  scopes still hold memories. A literal `*` is an ordinary agent id, not a
308
- wildcard. `--dry-run` previews the per-scope counts;
308
+ wildcard. `--dry-run` previews the per-scope counts.
309
309
  `--confirm` authorizes deletion. An interrupted deletion fails closed: other
310
310
  commands and provider startup return `scope_delete_recovery_required` until
311
311
  `delete-all --confirm` resumes it. Deletion intents are versioned and explicit.
@@ -313,8 +313,8 @@ A pre-versioned intent cannot show whether `*` is a single agent or all agents,
313
313
  so the plugin refuses it (`scope_delete_legacy_intent`). Resolve it with
314
314
  `--resolve-legacy single_agent`, or with `--resolve-legacy all_agents` when the
315
315
  record's agent is `*` or absent. Turns admitted after the deletion can still
316
- add new memories; everything admitted before it — stored memories, prepared
317
- writes and unprocessed turn events — is removed or invalidated. `list`,
316
+ add new memories. Everything admitted before it, including stored memories, prepared
317
+ writes and unprocessed turn events, is removed or invalidated. `list`,
318
318
  `export` and `delete-all` do not require the embedding service.
319
319
 
320
320
  ## Data flow and privacy
@@ -325,10 +325,10 @@ carries and where it can go:
325
325
  | Component | Carries | Destination |
326
326
  | --- | --- | --- |
327
327
  | Chat / agent turns | Your conversation | The model configured for the Hermes session |
328
- | Memory extraction (`llm.mode: inherit`) | The turn's user and assistant text | The same session model — with a cloud model, turn text reaches that cloud |
329
- | Relation checks | The candidate memory and the similar stored memory | Same routing as extraction; runs only when similarity selects a review candidate |
328
+ | Memory extraction (`llm.mode: inherit`) | The turn's user and assistant text | The same session model. With a cloud model, turn text reaches that cloud |
329
+ | Relation checks | The candidate memory and the similar stored memory | Same routing as extraction. It runs only when similarity selects a review candidate |
330
330
  | Embeddings (`embedding`) | Memory and search text | Ollama by default, or your OpenAI-compatible endpoint |
331
- | Storage (`qdrant`) | Memory payloads and provenance; the ledger stays local | Embedded local path, self-hosted server, or Qdrant Cloud |
331
+ | Storage (`qdrant`) | Memory payloads and provenance. The ledger stays local | Embedded local path, self-hosted server, or Qdrant Cloud |
332
332
 
333
333
  Three common deployments:
334
334
 
@@ -483,7 +483,7 @@ contains `memories` objects with `id`/`text`, and `queries` with `query`/`releva
483
483
  The default fixture now contains 24 labeled queries, including Chinese, English,
484
484
  mixed-language and hard-negative cases. The
485
485
  [original 12-query recording](docs/retrieval-baseline.json) is a historical
486
- synthetic regression baseline; rerun the current fixture for current measurements.
486
+ synthetic regression baseline. Rerun the current fixture for current measurements.
487
487
  These fixtures do not establish production recall quality. See the [recovery benchmark](docs/recovery-benchmark.md)
488
488
  for a separate synthetic replay and retained-storage measurement.
489
489
 
@@ -1,3 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: hermes-plugin-qdrant-memory
3
+ Version: 0.2.1
4
+ Summary: Durable Qdrant memory for Hermes, standalone CLI and local MCP
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/cnkang/hermes-plugin-qdrant-memory
7
+ Project-URL: Repository, https://github.com/cnkang/hermes-plugin-qdrant-memory
8
+ Project-URL: Issues, https://github.com/cnkang/hermes-plugin-qdrant-memory/issues
9
+ Project-URL: Changelog, https://github.com/cnkang/hermes-plugin-qdrant-memory/blob/main/CHANGELOG.md
10
+ Project-URL: MCP, https://github.com/cnkang/qdrant-memory-mcp
11
+ Project-URL: Skill, https://github.com/cnkang/skills/tree/main/qdrant-memory
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: qdrant-client<2,>=1.15
16
+ Requires-Dist: httpx<1,>=0.28
17
+ Requires-Dist: portalocker<4,>=3.2
18
+ Provides-Extra: mcp
19
+ Requires-Dist: mcp<2,>=1.30; extra == "mcp"
20
+ Dynamic: license-file
21
+
1
22
  # Qdrant Memory for Hermes
2
23
 
3
24
  [![CI](https://github.com/cnkang/hermes-plugin-qdrant-memory/actions/workflows/ci.yml/badge.svg)](https://github.com/cnkang/hermes-plugin-qdrant-memory/actions/workflows/ci.yml)
@@ -7,12 +28,11 @@
7
28
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
8
29
  [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
9
30
 
10
- > **v0.1.0 — limited technical preview.** Give Hermes Agent durable, private
11
- > long-term memory: your conversations become searchable memories in your own
12
- > Qdrant store, with recall, update and scoped deletion under your control.
13
- > The preview supports one writer per destination, with the documented
14
- > durability limits. See the [validation record](docs/validation.md) for the
15
- > current certification.
31
+ > **Runtime 0.2.1** provides the standalone CLI/MCP and fixes explicit writes of
32
+ > distinct, highly similar texts. MCP distribution 0.1.1 pins Runtime 0.2.1.
33
+ > The Hermes Plugin Catalog has its own reviewed immutable pin. A PyPI release
34
+ > does not upgrade it. One writer per destination remains required.
35
+ > See [release notes](docs/releases/0.2.1.md) and [validation](docs/validation.md).
16
36
 
17
37
  [English](README.md) | [简体中文](README.zh-CN.md)
18
38
 
@@ -26,6 +46,16 @@ as a community-maintained plugin.
26
46
 
27
47
  ## How it works
28
48
 
49
+ For non-Hermes users, install in a private Python 3.11+
50
+ environment: `pip install 'hermes-plugin-qdrant-memory[mcp]==0.2.1'` (omit `[mcp]`
51
+ for CLI only). `qdrant-memory` is the CLI and `mcp` is its server subcommand.
52
+ Standalone does not require Hermes. See [setup and quick start](docs/standalone.md).
53
+ MCP-focused agents may instead use the [metadata-only distribution](https://github.com/cnkang/qdrant-memory-mcp).
54
+ Public releases: [Runtime on PyPI](https://pypi.org/project/hermes-plugin-qdrant-memory/)
55
+ and [MCP distribution on PyPI](https://pypi.org/project/qdrant-memory-mcp/).
56
+ For native Hermes use its official plugin installation mechanism below. Before
57
+ an authorized upgrade, verify the same profile, collection and embedding pipeline.
58
+
29
59
  ```mermaid
30
60
  flowchart LR
31
61
  turn["Hermes turn"] --> admit["Provider callback"]
@@ -47,7 +77,7 @@ flowchart LR
47
77
  - A fully local setup works: embedded Qdrant and local Ollama keep the data
48
78
  on your machine.
49
79
  - Data locality follows the destination. Extraction uses the LLM configured
50
- in Hermes, and embedding uses the configured embedding service; either can
80
+ in Hermes, and embedding uses the configured embedding service. Either can
51
81
  be remote. See [Data flow and privacy](#data-flow-and-privacy).
52
82
  - Not supported in this preview: multi-writer Server/Cloud, hybrid search and
53
83
  a web UI.
@@ -56,9 +86,9 @@ flowchart LR
56
86
 
57
87
  The plugin primarily provides Hermes Agent's durable memory integration. As an
58
88
  additional capability, its shared backend can serve other agents through the
59
- Runtime 0.2.0's `qdrant-memory --home PATH` JSON CLI and optional local stdio MCP
60
- interface, without requiring Hermes. These interfaces support explicit scoped
61
- memory operations and maintenance/migration; automatic extraction and prefetch
89
+ Runtime's `qdrant-memory --home PATH` JSON CLI and optional local stdio MCP
90
+ interface. Standalone use does not require Hermes. These interfaces support explicit scoped
91
+ memory operations and maintenance/migration. Automatic extraction and prefetch
62
92
  remain Hermes lifecycle features. See [standalone setup](docs/standalone.md) for
63
93
  configuration and boundaries, and the agent-neutral
64
94
  [qdrant-memory skill](https://github.com/cnkang/skills/tree/main/qdrant-memory)
@@ -66,19 +96,20 @@ for multi-agent installation and usage guidance.
66
96
 
67
97
  The optional MCP server exposes four scoped tools: `qdrant_memory_search`,
68
98
  `qdrant_memory_add`, `qdrant_memory_update` and `qdrant_memory_delete`. Compatible
69
- clients launch it as a local stdio process and discover/call those tools;
70
- maintenance and migration use the operator CLI. See the
99
+ clients launch it as a local stdio process and discover/call those tools.
100
+ Maintenance and migration use the operator CLI. See the
71
101
  [MCP capabilities and connection guide](docs/mcp.md) for setup, shared-memory
72
- boundaries and the verified agent matrix. This is a tested source preview;
73
- the published v0.1.0 plugin release does not include the MCP entrypoint.
102
+ boundaries and the verified agent matrix. Runtime 0.2.0 published these interfaces.
103
+ The published v0.1.0 plugin release does not include the MCP entrypoint.
74
104
 
75
105
  ## See it in action
76
106
 
77
107
  ![Terminal recording: install the plugin, store a preference, and recall it in a new session](https://raw.githubusercontent.com/cnkang/hermes-plugin-qdrant-memory/main/assets/demo.gif)
78
108
 
79
- The recording shows the full flow on a clean profile: install from the
80
- official Hermes Plugin Catalog, teach a preference, wait for the background
81
- extraction, recall it in a new session, and clean up the demo scope. All steps
109
+ The recording shows the full flow on a clean profile. It installs from the
110
+ official Hermes Plugin Catalog, teaches a preference, and waits for the
111
+ background extraction. Then it recalls the preference in a new session and
112
+ cleans up the demo scope. All steps
82
113
  run in embedded mode. `scripts/demo.sh` reproduces the same walkthrough with
83
114
  the pinned release.
84
115
 
@@ -109,14 +140,17 @@ The install resolves the catalog's reviewed commit and prints the entry's declar
109
140
  tools, requirements and known issues before it clones anything.
110
141
  `hermes plugins enable` prepares the declared dependencies through Hermes PM.
111
142
 
112
- **Version-locked: install the pinned release through the Hermes CLI.**
143
+ **Version-locked: install the verified Runtime 0.2.1 source through the Hermes CLI.**
144
+
145
+ This immutable source revision includes the explicit-write fix. It does not
146
+ depend on a PyPI release or a catalog update.
113
147
 
114
148
  ```bash
115
149
  export HERMES_HOME="/absolute/path/to/your/hermes-profile"
116
150
  ollama pull qwen3-embedding:4b
117
151
  # Start Ollama if it is not running. Keep its service available.
118
152
  hermes plugins install https://github.com/cnkang/hermes-plugin-qdrant-memory \
119
- --ref 5babd3e4b4c022aad38a2b6db82cbd5b601f1ef8 # v0.1.0 release commit
153
+ --ref 8929693fcccd1efa91bfa062145e35d2369ca9cc # verified Runtime 0.2.1 source
120
154
  hermes plugins enable qdrant-memory
121
155
  hermes memory setup
122
156
  hermes config set memory.provider qdrant-memory
@@ -125,12 +159,15 @@ hermes qdrant-memory init
125
159
 
126
160
  For this version-locked form, `hermes plugins install` checks out the given ref, and
127
161
  `hermes plugins enable` prepares the declared dependencies through Hermes PM.
128
- `--ref` takes a full 40-character commit SHA; the
162
+ `--ref` takes a full 40-character commit SHA. The
129
163
  [releases page](https://github.com/cnkang/hermes-plugin-qdrant-memory/releases)
130
164
  lists the release commits. Do not pip-install into a managed Hermes
131
165
  environment. Omit `--ref` to track `main`, the development line.
132
166
 
133
- **Alternative: clone the release tag manually.**
167
+ **Historical release: clone a published tag manually.**
168
+
169
+ The v0.1.0 tag below predates the standalone interfaces and the explicit-write
170
+ fix. Use the pinned source above for Runtime 0.2.1.
134
171
 
135
172
  ```bash
136
173
  export HERMES_HOME="/absolute/path/to/your/hermes-profile"
@@ -159,7 +196,7 @@ import and registration. For development installs, see
159
196
  [development setup](docs/development.md).
160
197
 
161
198
  The release notes, supported Hermes/Python versions and the current
162
- limitations live in [docs/releases/v0.1.0.md](docs/releases/v0.1.0.md);
199
+ limitations live in [docs/releases/v0.1.0.md](docs/releases/v0.1.0.md).
163
200
  `main` stays the development line.
164
201
 
165
202
  For local development, symlink the repository into a disposable profile:
@@ -204,7 +241,10 @@ mappings) stays in the local Hermes home. Back up that home together with your
204
241
  Qdrant data. Migration from Mem0 is one-way, and version 0.1 has no vector
205
242
  reuse or hybrid retrieval.
206
243
 
207
- Similarity is a candidate-selection signal, not proof that two facts are identical.
244
+ Explicit add deduplicates only canonical text in the same scope and never calls
245
+ an LLM. Different texts are saved regardless of similarity. Update replaces only
246
+ the authorized exact ID. For automatic Hermes extraction,
247
+ similarity is a candidate-selection signal, not proof that two facts are identical.
208
248
  `SAME` skips, `SUPERSEDES` updates, `CONFLICT` creates a linked record, and
209
249
  `UNRELATED` adds a record. Migration uses source IDs instead of semantic merging.
210
250
 
@@ -286,7 +326,7 @@ default profile scope uses an agent (`hermes`). Pass `--agent hermes` to
286
326
  target it, or `--all-agents` to delete every agent scope of the user. Without
287
327
  `--all-agents`, the result reports `other_agent_scopes` when other agent
288
328
  scopes still hold memories. A literal `*` is an ordinary agent id, not a
289
- wildcard. `--dry-run` previews the per-scope counts;
329
+ wildcard. `--dry-run` previews the per-scope counts.
290
330
  `--confirm` authorizes deletion. An interrupted deletion fails closed: other
291
331
  commands and provider startup return `scope_delete_recovery_required` until
292
332
  `delete-all --confirm` resumes it. Deletion intents are versioned and explicit.
@@ -294,8 +334,8 @@ A pre-versioned intent cannot show whether `*` is a single agent or all agents,
294
334
  so the plugin refuses it (`scope_delete_legacy_intent`). Resolve it with
295
335
  `--resolve-legacy single_agent`, or with `--resolve-legacy all_agents` when the
296
336
  record's agent is `*` or absent. Turns admitted after the deletion can still
297
- add new memories; everything admitted before it — stored memories, prepared
298
- writes and unprocessed turn events — is removed or invalidated. `list`,
337
+ add new memories. Everything admitted before it, including stored memories, prepared
338
+ writes and unprocessed turn events, is removed or invalidated. `list`,
299
339
  `export` and `delete-all` do not require the embedding service.
300
340
 
301
341
  ## Data flow and privacy
@@ -306,10 +346,10 @@ carries and where it can go:
306
346
  | Component | Carries | Destination |
307
347
  | --- | --- | --- |
308
348
  | Chat / agent turns | Your conversation | The model configured for the Hermes session |
309
- | Memory extraction (`llm.mode: inherit`) | The turn's user and assistant text | The same session model — with a cloud model, turn text reaches that cloud |
310
- | Relation checks | The candidate memory and the similar stored memory | Same routing as extraction; runs only when similarity selects a review candidate |
349
+ | Memory extraction (`llm.mode: inherit`) | The turn's user and assistant text | The same session model. With a cloud model, turn text reaches that cloud |
350
+ | Relation checks | The candidate memory and the similar stored memory | Same routing as extraction. It runs only when similarity selects a review candidate |
311
351
  | Embeddings (`embedding`) | Memory and search text | Ollama by default, or your OpenAI-compatible endpoint |
312
- | Storage (`qdrant`) | Memory payloads and provenance; the ledger stays local | Embedded local path, self-hosted server, or Qdrant Cloud |
352
+ | Storage (`qdrant`) | Memory payloads and provenance. The ledger stays local | Embedded local path, self-hosted server, or Qdrant Cloud |
313
353
 
314
354
  Three common deployments:
315
355
 
@@ -464,7 +504,7 @@ contains `memories` objects with `id`/`text`, and `queries` with `query`/`releva
464
504
  The default fixture now contains 24 labeled queries, including Chinese, English,
465
505
  mixed-language and hard-negative cases. The
466
506
  [original 12-query recording](docs/retrieval-baseline.json) is a historical
467
- synthetic regression baseline; rerun the current fixture for current measurements.
507
+ synthetic regression baseline. Rerun the current fixture for current measurements.
468
508
  These fixtures do not establish production recall quality. See the [recovery benchmark](docs/recovery-benchmark.md)
469
509
  for a separate synthetic replay and retained-storage measurement.
470
510
 
@@ -41,6 +41,7 @@ tests/test_cli_output.py
41
41
  tests/test_config_embedding.py
42
42
  tests/test_deletion_ordering.py
43
43
  tests/test_discovery_cli.py
44
+ tests/test_explicit_dedupe.py
44
45
  tests/test_host_lifecycle.py
45
46
  tests/test_input_validation.py
46
47
  tests/test_inventory_export.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "hermes-plugin-qdrant-memory"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Durable Qdrant memory for Hermes, standalone CLI and local MCP"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -23,6 +23,8 @@ Homepage = "https://github.com/cnkang/hermes-plugin-qdrant-memory"
23
23
  Repository = "https://github.com/cnkang/hermes-plugin-qdrant-memory"
24
24
  Issues = "https://github.com/cnkang/hermes-plugin-qdrant-memory/issues"
25
25
  Changelog = "https://github.com/cnkang/hermes-plugin-qdrant-memory/blob/main/CHANGELOG.md"
26
+ MCP = "https://github.com/cnkang/qdrant-memory-mcp"
27
+ Skill = "https://github.com/cnkang/skills/tree/main/qdrant-memory"
26
28
 
27
29
  [project.entry-points."hermes_agent.memory_providers"]
28
30
  qdrant-memory = "qdrant_memory:register"
@@ -3,6 +3,14 @@
3
3
  from .models import content_hash
4
4
 
5
5
 
6
+ def decide_explicit(text, scope, store):
7
+ """Deduplicate authorized explicit writes only by canonical hash in scope."""
8
+ exact = store.lookup_hash(scope, content_hash(text))
9
+ if exact:
10
+ return "SKIP", str(exact.id), {}
11
+ return "ADD", None, {}
12
+
13
+
6
14
  def decide(text, scope, store, extractor, cfg):
7
15
  """Return ADD, UPDATE or SKIP after scoped hash and factual relation checks.
8
16
 
@@ -1,5 +1,5 @@
1
1
  name: qdrant-memory
2
- version: 0.2.0
2
+ version: 0.2.1
3
3
  description: Durable native memory with Qdrant and Hermes LLM extraction
4
4
  manifest_version: 2
5
5
  author: Kang Liu
@@ -5,7 +5,7 @@ import time
5
5
  from itertools import islice
6
6
  from threading import RLock
7
7
 
8
- from .dedupe import decide
8
+ from .dedupe import decide, decide_explicit
9
9
  from .models import Scope, content_hash, enforce_limits, now, payload, point_id
10
10
  from .progress import report
11
11
  from .retry import run_with_retry
@@ -163,11 +163,9 @@ class Runtime:
163
163
  return failures
164
164
 
165
165
  def add(self, text, scope, source="manual_tool", session_id="", **fields):
166
- """Add or reconcile a scoped fact through hash and factual relation review."""
166
+ """Save an explicit scoped fact with deterministic exact-text deduplication."""
167
167
  with self.lock:
168
- action, identifier, relation = decide(
169
- text, scope, self.store, self.extractor, self.cfg["dedupe"]
170
- )
168
+ action, identifier, relation = decide_explicit(text, scope, self.store)
171
169
  self.decisions[action] += 1
172
170
  self.ledger.increment(action)
173
171
  if action == "SKIP":
@@ -1,3 +1,3 @@
1
1
  """Single version source shared by registration and migration manifests."""
2
2
 
3
- VERSION = "0.2.0"
3
+ VERSION = "0.2.1"
@@ -0,0 +1,80 @@
1
+ """Explicit intent and automatic factual adjudication remain separate policies."""
2
+
3
+ import math
4
+
5
+ import pytest
6
+
7
+ from qdrant_memory.models import Scope
8
+ from qdrant_memory.tools import dispatch
9
+
10
+ from .helpers import LLM, runtime
11
+
12
+
13
+ @pytest.mark.parametrize("score", [0.92, 0.97, 1.0])
14
+ @pytest.mark.parametrize("with_extractor", [False, True])
15
+ def test_explicit_add_keeps_distinct_similar_facts(tmp_path, score, with_extractor):
16
+ """Similarity cannot discard explicit intent, even when an LLM reports SAME."""
17
+ llm = LLM("SAME")
18
+ rt = runtime(tmp_path, llm=llm)
19
+ if not with_extractor:
20
+ rt.extractor = None
21
+ scope = Scope("alice", "hermes")
22
+ rt.store.embedder.embed_query = lambda text: (
23
+ [1.0, 0.0, 0.0] if "preferred" in text else [score, math.sqrt(1 - score**2), 0.0]
24
+ )
25
+ try:
26
+ first = dispatch(rt, "qdrant_memory_add", {"text": "cats preferred"}, scope, "s")
27
+ assert rt.store.search("cats avoided", scope)[0].score == pytest.approx(score)
28
+ second = dispatch(rt, "qdrant_memory_add", {"text": "cats avoided"}, scope, "s")
29
+ assert first["action"] == second["action"] == "ADD"
30
+ assert first["id"] != second["id"]
31
+ duplicate = rt.add(" cats preferred ", scope)
32
+ assert duplicate == {"action": "SKIP", "id": first["id"]}
33
+ for other in (Scope("bob", "hermes"), Scope("alice", "other")):
34
+ assert rt.add("cats preferred", other)["id"] != first["id"]
35
+ assert rt.store.count(other) == 1
36
+ assert rt.store.count(scope) == 2
37
+ assert not llm.calls
38
+ finally:
39
+ rt.store.close()
40
+ rt.ledger.close()
41
+
42
+
43
+ @pytest.mark.parametrize(
44
+ "relation,count,texts",
45
+ [
46
+ ("SAME", 1, {"cats preferred"}),
47
+ ("SUPERSEDES", 1, {"cats avoided"}),
48
+ ("CONFLICT", 2, {"cats preferred", "cats avoided"}),
49
+ ("UNRELATED", 2, {"cats preferred", "cats avoided"}),
50
+ ],
51
+ )
52
+ def test_automatic_events_retain_factual_relations(tmp_path, relation, count, texts):
53
+ """Real extraction and durable event processing still adjudicate similar facts."""
54
+ llm = LLM(relation)
55
+ rt = runtime(tmp_path, llm=llm)
56
+ scope = Scope("alice", "hermes")
57
+ try:
58
+ first = rt.add("cats preferred", scope)
59
+ key = rt.ledger.enqueue_event(
60
+ {
61
+ "kind": "turn",
62
+ "scope": scope.as_dict(),
63
+ "source": "conversation",
64
+ "session_id": "s",
65
+ "user": "cats avoided",
66
+ "assistant": "ack",
67
+ }
68
+ )
69
+ rt.process_event(key)
70
+ assert rt.store.count(scope) == count
71
+ hits = rt.store.search("cats", scope)
72
+ assert {h.payload["text"] for h in hits} == texts
73
+ if relation == "CONFLICT":
74
+ conflict = next(h for h in hits if h.payload["text"] == "cats avoided")
75
+ assert conflict.payload["metadata"]["conflicts_with"] == first["id"]
76
+ assert any(c["purpose"] == "qdrant-memory.relation" for c in llm.calls)
77
+ assert rt.ledger.row("events", key)["status"] == "COMMITTED"
78
+ finally:
79
+ rt.store.close()
80
+ rt.ledger.close()
@@ -17,6 +17,13 @@ def test_manifests_declare_v2_metadata_and_match_runtime_dependencies():
17
17
 
18
18
  assert manifests[0] == manifests[1]
19
19
  manifest = manifests[0]
20
+ from qdrant_memory.version import VERSION
21
+
22
+ assert str(manifest["version"]) == project["version"] == VERSION
23
+ for name in ("README.md", "README.zh-CN.md", "docs/standalone.md", "docs/mcp.md"):
24
+ text = (ROOT / name).read_text(encoding="utf-8")
25
+ assert f"hermes-plugin-qdrant-memory[mcp]=={VERSION}" in text
26
+ assert "hermes-plugin-qdrant-memory[mcp]==0.2.0" not in text
20
27
  assert manifest["manifest_version"] == 2
21
28
  assert manifest["author"] == "Kang Liu"
22
29
  assert manifest["license"] == "MIT"