memleaf 0.2.20__tar.gz → 0.2.22__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 (110) hide show
  1. {memleaf-0.2.20 → memleaf-0.2.22}/CHANGELOG.md +20 -0
  2. {memleaf-0.2.20/src/memleaf.egg-info → memleaf-0.2.22}/PKG-INFO +3 -3
  3. {memleaf-0.2.20 → memleaf-0.2.22}/README.en.md +8 -7
  4. {memleaf-0.2.20 → memleaf-0.2.22}/README.md +2 -2
  5. {memleaf-0.2.20 → memleaf-0.2.22}/pyproject.toml +1 -1
  6. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/__init__.py +1 -1
  7. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/adapters/hermes.py +3 -2
  8. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/hermes_provider/__init__.py +8 -2
  9. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/hermes_provider/plugin.yaml +1 -1
  10. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/installer.py +7 -4
  11. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/processing.py +509 -46
  12. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/validation.py +2 -1
  13. {memleaf-0.2.20 → memleaf-0.2.22/src/memleaf.egg-info}/PKG-INFO +3 -3
  14. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_hermes_provider.py +5 -0
  15. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_pypi_install.py +9 -2
  16. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_stage_c1_mcp.py +1 -1
  17. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_stage_c3_packaging.py +1 -1
  18. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_todo_state_recovery.py +126 -0
  19. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_update_target_recovery.py +134 -0
  20. {memleaf-0.2.20 → memleaf-0.2.22}/IMPLEMENTATION_PLAN.md +0 -0
  21. {memleaf-0.2.20 → memleaf-0.2.22}/LICENSE +0 -0
  22. {memleaf-0.2.20 → memleaf-0.2.22}/MANIFEST.in +0 -0
  23. {memleaf-0.2.20 → memleaf-0.2.22}/RELEASE_CHECKLIST.md +0 -0
  24. {memleaf-0.2.20 → memleaf-0.2.22}/examples/README.md +0 -0
  25. {memleaf-0.2.20 → memleaf-0.2.22}/examples/basic_usage.py +0 -0
  26. {memleaf-0.2.20 → memleaf-0.2.22}/examples/mcp_stdio.ndjson +0 -0
  27. {memleaf-0.2.20 → memleaf-0.2.22}/install.ps1 +0 -0
  28. {memleaf-0.2.20 → memleaf-0.2.22}/install.sh +0 -0
  29. {memleaf-0.2.20 → memleaf-0.2.22}/setup.cfg +0 -0
  30. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/__main__.py +0 -0
  31. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/adapters/__init__.py +0 -0
  32. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/adapters/antigravity.py +0 -0
  33. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/adapters/base.py +0 -0
  34. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/adapters/codex.py +0 -0
  35. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/budget.py +0 -0
  36. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/capture.py +0 -0
  37. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/cli.py +0 -0
  38. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/compaction.py +0 -0
  39. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/config.py +0 -0
  40. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/credentials.py +0 -0
  41. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/frontmatter.py +0 -0
  42. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/hermes_provider/README.md +0 -0
  43. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/host_events.py +0 -0
  44. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/host_runtime.py +0 -0
  45. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/inbox.py +0 -0
  46. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/index.py +0 -0
  47. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/llm/__init__.py +0 -0
  48. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/llm/base.py +0 -0
  49. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/llm/claude_compatible.py +0 -0
  50. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/llm/gemini.py +0 -0
  51. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/llm/openai_compatible.py +0 -0
  52. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/llm/router.py +0 -0
  53. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/locking.py +0 -0
  54. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/mcp_server.py +0 -0
  55. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/memory_writer.py +0 -0
  56. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/model_discovery.py +0 -0
  57. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/models.py +0 -0
  58. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/native_index.py +0 -0
  59. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/native_registration.py +0 -0
  60. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/prompts.py +0 -0
  61. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/redaction.py +0 -0
  62. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/retrieval.py +0 -0
  63. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/retrieval_gate.py +0 -0
  64. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/scope_maintenance.py +0 -0
  65. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/scope_state.py +0 -0
  66. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/service.py +0 -0
  67. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf/vault.py +0 -0
  68. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf.egg-info/SOURCES.txt +0 -0
  69. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf.egg-info/dependency_links.txt +0 -0
  70. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf.egg-info/entry_points.txt +0 -0
  71. {memleaf-0.2.20 → memleaf-0.2.22}/src/memleaf.egg-info/top_level.txt +0 -0
  72. {memleaf-0.2.20 → memleaf-0.2.22}/tests/__init__.py +0 -0
  73. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_admission_noise.py +0 -0
  74. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_codex_install.py +0 -0
  75. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_codex_native_cli.py +0 -0
  76. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_context_budget.py +0 -0
  77. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_credential_safety.py +0 -0
  78. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_cross_host_acceptance.py +0 -0
  79. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_cross_turn_dedupe_regressions.py +0 -0
  80. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_extraction_quality_regressions.py +0 -0
  81. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_global_todo_acceptance.py +0 -0
  82. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_global_todo_retrieval.py +0 -0
  83. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_hermes_native_registration.py +0 -0
  84. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_hermes_stdio_transport.py +0 -0
  85. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_host_events.py +0 -0
  86. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_host_runtime_contract.py +0 -0
  87. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_install.py +0 -0
  88. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_maintenance_v2.py +0 -0
  89. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_model_discovery.py +0 -0
  90. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_retrieval_gate.py +0 -0
  91. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_retrieval_v2.py +0 -0
  92. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_session_lineage.py +0 -0
  93. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_stage_a.py +0 -0
  94. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_stage_b1.py +0 -0
  95. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_stage_b2a.py +0 -0
  96. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_stage_b2b.py +0 -0
  97. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_stage_b3a_commit.py +0 -0
  98. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_stage_b3a_contract.py +0 -0
  99. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_stage_b3b_native_context.py +0 -0
  100. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_stage_b3b_native_index.py +0 -0
  101. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_stage_b3b_scope.py +0 -0
  102. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_stage_b3c_retrieval.py +0 -0
  103. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_stage_b3d_scope_maintenance.py +0 -0
  104. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_stage_c2_init.py +0 -0
  105. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_upgrade_preserves_vault.py +0 -0
  106. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_v2_gate_limits.py +0 -0
  107. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_v2_host_flow.py +0 -0
  108. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_v2_mcp_flow.py +0 -0
  109. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_v2_nomatch_semantics.py +0 -0
  110. {memleaf-0.2.20 → memleaf-0.2.22}/tests/test_v2_search_gate_acceptance.py +0 -0
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to memleaf are documented here.
4
4
 
