memleaf 0.2.1__tar.gz → 0.2.2__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 (102) hide show
  1. {memleaf-0.2.1 → memleaf-0.2.2}/CHANGELOG.md +10 -0
  2. {memleaf-0.2.1 → memleaf-0.2.2}/PKG-INFO +3 -3
  3. {memleaf-0.2.1 → memleaf-0.2.2}/README.en.md +2 -2
  4. {memleaf-0.2.1 → memleaf-0.2.2}/README.md +2 -2
  5. {memleaf-0.2.1 → memleaf-0.2.2}/pyproject.toml +1 -1
  6. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/__init__.py +1 -1
  7. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/hermes_provider/plugin.yaml +1 -1
  8. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf.egg-info/PKG-INFO +3 -3
  9. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_pypi_install.py +1 -1
  10. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_stage_a.py +4 -2
  11. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_stage_c1_mcp.py +1 -1
  12. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_stage_c3_packaging.py +37 -1
  13. {memleaf-0.2.1 → memleaf-0.2.2}/IMPLEMENTATION_PLAN.md +0 -0
  14. {memleaf-0.2.1 → memleaf-0.2.2}/LICENSE +0 -0
  15. {memleaf-0.2.1 → memleaf-0.2.2}/MANIFEST.in +0 -0
  16. {memleaf-0.2.1 → memleaf-0.2.2}/RELEASE_CHECKLIST.md +0 -0
  17. {memleaf-0.2.1 → memleaf-0.2.2}/examples/README.md +0 -0
  18. {memleaf-0.2.1 → memleaf-0.2.2}/examples/basic_usage.py +0 -0
  19. {memleaf-0.2.1 → memleaf-0.2.2}/examples/mcp_stdio.ndjson +0 -0
  20. {memleaf-0.2.1 → memleaf-0.2.2}/install.ps1 +0 -0
  21. {memleaf-0.2.1 → memleaf-0.2.2}/install.sh +0 -0
  22. {memleaf-0.2.1 → memleaf-0.2.2}/setup.cfg +0 -0
  23. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/__main__.py +0 -0
  24. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/adapters/__init__.py +0 -0
  25. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/adapters/antigravity.py +0 -0
  26. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/adapters/base.py +0 -0
  27. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/adapters/codex.py +0 -0
  28. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/adapters/hermes.py +0 -0
  29. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/budget.py +0 -0
  30. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/capture.py +0 -0
  31. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/cli.py +0 -0
  32. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/compaction.py +0 -0
  33. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/config.py +0 -0
  34. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/credentials.py +0 -0
  35. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/frontmatter.py +0 -0
  36. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/hermes_provider/README.md +0 -0
  37. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/hermes_provider/__init__.py +0 -0
  38. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/host_events.py +0 -0
  39. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/host_runtime.py +0 -0
  40. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/inbox.py +0 -0
  41. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/index.py +0 -0
  42. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/installer.py +0 -0
  43. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/llm/__init__.py +0 -0
  44. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/llm/base.py +0 -0
  45. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/llm/claude_compatible.py +0 -0
  46. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/llm/gemini.py +0 -0
  47. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/llm/openai_compatible.py +0 -0
  48. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/llm/router.py +0 -0
  49. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/locking.py +0 -0
  50. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/mcp_server.py +0 -0
  51. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/memory_writer.py +0 -0
  52. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/model_discovery.py +0 -0
  53. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/models.py +0 -0
  54. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/native_index.py +0 -0
  55. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/processing.py +0 -0
  56. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/prompts.py +0 -0
  57. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/redaction.py +0 -0
  58. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/retrieval.py +0 -0
  59. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/retrieval_gate.py +0 -0
  60. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/scope_maintenance.py +0 -0
  61. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/scope_state.py +0 -0
  62. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/service.py +0 -0
  63. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/validation.py +0 -0
  64. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf/vault.py +0 -0
  65. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf.egg-info/SOURCES.txt +0 -0
  66. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf.egg-info/dependency_links.txt +0 -0
  67. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf.egg-info/entry_points.txt +0 -0
  68. {memleaf-0.2.1 → memleaf-0.2.2}/src/memleaf.egg-info/top_level.txt +0 -0
  69. {memleaf-0.2.1 → memleaf-0.2.2}/tests/__init__.py +0 -0
  70. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_admission_noise.py +0 -0
  71. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_codex_install.py +0 -0
  72. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_codex_native_cli.py +0 -0
  73. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_context_budget.py +0 -0
  74. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_credential_safety.py +0 -0
  75. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_cross_host_acceptance.py +0 -0
  76. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_hermes_provider.py +0 -0
  77. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_hermes_stdio_transport.py +0 -0
  78. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_host_events.py +0 -0
  79. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_host_runtime_contract.py +0 -0
  80. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_install.py +0 -0
  81. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_maintenance_v2.py +0 -0
  82. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_model_discovery.py +0 -0
  83. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_retrieval_gate.py +0 -0
  84. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_retrieval_v2.py +0 -0
  85. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_session_lineage.py +0 -0
  86. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_stage_b1.py +0 -0
  87. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_stage_b2a.py +0 -0
  88. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_stage_b2b.py +0 -0
  89. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_stage_b3a_commit.py +0 -0
  90. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_stage_b3a_contract.py +0 -0
  91. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_stage_b3b_native_context.py +0 -0
  92. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_stage_b3b_native_index.py +0 -0
  93. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_stage_b3b_scope.py +0 -0
  94. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_stage_b3c_retrieval.py +0 -0
  95. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_stage_b3d_scope_maintenance.py +0 -0
  96. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_stage_c2_init.py +0 -0
  97. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_upgrade_preserves_vault.py +0 -0
  98. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_v2_gate_limits.py +0 -0
  99. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_v2_host_flow.py +0 -0
  100. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_v2_mcp_flow.py +0 -0
  101. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_v2_nomatch_semantics.py +0 -0
  102. {memleaf-0.2.1 → memleaf-0.2.2}/tests/test_v2_search_gate_acceptance.py +0 -0
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to memleaf are documented here.
4
4
 
