memleaf 0.2.3__tar.gz → 0.2.4__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.3 → memleaf-0.2.4}/CHANGELOG.md +9 -0
  2. {memleaf-0.2.3 → memleaf-0.2.4}/PKG-INFO +3 -3
  3. {memleaf-0.2.3 → memleaf-0.2.4}/README.en.md +2 -2
  4. {memleaf-0.2.3 → memleaf-0.2.4}/README.md +2 -2
  5. {memleaf-0.2.3 → memleaf-0.2.4}/pyproject.toml +1 -1
  6. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/__init__.py +1 -1
  7. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/hermes_provider/__init__.py +1 -0
  8. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/hermes_provider/plugin.yaml +1 -1
  9. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/processing.py +20 -4
  10. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/prompts.py +16 -0
  11. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf.egg-info/PKG-INFO +3 -3
  12. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_hermes_provider.py +38 -0
  13. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_pypi_install.py +1 -1
  14. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_stage_b1.py +17 -2
  15. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_stage_c1_mcp.py +1 -1
  16. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_stage_c3_packaging.py +1 -1
  17. {memleaf-0.2.3 → memleaf-0.2.4}/IMPLEMENTATION_PLAN.md +0 -0
  18. {memleaf-0.2.3 → memleaf-0.2.4}/LICENSE +0 -0
  19. {memleaf-0.2.3 → memleaf-0.2.4}/MANIFEST.in +0 -0
  20. {memleaf-0.2.3 → memleaf-0.2.4}/RELEASE_CHECKLIST.md +0 -0
  21. {memleaf-0.2.3 → memleaf-0.2.4}/examples/README.md +0 -0
  22. {memleaf-0.2.3 → memleaf-0.2.4}/examples/basic_usage.py +0 -0
  23. {memleaf-0.2.3 → memleaf-0.2.4}/examples/mcp_stdio.ndjson +0 -0
  24. {memleaf-0.2.3 → memleaf-0.2.4}/install.ps1 +0 -0
  25. {memleaf-0.2.3 → memleaf-0.2.4}/install.sh +0 -0
  26. {memleaf-0.2.3 → memleaf-0.2.4}/setup.cfg +0 -0
  27. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/__main__.py +0 -0
  28. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/adapters/__init__.py +0 -0
  29. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/adapters/antigravity.py +0 -0
  30. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/adapters/base.py +0 -0
  31. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/adapters/codex.py +0 -0
  32. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/adapters/hermes.py +0 -0
  33. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/budget.py +0 -0
  34. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/capture.py +0 -0
  35. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/cli.py +0 -0
  36. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/compaction.py +0 -0
  37. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/config.py +0 -0
  38. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/credentials.py +0 -0
  39. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/frontmatter.py +0 -0
  40. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/hermes_provider/README.md +0 -0
  41. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/host_events.py +0 -0
  42. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/host_runtime.py +0 -0
  43. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/inbox.py +0 -0
  44. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/index.py +0 -0
  45. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/installer.py +0 -0
  46. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/llm/__init__.py +0 -0
  47. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/llm/base.py +0 -0
  48. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/llm/claude_compatible.py +0 -0
  49. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/llm/gemini.py +0 -0
  50. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/llm/openai_compatible.py +0 -0
  51. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/llm/router.py +0 -0
  52. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/locking.py +0 -0
  53. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/mcp_server.py +0 -0
  54. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/memory_writer.py +0 -0
  55. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/model_discovery.py +0 -0
  56. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/models.py +0 -0
  57. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/native_index.py +0 -0
  58. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/redaction.py +0 -0
  59. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/retrieval.py +0 -0
  60. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/retrieval_gate.py +0 -0
  61. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/scope_maintenance.py +0 -0
  62. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/scope_state.py +0 -0
  63. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/service.py +0 -0
  64. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/validation.py +0 -0
  65. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf/vault.py +0 -0
  66. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf.egg-info/SOURCES.txt +0 -0
  67. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf.egg-info/dependency_links.txt +0 -0
  68. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf.egg-info/entry_points.txt +0 -0
  69. {memleaf-0.2.3 → memleaf-0.2.4}/src/memleaf.egg-info/top_level.txt +0 -0
  70. {memleaf-0.2.3 → memleaf-0.2.4}/tests/__init__.py +0 -0
  71. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_admission_noise.py +0 -0
  72. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_codex_install.py +0 -0
  73. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_codex_native_cli.py +0 -0
  74. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_context_budget.py +0 -0
  75. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_credential_safety.py +0 -0
  76. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_cross_host_acceptance.py +0 -0
  77. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_hermes_stdio_transport.py +0 -0
  78. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_host_events.py +0 -0
  79. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_host_runtime_contract.py +0 -0
  80. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_install.py +0 -0
  81. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_maintenance_v2.py +0 -0
  82. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_model_discovery.py +0 -0
  83. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_retrieval_gate.py +0 -0
  84. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_retrieval_v2.py +0 -0
  85. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_session_lineage.py +0 -0
  86. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_stage_a.py +0 -0
  87. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_stage_b2a.py +0 -0
  88. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_stage_b2b.py +0 -0
  89. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_stage_b3a_commit.py +0 -0
  90. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_stage_b3a_contract.py +0 -0
  91. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_stage_b3b_native_context.py +0 -0
  92. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_stage_b3b_native_index.py +0 -0
  93. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_stage_b3b_scope.py +0 -0
  94. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_stage_b3c_retrieval.py +0 -0
  95. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_stage_b3d_scope_maintenance.py +0 -0
  96. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_stage_c2_init.py +0 -0
  97. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_upgrade_preserves_vault.py +0 -0
  98. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_v2_gate_limits.py +0 -0
  99. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_v2_host_flow.py +0 -0
  100. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_v2_mcp_flow.py +0 -0
  101. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_v2_nomatch_semantics.py +0 -0
  102. {memleaf-0.2.3 → memleaf-0.2.4}/tests/test_v2_search_gate_acceptance.py +0 -0
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to memleaf are documented here.
4
4
 
5
+ ## 0.2.4 — 2026-09-01
6
+
7
+ - Give `relative_time` retries explicit, actionable correction instructions so
8
+ the summarizer recalculates one-off dates from evidence timestamps, removes
9
+ relative wording, and resolves conflicting parenthesized dates before the
10
+ strict validator accepts the memory.
11
+ - Preserve the `relative_time` validation detail across the Hermes provider
12
+ boundary instead of degrading it to `other_schema_violation` in diagnostics.
13
+
5
14
  ## 0.2.3 — 2026-09-01
6
15
 
7
16
  - Preserve capture event timestamps in extraction prompts and resolve one-off
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memleaf
3
- Version: 0.2.3
3
+ Version: 0.2.4
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.3。**
27
- > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.3 通过 PyPI 分发。
26
+ > **当前版本:0.2.4。**
27
+ > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.4 通过 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.3.**
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.3 is distributed through PyPI.
7
+ > **Version: 0.2.4.**
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.4 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.3。**
8
- > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.3 通过 PyPI 分发。
7
+ > **当前版本:0.2.4。**
8
+ > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.4 通过 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.3"
7
+ version = "0.2.4"
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.3"
3
+ __version__ = "0.2.4"
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
@@ -85,6 +85,7 @@ _MODEL_VALIDATION_DETAILS = frozenset(
85
85
  "reason_too_long",
86
86
  "source_shape",
87
87
  "todo_fields",
88
+ "relative_time",
88
89
  "other_schema_violation",
89
90
  }
90
91
  )
@@ -1,5 +1,5 @@
1
1
  name: memleaf
2
- version: 0.2.3
2
+ version: 0.2.4
3
3
  description: "Hermes-native external MemoryProvider for local-first Markdown memory shared with the memleaf MCP server."
4
4
  hooks:
5
5
  - prefetch
@@ -30,7 +30,14 @@ from .locking import atomic_write_json, atomic_write_text, read_json
30
30
  from .memory_writer import MemoryWriter
31
31
  from .models import Memory, utc_now
32
32
  from .native_index import NativeIndexer
33
- from .prompts import JSON_CORRECTION, GATE_SYSTEM, SUMMARIZE_SYSTEM, gate_prompt, summarize_prompt
33
+ from .prompts import (
34
+ JSON_CORRECTION,
35
+ RELATIVE_TIME_CORRECTION,
36
+ GATE_SYSTEM,
37
+ SUMMARIZE_SYSTEM,
38
+ gate_prompt,
39
+ summarize_prompt,
40
+ )
34
41
  from .redaction import redact_text
35
42
  from .retrieval import candidate_matches_query, filter_by_scope, normalize_term
36
43
  from .scope_state import ScopeError, normalize_scopes, register_scope_nodes
@@ -463,6 +470,15 @@ class Processor:
463
470
  return reason
464
471
  return None
465
472
 
473
+ @staticmethod
474
+ def _correction_instruction(error: BaseException) -> Optional[str]:
475
+ hint = Processor._safe_correction_hint(error)
476
+ if hint == "relative_time":
477
+ return RELATIVE_TIME_CORRECTION
478
+ if hint is not None:
479
+ return f"Previous output violated: {hint}."
480
+ return None
481
+
466
482
  def _diagnostic_enabled(self) -> bool:
467
483
  try:
468
484
  config = self.service.vault.config()
@@ -620,10 +636,10 @@ class Processor:
620
636
  except Exception:
621
637
  pass
622
638
  if self._allows_next_json_attempt(error, attempt_count):
623
- hint = self._safe_correction_hint(error)
624
639
  correction_prompt = prompt + "\n\n" + JSON_CORRECTION
625
- if hint is not None:
626
- correction_prompt += f"\nPrevious output violated: {hint}."
640
+ instruction = self._correction_instruction(error)
641
+ if instruction is not None:
642
+ correction_prompt += f"\n{instruction}"
627
643
  continue
628
644
  raise
629
645
  try:
@@ -195,6 +195,22 @@ JSON_CORRECTION = (
195
195
  )
196
196
 
197
197
 
198
+ RELATIVE_TIME_CORRECTION = (
199
+ "Previous output violated: relative_time. Re-read the evidence events and use "
200
+ "the timestamp of the event supporting each date as the anchor; recompute "
201
+ "each one-off relative date from that timestamp and the stated weekday "
202
+ "semantics. In the summary title and body, every one-off calendar date must "
203
+ "be written only as YYYY-MM-DD: remove forms such as today/tomorrow/yesterday, "
204
+ "今天/明天/昨天, 本周三/这周三/下周三/上周三, and this/next/last Wednesday. "
205
+ "If a relative weekday is followed by a parenthesized numeric date and they "
206
+ "conflict, trust the event timestamp plus the weekday meaning, replace the "
207
+ "numeric date with the computed YYYY-MM-DD, and remove the relative wording "
208
+ "and conflicting parenthetical date. Do not guess when no supporting timestamp "
209
+ "exists; omit or defer the date-dependent detail. Recurring schedules such as "
210
+ "每天、每周三, or every Wednesday are allowed. Return only the strict JSON object."
211
+ )
212
+
213
+
198
214
  COMPACT_SYSTEM = """You are memleaf's memory compactor. Return JSON only.
199
215
  Merge only the supplied low-priority memories when they express compatible
200
216
  information. Return an object with a memories array; [] is a safe no-op.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memleaf
3
- Version: 0.2.3
3
+ Version: 0.2.4
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.3。**
27
- > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.3 通过 PyPI 分发。
26
+ > **当前版本:0.2.4。**
27
+ > 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.4 通过 PyPI 分发。
28
28
  > **当前版本支持 Hermes 和 Codex。** Antigravity(反重力)不检测、不安装、不配置。
29
29
 
30
30
  ## 项目定位
@@ -1717,6 +1717,44 @@ class HermesProviderTests(unittest.TestCase):
1717
1717
  self.assertIsNotNone(fields)
1718
1718
  self.assertEqual(fields[4], "invalid_update_target")
1719
1719
 
1720
+ def test_relative_time_validation_detail_is_preserved_and_logged(self) -> None:
1721
+ value = {
1722
+ "isError": True,
1723
+ "structuredContent": {
1724
+ "error": {
1725
+ "code": "model_invalid_response",
1726
+ "stage": "summarize",
1727
+ "validation_reason": "schema_violation",
1728
+ "validation_detail": "relative_time",
1729
+ "attempt_count": 3,
1730
+ }
1731
+ },
1732
+ }
1733
+ fields = provider_module._mcp_error_fields(value)
1734
+ self.assertIsNotNone(fields)
1735
+ self.assertEqual(fields[4], "relative_time")
1736
+ error = provider_module._MCPToolError(
1737
+ code="model_invalid_response",
1738
+ stage="summarize",
1739
+ validation_reason="schema_violation",
1740
+ validation_detail="relative_time",
1741
+ attempt_count=3,
1742
+ )
1743
+ self.assertEqual(error.validation_detail, "relative_time")
1744
+
1745
+ provider = self.provider(
1746
+ responses=[
1747
+ {"stored": True},
1748
+ {"stored": True},
1749
+ value,
1750
+ ]
1751
+ )
1752
+ with patch.object(provider_module.logger, "info") as info:
1753
+ provider.sync_turn("visible user", "visible assistant", session_id="relative-time-session")
1754
+ output = "\n".join(call.args[0] % call.args[1:] for call in info.call_args_list)
1755
+ self.assertIn("validation_detail=relative_time", output)
1756
+ self.assertIn("attempt_count=3", output)
1757
+
1720
1758
  def test_capture_failure_skips_process_and_does_not_escape_provider(self) -> None:
1721
1759
  provider = self.provider(responses=[{"stored": True}, RuntimeError("capture unavailable")])
1722
1760
  with patch.object(provider_module.logger, "warning") as warning:
@@ -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.3", (target / "plugin.yaml").read_text(encoding="utf-8"))
42
+ self.assertIn("version: 0.2.4", (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:
@@ -9,7 +9,13 @@ from unittest import mock
9
9
  from memleaf import Memleaf
10
10
  from memleaf.config import default_config, load_config, save_config
11
11
  from memleaf.inbox import complete_turns, parse_inbox
12
- from memleaf.prompts import GATE_SYSTEM, SUMMARIZE_SYSTEM, gate_prompt, summarize_prompt
12
+ from memleaf.prompts import (
13
+ GATE_SYSTEM,
14
+ RELATIVE_TIME_CORRECTION,
15
+ SUMMARIZE_SYSTEM,
16
+ gate_prompt,
17
+ summarize_prompt,
18
+ )
13
19
  from memleaf.validation import ModelOutputError
14
20
  from memleaf.index import event_key, turn_key
15
21
  from memleaf.llm import (
@@ -204,7 +210,11 @@ class StageB1Test(unittest.TestCase):
204
210
  "status": "active",
205
211
  }
206
212
 
207
- responses = [json.dumps(gate), json.dumps(summary("在本周三前完成。")), json.dumps(summary("在2026-09-02前完成。"))]
213
+ responses = [
214
+ json.dumps(gate),
215
+ json.dumps(summary("在本周三(9/3)前完成。")),
216
+ json.dumps(summary("在2026-09-02前完成。")),
217
+ ]
208
218
  calls = []
209
219
 
210
220
  def callback(prompt, **kwargs):
@@ -231,6 +241,11 @@ class StageB1Test(unittest.TestCase):
231
241
  self.assertEqual(len(calls), 3)
232
242
  self.assertEqual([purpose for _, purpose in calls], ["gate", "summarize", "summarize"])
233
243
  self.assertIn(anchor, calls[0][0])
244
+ self.assertIn(RELATIVE_TIME_CORRECTION, calls[2][0])
245
+ self.assertIn("event supporting each date as the anchor", calls[2][0])
246
+ self.assertIn("every one-off calendar date must be written only as YYYY-MM-DD", calls[2][0])
247
+ self.assertIn("parenthesized numeric date", calls[2][0])
248
+ self.assertIn("trust the event timestamp plus the weekday meaning", calls[2][0])
234
249
  self.assertIn("Previous output violated: relative_time.", calls[2][0])
235
250
  memory = self.service.read(result["memory_ids"][0])
236
251
  self.assertIsNotNone(memory)
@@ -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.3"},
107
+ {"name": "memleaf", "version": "0.2.4"},
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.3")
27
+ self.assertEqual(self.project["version"], "0.2.4")
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")
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