5
+ ## 0.2.22 — 2026-09-03
6
+
7
+ - Safely split valid multi-project `unscoped` aggregate candidates using only
8
+ project names grounded in each candidate, so one ambiguous fragment no
9
+ longer blocks valid project-local siblings.
10
+ - Persist explicit completion of an existing todo separately from new
11
+ customer rework, preserving immutable targets and making replay idempotent.
12
+ - Require declarative completion language before closing todos, avoiding false
13
+ completion for future actions such as needing to submit, waiting for
14
+ feedback, or being asked to send materials.
15
+ - Keep Hermes from claiming permanent persistence before automatic processing
16
+ succeeds; only a successful explicit remember/update result authorizes that
17
+ claim.
18
+
19
+ ## 0.2.21 — 2026-09-03
20
+
21
+ - Fix `memleaf install` for Hermes after the 12th MCP tool was added: the installer now uses the adapter's canonical tool-count constant instead of a stale private `11`.
22
+ - Add a regression invariant that compares the Hermes installer count with the actual public MCP tool tuple, preventing future tool additions from silently breaking installation self-checks.
23
+ - Correct the English README's stale 11-tool and 3-memory/6000-character descriptions so the published documentation matches the v0.2 retrieval contract.
24
+
5
25
  ## 0.2.20 — 2026-09-03
6
26
 
7
27
  - Add `list_todos` for complete active todo retrieval across all scopes with stable pagination, date/status filtering, and current-turn retrieval gating.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memleaf
3
- Version: 0.2.20
3
+ Version: 0.2.22
4
4
  Summary: A local-first Markdown memory core for AI agents
5
5
  Author: memleaf contributors
6
6
  License-Expression: MIT
@@ -23,8 +23,8 @@ Dynamic: license-file
23
23
 