5
+ ## 0.2.2 — 2026-09-01
6
+
7
+ - Publish GitHub Release and PyPI from the same wheel and source distribution
8
+ artifact verified by the CI build job; the PyPI workflow accepts only a
9
+ successful push from this repository's `main` branch.
10
+ - Make GitHub Release publication idempotent: an existing tag must resolve to
11
+ the current release commit before its three assets are replaced or completed.
12
+ - Normalize resolved paths in the partial-delete regression test so the
13
+ macOS `/var` and `/private/var` aliases exercise the intended failure path.
14
+
5
15
  ## 0.2.1 — 2026-08-31
6
16
 
7
17
  - Fixed the finalized `search → read` contract in both supported hosts: Codex `PostToolUse` and Hermes diagnostics now accept only `memory_id + title` search candidates, so a real `found` result no longer degrades to an error before `read`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memleaf
3
- Version: 0.2.1
3
+ Version: 0.2.2
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.1。**
27
- > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.1 通过 PyPI 分发。
26
+ > **当前版本:0.2.2。**
27
+ > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.2 通过 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.1.**
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.1 is distributed through PyPI.
7
+ > **Version: 0.2.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.2 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.1。**
8
- > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.1 通过 PyPI 分发。
7
+ > **当前版本:0.2.2。**
8
+ > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.2 通过 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.1"
7
+ version = "0.2.2"
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.1"
3
+ __version__ = "0.2.2"
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
@@ -1,5 +1,5 @@
1
1
  name: memleaf
2
- version: 0.2.1
2
+ version: 0.2.2
3
3
  description: "Hermes-native external MemoryProvider for local-first Markdown memory shared with the memleaf MCP server."
4
4
  hooks:
5
5
  - prefetch
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memleaf
3
- Version: 0.2.1
3
+ Version: 0.2.2
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.1。**
27
- > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.1 通过 PyPI 分发。
26
+ > **当前版本:0.2.2。**
27
+ > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.2 通过 PyPI 分发。
28
28
  > **当前版本支持 Hermes 和 Codex。** Antigravity(反重力)不检测、不安装、不配置。
29
29
 
30
30
  ## 项目定位
@@ -39,7 +39,7 @@ class PyPIInstallTests(unittest.TestCase):
39
39
 
40
40
  self.assertTrue(target.is_dir())
41
41
  self.assertFalse(target.is_symlink())
42
- self.assertIn("version: 0.2.1", (target / "plugin.yaml").read_text(encoding="utf-8"))
42
+ self.assertIn("version: 0.2.2", (target / "plugin.yaml").read_text(encoding="utf-8"))
43
43
 
44
44
  def test_windows_hermes_paths_follow_official_native_layout(self) -> None:
45
45
  with tempfile.TemporaryDirectory(prefix="memleaf-win-paths-") as temporary:
@@ -306,13 +306,15 @@ class StageATest(unittest.TestCase):
306
306
  )
