memleaf 0.2.23__tar.gz → 0.2.25__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.
- {memleaf-0.2.23 → memleaf-0.2.25}/CHANGELOG.md +20 -0
- {memleaf-0.2.23/src/memleaf.egg-info → memleaf-0.2.25}/PKG-INFO +3 -3
- {memleaf-0.2.23 → memleaf-0.2.25}/README.en.md +2 -2
- {memleaf-0.2.23 → memleaf-0.2.25}/README.md +2 -2
- {memleaf-0.2.23 → memleaf-0.2.25}/pyproject.toml +1 -1
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/__init__.py +1 -1
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/cli.py +124 -14
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/hermes_provider/README.md +28 -4
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/hermes_provider/plugin.yaml +1 -1
- memleaf-0.2.25/src/memleaf/hermes_runtime.py +349 -0
- memleaf-0.2.25/src/memleaf/installer.py +1100 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/processing.py +471 -9
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/prompts.py +12 -1
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/validation.py +2 -2
- {memleaf-0.2.23 → memleaf-0.2.25/src/memleaf.egg-info}/PKG-INFO +3 -3
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf.egg-info/SOURCES.txt +4 -0
- memleaf-0.2.25/tests/test_email_actionable_coverage.py +369 -0
- memleaf-0.2.25/tests/test_global_todo_query_no_write.py +444 -0
- memleaf-0.2.25/tests/test_hermes_runtime_install.py +642 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_host_events.py +8 -2
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_pypi_install.py +26 -11
- memleaf-0.2.23/src/memleaf/installer.py +0 -547
- {memleaf-0.2.23 → memleaf-0.2.25}/IMPLEMENTATION_PLAN.md +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/LICENSE +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/MANIFEST.in +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/RELEASE_CHECKLIST.md +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/examples/README.md +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/examples/basic_usage.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/examples/mcp_stdio.ndjson +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/install.ps1 +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/install.sh +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/setup.cfg +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/__main__.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/adapters/__init__.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/adapters/antigravity.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/adapters/base.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/adapters/codex.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/adapters/hermes.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/budget.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/capture.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/compaction.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/config.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/credentials.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/frontmatter.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/hermes_provider/__init__.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/host_events.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/host_runtime.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/inbox.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/index.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/llm/__init__.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/llm/base.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/llm/claude_compatible.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/llm/gemini.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/llm/openai_compatible.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/llm/router.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/locking.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/mcp_server.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/memory_writer.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/model_discovery.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/models.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/native_index.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/native_registration.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/redaction.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/retrieval.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/retrieval_gate.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/scope_maintenance.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/scope_state.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/service.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf/vault.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf.egg-info/dependency_links.txt +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf.egg-info/entry_points.txt +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/src/memleaf.egg-info/top_level.txt +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/__init__.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_admission_noise.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_codex_install.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_codex_native_cli.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_context_budget.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_credential_safety.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_cross_host_acceptance.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_cross_turn_dedupe_regressions.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_extraction_quality_regressions.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_global_todo_acceptance.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_global_todo_retrieval.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_hermes_native_registration.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_hermes_provider.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_hermes_stdio_transport.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_host_runtime_contract.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_install.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_maintenance_v2.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_model_discovery.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_retrieval_gate.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_retrieval_v2.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_session_lineage.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_stage_a.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_stage_b1.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_stage_b2a.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_stage_b2b.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_stage_b3a_commit.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_stage_b3a_contract.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_stage_b3b_native_context.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_stage_b3b_native_index.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_stage_b3b_scope.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_stage_b3c_retrieval.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_stage_b3d_scope_maintenance.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_stage_c1_mcp.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_stage_c2_init.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_stage_c3_packaging.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_todo_state_recovery.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_update_target_recovery.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_upgrade_preserves_vault.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_v023_scope_correction.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_v2_gate_limits.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_v2_host_flow.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_v2_mcp_flow.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/tests/test_v2_nomatch_semantics.py +0 -0
- {memleaf-0.2.23 → memleaf-0.2.25}/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.25 — 2026-09-04
|
|
6
|
+
|
|
7
|
+
- Keep global todo questions and cross-session recaps strictly read-only,
|
|
8
|
+
without creating memories, appending sources, or producing history; an
|
|
9
|
+
explicit user assertion before a follow-up question remains write-eligible.
|
|
10
|
+
- Recover project-grounded, explicitly counted repair items omitted from a
|
|
11
|
+
mailbox extraction gate, split concrete detail lines into independent todos,
|
|
12
|
+
and reuse an existing same-scope item when it is an update.
|
|
13
|
+
- Record compact candidate-level `CREATE`, `UPDATE`, `NO_CHANGE`, and
|
|
14
|
+
`DEFERRED` dispositions in the processed-turn ledger for auditable coverage.
|
|
15
|
+
|
|
16
|
+
## 0.2.24 — 2026-09-04
|
|
17
|
+
|
|
18
|
+
- Make Hermes MCP installation reliably persistent: write `mcp_servers.memleaf` through Hermes' canonical `config set` interface, disable the entry while command/arguments are updated, then read `config.yaml` back and require an exact command plus `--vault` match before reporting success.
|
|
19
|
+
- Detect a same-Vault MCP entry that points at a different absolute memleaf runtime before changing Hermes. Add explicit `--mcp-runtime auto|current|existing` policies, prefer the current Python interpreter's scripts directory over `PATH`, and require an exact version check before retaining another environment.
|
|
20
|
+
- Snapshot Hermes `config.yaml`, `memleaf.json`, and `plugins/memleaf` during installation; on a later failure, attempt every rollback target even when one restore fails, and report the failed paths together with the installation stage and recovery commands.
|
|
21
|
+
- Improve human and JSON diagnostics for MCP persistence, runtime conflicts, lifecycle/test failures, and rollback results; document `memleaf-mcp --vault`, manual YAML, and the difference between Hermes connection discovery and final configuration persistence.
|
|
22
|
+
- Make Windows CI fail immediately after every native command and keep POSIX command-rendering assertions platform-specific, preventing intermediate Windows test failures from being reported as green.
|
|
23
|
+
- Leave memory extraction, CREATE/UPDATE/NO_CHANGE decisions, retrieval, injection, todo handling, and globally shared Vault visibility unchanged.
|
|
24
|
+
|
|
5
25
|
## 0.2.23 — 2026-09-03
|
|
6
26
|
|
|
7
27
|
- Add a strict `scope_correction` transaction for explicit cross-project corrections without weakening ordinary UPDATE scope isolation: uniquely recover the wrong active target, reuse its `memory_id` when appropriate, or retire it to history with `invalidated_reason: scope_correction` and `superseded_by` when a correct active survivor already exists.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memleaf
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.25
|
|
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.
|
|
27
|
-
> 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.
|
|
26
|
+
> **当前版本:0.2.25。**
|
|
27
|
+
> 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.25 通过 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.
|
|
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.
|
|
7
|
+
> **Version: 0.2.25.**
|
|
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.25 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
|
|
@@ -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.
|
|
8
|
-
> 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.
|
|
7
|
+
> **当前版本:0.2.25。**
|
|
8
|
+
> 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.25 通过 PyPI 分发。
|
|
9
9
|
> **当前版本支持 Hermes 和 Codex。** Antigravity(反重力)不检测、不安装、不配置。
|
|
10
10
|
|
|
11
11
|
## 项目定位
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Local-first Markdown memory core for AI agents."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.2.
|
|
3
|
+
__version__ = "0.2.25"
|
|
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
|
|
@@ -6,6 +6,8 @@ import argparse
|
|
|
6
6
|
import getpass
|
|
7
7
|
import json
|
|
8
8
|
import os
|
|
9
|
+
import shlex
|
|
10
|
+
import subprocess
|
|
9
11
|
import sys
|
|
10
12
|
from pathlib import Path
|
|
11
13
|
from typing import Sequence
|
|
@@ -65,6 +67,15 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
65
67
|
help="host to configure (default: hermes)",
|
|
66
68
|
)
|
|
67
69
|
install.add_argument("--vault", type=Path, default=None, help="vault directory")
|
|
70
|
+
install.add_argument(
|
|
71
|
+
"--mcp-runtime",
|
|
72
|
+
choices=("auto", "current", "existing"),
|
|
73
|
+
default="auto",
|
|
74
|
+
help=(
|
|
75
|
+
"Hermes only: fail on a second memleaf runtime (auto), migrate to "
|
|
76
|
+
"this installation (current), or retain a version-matched configured runtime (existing)"
|
|
77
|
+
),
|
|
78
|
+
)
|
|
68
79
|
install.add_argument("--json", action="store_true", help="emit one JSON result")
|
|
69
80
|
host_event = commands.add_parser(
|
|
70
81
|
"host-event",
|
|
@@ -87,6 +98,96 @@ def _configure_stdio_utf8() -> None:
|
|
|
87
98
|
continue
|
|
88
99
|
|
|
89
100
|
|
|
101
|
+
def _format_command(command: Sequence[object]) -> str:
|
|
102
|
+
values = [str(item) for item in command]
|
|
103
|
+
return subprocess.list2cmdline(values) if os.name == "nt" else shlex.join(values)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _print_install_failure(output: dict, *, host: str) -> None:
|
|
107
|
+
print(
|
|
108
|
+
f"memleaf install failed: {output.get('reason', 'unknown error')}",
|
|
109
|
+
file=sys.stderr,
|
|
110
|
+
)
|
|
111
|
+
stage = output.get("stage")
|
|
112
|
+
if isinstance(stage, str) and stage:
|
|
113
|
+
print(f"failed stage: {stage}", file=sys.stderr)
|
|
114
|
+
|
|
115
|
+
if host == "hermes" and output.get("core_version") is not None:
|
|
116
|
+
provider_version = output.get("provider_version")
|
|
117
|
+
if output.get("provider_updated"):
|
|
118
|
+
provider_text = provider_version or "unknown"
|
|
119
|
+
elif provider_version:
|
|
120
|
+
provider_text = f"not changed (rejected candidate {provider_version})"
|
|
121
|
+
else:
|
|
122
|
+
provider_text = "not changed"
|
|
123
|
+
print(
|
|
124
|
+
"memleaf versions: "
|
|
125
|
+
f"core={output.get('core_version') or 'unknown'}, "
|
|
126
|
+
f"Hermes provider={provider_text}",
|
|
127
|
+
file=sys.stderr,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
runtime = output.get("mcp_runtime")
|
|
131
|
+
if isinstance(runtime, dict):
|
|
132
|
+
config_path = runtime.get("config_path")
|
|
133
|
+
configured = runtime.get("configured_command")
|
|
134
|
+
current = runtime.get("current_command") or runtime.get("expected_command")
|
|
135
|
+
existing_version = runtime.get("existing_version")
|
|
136
|
+
if config_path:
|
|
137
|
+
print(f"Hermes config: {config_path}", file=sys.stderr)
|
|
138
|
+
if configured:
|
|
139
|
+
print(f"configured MCP runtime: {configured}", file=sys.stderr)
|
|
140
|
+
if current:
|
|
141
|
+
print(f"current memleaf runtime: {current}", file=sys.stderr)
|
|
142
|
+
if existing_version:
|
|
143
|
+
print(f"configured MCP version: {existing_version}", file=sys.stderr)
|
|
144
|
+
|
|
145
|
+
mcp = output.get("mcp")
|
|
146
|
+
if isinstance(mcp, dict):
|
|
147
|
+
mcp_reason = mcp.get("reason")
|
|
148
|
+
if isinstance(mcp_reason, str) and mcp_reason and mcp_reason != output.get("reason"):
|
|
149
|
+
print(f"MCP detail: {mcp_reason}", file=sys.stderr)
|
|
150
|
+
config_path = mcp.get("config_path")
|
|
151
|
+
if config_path and not (
|
|
152
|
+
isinstance(runtime, dict) and config_path == runtime.get("config_path")
|
|
153
|
+
):
|
|
154
|
+
print(f"Hermes config: {config_path}", file=sys.stderr)
|
|
155
|
+
backup_path = mcp.get("backup_path")
|
|
156
|
+
if backup_path:
|
|
157
|
+
print(f"config backup: {backup_path}", file=sys.stderr)
|
|
158
|
+
|
|
159
|
+
rollback = output.get("rollback_status")
|
|
160
|
+
if rollback == "completed":
|
|
161
|
+
print("Hermes configuration was restored to its pre-install state.", file=sys.stderr)
|
|
162
|
+
elif rollback == "failed":
|
|
163
|
+
print(
|
|
164
|
+
"WARNING: automatic rollback failed; inspect the reported Hermes paths before restarting.",
|
|
165
|
+
file=sys.stderr,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
user_action = output.get("user_action")
|
|
169
|
+
if output.get("user_action_required") and isinstance(user_action, str) and user_action:
|
|
170
|
+
print(f"action required: {user_action}", file=sys.stderr)
|
|
171
|
+
|
|
172
|
+
commands = output.get("recovery_commands")
|
|
173
|
+
if isinstance(commands, list) and commands:
|
|
174
|
+
print("recovery commands:", file=sys.stderr)
|
|
175
|
+
for command in commands:
|
|
176
|
+
if isinstance(command, list) and command:
|
|
177
|
+
print(f" {_format_command(command)}", file=sys.stderr)
|
|
178
|
+
|
|
179
|
+
diagnostic = ["python", "-m", "memleaf", "install", "--host", host]
|
|
180
|
+
if host == "hermes" and isinstance(runtime, dict):
|
|
181
|
+
policy = runtime.get("policy")
|
|
182
|
+
if policy in {"current", "existing"}:
|
|
183
|
+
diagnostic.extend(["--mcp-runtime", policy])
|
|
184
|
+
diagnostic.append("--json")
|
|
185
|
+
print(
|
|
186
|
+
f"Run `{_format_command(diagnostic)}` for the complete diagnostic result.",
|
|
187
|
+
file=sys.stderr,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
90
191
|
def main(argv: Sequence[str] | None = None) -> int:
|
|
91
192
|
_configure_stdio_utf8()
|
|
92
193
|
parser = build_parser()
|
|
@@ -96,8 +197,22 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
96
197
|
output = _init(args)
|
|
97
198
|
elif args.command == "install":
|
|
98
199
|
from .installer import install_codex, install_hermes
|
|
99
|
-
|
|
100
|
-
|
|
200
|
+
|
|
201
|
+
if args.host == "codex":
|
|
202
|
+
if args.mcp_runtime != "auto":
|
|
203
|
+
output = {
|
|
204
|
+
"status": "failure",
|
|
205
|
+
"stage": "arguments",
|
|
206
|
+
"reason": "--mcp-runtime applies only to Hermes installations",
|
|
207
|
+
"vault": str(args.vault) if args.vault is not None else None,
|
|
208
|
+
}
|
|
209
|
+
else:
|
|
210
|
+
output = install_codex(vault_path=args.vault)
|
|
211
|
+
else:
|
|
212
|
+
install_kwargs = {"vault_path": args.vault}
|
|
213
|
+
if args.mcp_runtime != "auto":
|
|
214
|
+
install_kwargs["mcp_runtime"] = args.mcp_runtime
|
|
215
|
+
output = install_hermes(**install_kwargs)
|
|
101
216
|
elif args.command == "host-event":
|
|
102
217
|
output = _host_event(args)
|
|
103
218
|
else: # pragma: no cover - argparse requires a known subcommand.
|
|
@@ -116,7 +231,8 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
116
231
|
if getattr(args, "json", False):
|
|
117
232
|
print(json.dumps({"error": "initialization failed"}, ensure_ascii=False))
|
|
118
233
|
else:
|
|
119
|
-
|
|
234
|
+
action = getattr(args, "command", "init")
|
|
235
|
+
print(f"memleaf {action} failed unexpectedly", file=sys.stderr)
|
|
120
236
|
return 1
|
|
121
237
|
|
|
122
238
|
if args.command == "host-event":
|
|
@@ -139,22 +255,16 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
139
255
|
if output.get("vault_source") == "hermes_config":
|
|
140
256
|
print("Preserved the Vault from the existing Hermes memleaf configuration.")
|
|
141
257
|
if args.host == "hermes":
|
|
258
|
+
runtime = output.get("mcp_runtime")
|
|
259
|
+
if isinstance(runtime, dict) and runtime.get("selected_command"):
|
|
260
|
+
print(f"Hermes MCP runtime: {runtime['selected_command']}")
|
|
142
261
|
print("Restart Hermes to use memleaf.")
|
|
143
262
|
elif output.get("user_action_required"):
|
|
144
263
|
print(f"Codex action required: {output.get('user_action')}")
|
|
145
264
|
if output.get("model", {}).get("status") == "not_configured":
|
|
146
265
|
print("Configure a memleaf model route before using automatic processing.")
|
|
147
266
|
else:
|
|
148
|
-
|
|
149
|
-
output.get("core_version") is not None
|
|
150
|
-
or output.get("provider_version") is not None
|
|
151
|
-
):
|
|
152
|
-
print(
|
|
153
|
-
"memleaf versions: "
|
|
154
|
-
f"core={output.get('core_version') or 'unknown'}, "
|
|
155
|
-
f"Hermes provider={output.get('provider_version') or 'unknown'}"
|
|
156
|
-
)
|
|
157
|
-
print(f"memleaf install failed: {output.get('reason', 'unknown error')}", file=sys.stderr)
|
|
267
|
+
_print_install_failure(output, host=args.host)
|
|
158
268
|
return 0 if output.get("status") in {"configured", "already_configured"} else 2
|
|
159
269
|
if args.json:
|
|
160
270
|
print(json.dumps(output, ensure_ascii=False, sort_keys=True))
|
|
@@ -309,7 +419,7 @@ def _prepare_model_route(
|
|
|
309
419
|
candidate = discovery.selected
|
|
310
420
|
diagnostics = list(discovery.diagnostics)
|
|
311
421
|
if candidate is None:
|
|
312
|
-
# An existing direct route is a safe idempotency fallback.
|
|
422
|
+
# An existing direct route is a safe idempotency fallback. It is not
|
|
313
423
|
# reported as a host discovery result and never appears with its key.
|
|
314
424
|
candidate = _existing_memleaf_route(requested_vault / "config.yaml")
|
|
315
425
|
if candidate is None and not dry_run and not non_interactive and sys.stdin.isatty():
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Memleaf memory provider for Hermes
|
|
2
2
|
|
|
3
3
|
This is a Hermes user-level memory-provider plugin for the local memleaf vault.
|
|
4
|
-
It is separate from the `memleaf-mcp` MCP server, but uses the same
|
|
5
|
-
|
|
4
|
+
It is separate from the `memleaf-mcp` MCP server, but uses the same Vault and
|
|
5
|
+
must use a version-compatible memleaf runtime.
|
|
6
6
|
|
|
7
7
|
The native provider supplies a bounded Scope Map (no memory titles or bodies),
|
|
8
8
|
captures visible Hermes turns, and automatically processes complete turns.
|
|
@@ -28,12 +28,36 @@ The optional provider config is `~/.hermes/memleaf.json`:
|
|
|
28
28
|
```json
|
|
29
29
|
{
|
|
30
30
|
"vault": "~/.memleaf",
|
|
31
|
-
"command": "memleaf-mcp",
|
|
31
|
+
"command": "/absolute/path/to/memleaf-mcp",
|
|
32
32
|
"timeout": 5,
|
|
33
|
-
"process_timeout": 300
|
|
33
|
+
"process_timeout": 300,
|
|
34
|
+
"auto_process": true
|
|
34
35
|
}
|
|
35
36
|
```
|
|
36
37
|
|
|
38
|
+
The separate persisted MCP entry uses the same executable and Vault:
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
mcp_servers:
|
|
42
|
+
memleaf:
|
|
43
|
+
command: /absolute/path/to/memleaf-mcp
|
|
44
|
+
args:
|
|
45
|
+
- --vault
|
|
46
|
+
- /absolute/path/to/vault
|
|
47
|
+
enabled: true
|
|
48
|
+
lazy: true
|
|
49
|
+
idle_timeout_seconds: 60
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Use `python -m memleaf install` to configure both entries. The installer writes
|
|
53
|
+
the MCP entry through `hermes config set`, reads `config.yaml` back, and tests
|
|
54
|
+
that all 12 tools are discoverable before it reports success. When two memleaf
|
|
55
|
+
virtual environments are present, use `--mcp-runtime current` to migrate to the
|
|
56
|
+
runtime executing the installer or `--mcp-runtime existing` to retain an
|
|
57
|
+
already configured executable after an exact version check. See the
|
|
58
|
+
[Hermes MCP runtime guide](https://github.com/miffyblueboo/memleaf/blob/main/docs/hermes-mcp-runtime.md)
|
|
59
|
+
for the full multi-environment and manual-recovery procedure.
|
|
60
|
+
|
|
37
61
|
Capture, stats, and context calls use the short `timeout`; model-backed
|
|
38
62
|
processing uses `process_timeout` so a slow local model does not get mistaken
|
|
39
63
|
for a dead MCP worker. A failed or timed-out request closes the MCP process;
|