24
24
  [English](README.en.md) · [PyPI](https://pypi.org/project/memleaf/) · [GitHub](https://github.com/miffyblueboo/memleaf)
25
25
 
26
- > **当前版本:0.2.20。**
27
- > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.20 通过 PyPI 分发。
26
+ > **当前版本:0.2.22。**
27
+ > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.22 通过 PyPI 分发。
28
28
  > **当前版本支持 Hermes 和 Codex。** Antigravity(反重力)不检测、不安装、不配置。
29
29
 
30
30
  ## 项目定位
@@ -4,8 +4,8 @@
4
4
 
5
5
  [中文](README.md) · [PyPI](https://pypi.org/project/memleaf/) · [GitHub](https://github.com/miffyblueboo/memleaf)
6
6
 
7
- > **Version: 0.2.20.**
8
- > The core library, Vault, stdio MCP server, initialization CLI, model routing, memory extraction, controlled retrieval protocol, and host adapters are implemented. memleaf 0.2.20 is distributed through PyPI.
7
+ > **Version: 0.2.22.**
8
+ > The core library, Vault, stdio MCP server, initialization CLI, model routing, memory extraction, controlled retrieval protocol, and host adapters are implemented. memleaf 0.2.22 is distributed through PyPI.
9
9
  > **The current release supports Hermes and Codex.** Antigravity is not detected, installed, or configured.
10
10
 
11
11
  ## Project scope
@@ -55,7 +55,7 @@ Current limits:
55
55
  - Scope Map: at most 20 items and approximately 2,000 characters per page;
56
56
  - search candidates: at most 20 items and approximately 4,000 characters per page;
57
57
  - one read page: at most 2,000 body characters;
58
- - managed turn: at most 3 distinct memory IDs and 6,000 body characters in total;
58
+ - managed turn: all relevant memories may be read; `read_count` and `read_chars` are audit-only and do not block valid reads;
59
59
  - `retrieval_id` must belong to the current turn, and a successful `search` is required before `read`;
60
60
  - `found`, `no_match`, and tool errors are distinct states; an error must not be reported as no match;
61
61
  - `context()` and Python `search(view="full")` remain available for compatibility, but are not part of the automatic retrieval path.
@@ -141,12 +141,12 @@ Both installation paths automatically:
141
141
  4. Activate `memory.provider=memleaf`.
142
142
  5. Configure the memleaf MCP entry through Hermes' official CLI.
143
143
  6. Configure MCP lazy/idle lifecycle settings.
144
- 7. Verify that the MCP server exposes all 11 tools.
144
+ 7. Verify that the MCP server exposes all 12 tools.
145
145
  8. Record the local Agent integration status.
146
146
 
147
147
  Restart Hermes after installation.
148
148
 
149
- If Hermes cannot be detected, no complete model route can be configured, Provider activation fails, or the 11-tool MCP verification fails, the installer returns an explicit failure rather than reporting an incomplete integration as successful.
149
+ If Hermes cannot be detected, no complete model route can be configured, Provider activation fails, or the 12-tool MCP verification fails, the installer returns an explicit failure rather than reporting an incomplete integration as successful.
150
150
 
151
151
  The repository `install.sh` remains for source development, offline source installation, and troubleshooting. Normal PyPI users do not need to run it.
152
152
 
@@ -243,7 +243,7 @@ python -m memleaf.mcp_server --vault "$HOME/.memleaf"
243
243
 
244
244
  Without `--vault`, the server uses `~/.memleaf`; `MEMLEAF_VAULT` can also specify the Vault. In normal use the server does not need to be kept running manually: Hermes or Codex starts it on demand. stdout contains only JSON-RPC messages so logs do not corrupt the protocol stream.
245
245
 
246
- The server currently exposes 11 tools:
246
+ The server currently exposes 12 tools:
247
247
 
248
248
  | Tool | Purpose |
249
249
  | --- | --- |
@@ -251,6 +251,7 @@ The server currently exposes 11 tools:
251
251
  | `context` | Compatibility directory; not used by the automatic retrieval path |
252
252
  | `scope_catalog` | Return Scopes, parents, and aliases without memory bodies |
253
253
  | `search` | Return a bounded candidate directory and `found`/`no_match` status |
254
+ | `list_todos` | Enumerate current todo memories across scopes with status/date filters and pagination |
254
255
  | `read` | Read a selected memory body in pages using the current `retrieval_id` |
255
256
  | `process` | Process complete inbox turns under the admission rules |
256
257
  | `remember` | Create or update memory after an explicit request |
@@ -259,7 +260,7 @@ The server currently exposes 11 tools:
259
260
  | `rebuild_index` | Rebuild local derived indexes |
260
261
  | `stats` | Return Vault counts and diagnostic statistics |
261
262
 
262
- Search results are clues; a title alone is not a business fact. Managed retrieval must use the same `retrieval_id` for `search → read`. Tool errors, Scope conflicts, and exhausted read budgets must be handled as such.
263
+ Search results are clues; a title alone is not a business fact. Managed retrieval must use the same `retrieval_id` for `search → read`. Tool errors, Scope conflicts, retrieval-turn violations, and read page/version errors must be handled as such.
263
264
 
264
265
  ## Python API
265
266
 
@@ -4,8 +4,8 @@
4
4
 
5
5
  [English](README.en.md) · [PyPI](https://pypi.org/project/memleaf/) · [GitHub](https://github.com/miffyblueboo/memleaf)
6
6
 
7
- > **当前版本:0.2.20。**
8
- > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.20 通过 PyPI 分发。
7
+ > **当前版本:0.2.22。**
8
+ > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.22 通过 PyPI 分发。
9
9
  > **当前版本支持 Hermes 和 Codex。** Antigravity(反重力)不检测、不安装、不配置。
10
10
 
11
11
  ## 项目定位
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "memleaf"
7
- version = "0.2.20"
7
+ version = "0.2.22"
8
8
  description = "A local-first Markdown memory core for AI agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,6 +1,6 @@
1
1
  """Local-first Markdown memory core for AI agents."""
2
2
 
3
- __version__ = "0.2.20"
3
+ __version__ = "0.2.22"
4
4
 
5
5
  from .config import DEFAULT_CONFIG, default_config, load_config, save_config
6
6
  from .frontmatter import FrontmatterError, dump_frontmatter, dump_yaml, load_yaml, parse_frontmatter
@@ -24,7 +24,8 @@ from .base import (
24
24
 
25
25
 
26
26
  _MCP_IDLE_TIMEOUT_SECONDS = 60
27
- _MCP_EXPECTED_TOOL_COUNT = 12
27
+ MCP_EXPECTED_TOOL_COUNT = 12
28
+ _MCP_EXPECTED_TOOL_COUNT = MCP_EXPECTED_TOOL_COUNT
28
29
 
29
30
 
30
31
  def hermes_home_for_platform(
@@ -417,7 +418,7 @@ class HermesAdapter:
417
418
  self,
418
419
  detection: Detection | None = None,
419
420
  *,
420
- expected_tools: int = _MCP_EXPECTED_TOOL_COUNT,
421
+ expected_tools: int = MCP_EXPECTED_TOOL_COUNT,
421
422
  ) -> bool:
422
423
  """Run the official MCP test and require the expected tool count."""
423
424
 
@@ -1964,8 +1964,14 @@ class MemleafMemoryProvider(MemoryProvider):
1964
1964
  "observer only: do not claim a search happened unless the visible tool "
1965
1965
  "messages show it. Visible Hermes "
1966
1966
  "turns are durably captured into the local memleaf inbox. Automatic "
1967
- "capture → process is authoritative: if process fails, report that "
1968
- "automatic processing failed and leave the inbox retryable. Do not use "
1967
+ "capture → process runs after your final answer and is authoritative: "
1968
+ "a capture only proves that the inbox received the turn. Until a "
1969
+ "successful explicit remember or update tool result is visible, never "
1970
+ "say that the turn was saved to permanent memory, persisted, or "
1971
+ "记好了/已落库. For automatic handling, say only that you will process "
1972
+ "it through the workflow or that it is recorded in the current "
1973
+ "conversation. If process fails, report that automatic processing "
1974
+ "failed and leave the inbox retryable. Do not use "
1969
1975
  "terminal, search_files, read_file, Python, or direct filesystem "
1970
1976
  "operations to search or read the memleaf Vault; those tools remain "
1971
1977
  "available for ordinary project/wiki files. Do not use direct vault "
@@ -1,5 +1,5 @@
1
1
  name: memleaf
2
- version: 0.2.20
2
+ version: 0.2.22
3
3
  description: "Hermes-native external MemoryProvider for local-first Markdown memory shared with the memleaf MCP server."
4
4
  hooks:
5
5
  - prefetch
@@ -19,14 +19,17 @@ from typing import Any
19
19
  from . import __version__
20
20
  from .adapters.base import update_agents_index
21
21
  from .adapters.codex import CodexAdapter
22
- from .adapters.hermes import HermesAdapter, hermes_home_for_platform
22
+ from .adapters.hermes import (
23
+ HermesAdapter,
24
+ MCP_EXPECTED_TOOL_COUNT,
25
+ hermes_home_for_platform,
26
+ )
23
27
  from .cli import _home_from_environment, _prepare_model_route
24
28
  from .locking import atomic_write_json
25
29
  from .native_registration import ensure_hermes_native_sources
26
30
  from .vault import Vault
27
31
 
28
32
 
29
- _EXPECTED_TOOLS = 11
30
33
  _PROVIDER_VERSION_RE = re.compile(r"^version:\s*([^\s#]+)\s*(?:#.*)?$", re.MULTILINE)
31
34
 
32
35
 
@@ -401,14 +404,14 @@ def install_hermes(*, vault_path: Path | None = None) -> dict[str, Any]:
401
404
  "provider": str(provider_path),
402
405
  "model": model,
403
406
  }
404
- if not adapter.test_mcp(detection, expected_tools=_EXPECTED_TOOLS):
407
+ if not adapter.test_mcp(detection, expected_tools=MCP_EXPECTED_TOOL_COUNT):
405
408
  update_agents_index(
406
409
  vault.agents_index_path,
407
410
  {"hermes": {"mcp_status": "failed", "mcp_availability": "unavailable"}},
408
411
  )
409
412
  return {
410
413
  "status": "failure",
411
- "reason": "Hermes MCP test did not confirm 11 tools",
414
+ "reason": f"Hermes MCP test did not confirm {MCP_EXPECTED_TOOL_COUNT} tools",
412
415
  "core_version": core_version,
413
416
  "provider_version": provider_version,
414
417
  "vault": str(vault.root),