307
307
  active_path = self.vault_path / "knowledge" / f"{active.memory_id}.md"
308
308
  first_path = self.vault_path / "history" / f"{first_history.memory_id}.md"
309
- second_path = self.vault_path / "history" / f"{second_history.memory_id}.md"
309
+ second_path = (
310
+ self.vault_path / "history" / f"{second_history.memory_id}.md"
311
+ ).resolve()
310
312
  original_unlink = Path.unlink
311
313
  attempted: list[str] = []
312
314
 
313
315
  def flaky_unlink(path, *args, **kwargs):
314
316
  attempted.append(path.name)
315
- if path == second_path:
317
+ if path.resolve() == second_path:
316
318
  raise PermissionError("simulated history delete failure")
317
319
  return original_unlink(path, *args, **kwargs)
318
320
 
@@ -104,7 +104,7 @@ class StageC1MCPTest(unittest.TestCase):
104
104
  testcase.assertEqual(result["resultType"], "complete")
105
105
  testcase.assertEqual(
106
106
  result["_meta"]["io.modelcontextprotocol/serverInfo"],
107
- {"name": "memleaf", "version": "0.2.1"},
107
+ {"name": "memleaf", "version": "0.2.2"},
108
108
  )
109
109
  return result
110
110
 
@@ -24,7 +24,7 @@ class StageC3PackagingTests(unittest.TestCase):
24
24
 
25
25
  def test_project_metadata_and_version_export(self) -> None:
26
26
  self.assertEqual(self.project["name"], "memleaf")
27
- self.assertEqual(self.project["version"], "0.2.1")
27
+ self.assertEqual(self.project["version"], "0.2.2")
28
28
  self.assertEqual(self.project["requires-python"], ">=3.11")
29
29
  self.assertEqual(self.project["dependencies"], [])
30
30
  self.assertEqual(self.project["license"], "MIT")
@@ -121,6 +121,42 @@ class StageC3PackagingTests(unittest.TestCase):
121
121
  self.assertNotIn("/Users/", sample.read_text(encoding="utf-8"))
122
122
  self.assertNotIn("secret", sample.read_text(encoding="utf-8").casefold())
123
123
 
124
+ def test_pypi_workflow_uses_the_triggering_ci_artifact_without_rebuilding(self) -> None:
125
+ workflow_path = ROOT / ".github" / "workflows" / "release.yml"
126
+ if not workflow_path.is_file():
127
+ self.skipTest("repository workflows are not included in the source distribution")
128
+ workflow = workflow_path.read_text(encoding="utf-8")
129
+ self.assertNotIn("python -m build", workflow)
130
+ self.assertEqual(workflow.count("github.event.workflow_run.event == 'push'"), 2)
131
+ self.assertEqual(
132
+ workflow.count("github.event.workflow_run.head_repository.full_name == github.repository"),
133
+ 2,
134
+ )
135
+ self.assertIn("ref: ${{ github.event.workflow_run.head_sha }}", workflow)
136
+ self.assertIn("name: memleaf-distributions", workflow)
137
+ self.assertIn("github-token: ${{ github.token }}", workflow)
138
+ self.assertIn("repository: ${{ github.repository }}", workflow)
139
+ self.assertIn("run-id: ${{ github.event.workflow_run.id }}", workflow)
140
+ self.assertIn("actions: read", workflow)
141
+ self.assertIn("contents: read", workflow)
142
+ self.assertIn("id-token: write", workflow)
143
+
144
+ def test_github_release_update_validates_tag_target_and_clobbers_assets(self) -> None:
145
+ workflow_path = ROOT / ".github" / "workflows" / "ci.yml"
146
+ if not workflow_path.is_file():
147
+ self.skipTest("repository workflows are not included in the source distribution")
148
+ workflow = workflow_path.read_text(encoding="utf-8")
149
+ self.assertIn('gh release view "$TAG"', workflow)
150
+ self.assertIn('--json targetCommitish --jq \'.targetCommitish\'', workflow)
151
+ self.assertIn(
152
+ 'gh api "repos/${GITHUB_REPOSITORY}/commits/${TAG}" --jq \'.sha\'',
153
+ workflow,
154
+ )
155
+ self.assertIn('test "$target_sha" = "$RELEASE_SHA"', workflow)
156
+ self.assertIn("gh release upload", workflow)
157
+ self.assertIn("--clobber", workflow)
158
+ self.assertIn("dist/SHA256SUMS", workflow)
159
+
124
160
 
125
161
  if __name__ == "__main__":
126
162
  unittest.main()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes