memleaf 0.2.70__tar.gz → 0.2.72__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.70 → memleaf-0.2.72}/CHANGELOG.md +19 -0
- {memleaf-0.2.70/src/memleaf.egg-info → memleaf-0.2.72}/PKG-INFO +2 -2
- {memleaf-0.2.70 → memleaf-0.2.72}/README.en.md +1 -1
- {memleaf-0.2.70 → memleaf-0.2.72}/README.md +1 -1
- {memleaf-0.2.70 → memleaf-0.2.72}/pyproject.toml +1 -1
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/__init__.py +1 -1
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/hermes_provider/_provider.py +13 -3
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/hermes_provider/_shared.py +6 -2
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/hermes_provider/plugin.yaml +1 -1
- memleaf-0.2.72/src/memleaf/incremental_dates.py +146 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/incremental_protocol.py +13 -3
- {memleaf-0.2.70 → memleaf-0.2.72/src/memleaf.egg-info}/PKG-INFO +2 -2
- memleaf-0.2.70/src/memleaf/incremental_dates.py +0 -75
- {memleaf-0.2.70 → memleaf-0.2.72}/LICENSE +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/MANIFEST.in +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/acceptance-evidence-guide.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/automatic-processing-route.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/body-compaction.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/candidate-reconciliation.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/capture-budget-design.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/config-migrations.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/core-refactor.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/development-closeout.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/evidence-retention.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/explicit-memory-update.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/extraction-latency.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/gate-evidence-boundary.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/general-processing.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/hermes-mcp-runtime.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/hermes-provider-compatibility.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/host-acceptance-runbook.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/immutable-release-assets.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/incremental-commit-contract.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/incremental-execution.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/incremental-native-comparison.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/incremental-partial-recovery.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/incremental-planner-preview.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/incremental-runtime-contract.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/incremental-scope-registration.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/incremental-selected-retention.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/installed-artifact-verification.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/memory-state-contract.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/migration-preflight.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/processing-quality-acceptance.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/public-query-integrity.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/query-performance.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/remember-incremental-route.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/runtime-state-retention.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/semantic-extraction-protocol.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/source-work-contract.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/docs/v0.2.26-processing-status.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/examples/README.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/examples/basic_usage.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/examples/incremental_acceptance.json +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/examples/mcp_stdio.ndjson +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/examples/query_benchmark.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/install.ps1 +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/install.sh +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/setup.cfg +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/__main__.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/acceptance.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/adapters/__init__.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/adapters/antigravity.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/adapters/base.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/adapters/codex.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/adapters/hermes.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/admission.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/batch_review.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/budget.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/capture.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/cli.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/compaction.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/config.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/create_coordinator.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/credentials.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/evidence_budget.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/evidence_policy.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/evidence_structure.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/evidence_syntax.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/explicit_text_source.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/extraction_budget.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/extraction_capability.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/extraction_work_state.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/frontmatter.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/hermes_provider/README.md +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/hermes_provider/__init__.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/hermes_provider/_mcp_client.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/hermes_provider/evidence_budget.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/hermes_provider/provider_compatibility.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/hermes_runtime.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/host_events.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/host_runtime.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/inbox.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/incremental_commit.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/incremental_execution.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/incremental_journal.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/incremental_native.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/incremental_partial.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/incremental_preview.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/incremental_prompts.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/incremental_recovery.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/incremental_run_state.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/incremental_runtime.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/incremental_scopes.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/incremental_selection.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/index.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/inspection.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/installer.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/legacy_update_journal.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/llm/__init__.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/llm/base.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/llm/claude_compatible.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/llm/gemini.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/llm/openai_compatible.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/llm/router.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/llm/thinking.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/locking.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/mcp_server.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/memory_commit.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/memory_planner.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/memory_retraction.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/memory_update.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/memory_writer.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/migration.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/model_capabilities.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/model_discovery.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/model_execution.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/models.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/native_index.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/native_registration.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/parallel_model.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/planning_context.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/process_common.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/process_jobs.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/process_journal.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/process_owner.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/processing.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/processing_route.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/prompts.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/provenance.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/provider_compatibility.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/query_clock.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/query_progress.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/query_scan.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/receipt_codec.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/recording_policy.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/redaction.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/remember_route.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/retention.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/retrieval.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/retrieval_gate.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/runtime_retention.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/scope_maintenance.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/scope_state.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/semantic_maintenance.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/semantic_protocol.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/service.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/single_pass_memory_planner.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/single_pass_plan.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/source_policy.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/state_layout.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/subprocess_flags.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/summary_batch.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/target_reconciliation.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/turn_audit.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/turn_plan.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/update_coordinator.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/update_review.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/validation.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf/vault.py +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf.egg-info/SOURCES.txt +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf.egg-info/dependency_links.txt +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf.egg-info/entry_points.txt +0 -0
- {memleaf-0.2.70 → memleaf-0.2.72}/src/memleaf.egg-info/top_level.txt +0 -0
|
@@ -4,6 +4,25 @@ All notable changes to memleaf are documented here.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.2.72 — 2026-09-22
|
|
8
|
+
|
|
9
|
+
### Hermes todo observation compatibility
|
|
10
|
+
|
|
11
|
+
- Accept the boolean `history` field returned by `list_todos` when observing
|
|
12
|
+
Hermes retrieval results, while keeping ordinary `search` result validation
|
|
13
|
+
strict and rejecting malformed field types.
|
|
14
|
+
|
|
15
|
+
## 0.2.71 — 2026-09-21
|
|
16
|
+
|
|
17
|
+
### Source-time and calendar grounding
|
|
18
|
+
|
|
19
|
+
- Normalize native Hermes Unix timestamps using the host-local day while
|
|
20
|
+
rejecting non-finite and out-of-range numeric values.
|
|
21
|
+
- Provide source-local calendar hints to incremental planning without using the
|
|
22
|
+
processing clock or making an additional model request.
|
|
23
|
+
- Reject generated content dates that conflict with cited source dates or
|
|
24
|
+
same-clock relative-date evidence, while preserving existing target wording.
|
|
25
|
+
|
|
7
26
|
## 0.2.70 — 2026-09-21
|
|
8
27
|
|
|
9
28
|
### Public repository and artifact hygiene
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memleaf
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.72
|
|
4
4
|
Summary: A local-first Markdown memory core for AI agents
|
|
5
5
|
Author: memleaf contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,7 +23,7 @@ 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.
|
|
26
|
+
> **版本:0.2.72。**
|
|
27
27
|
> 自动提炼和复核共用同一条“未来记忆价值”标准:模型综合未来复用、信息增量、再次读取时的直接可用性和忘记成本,只保留对未来理解、判断或行动有实质影响的最小核心;没有明确价值的信息不提炼,不按具体业务场景硬编码排除。
|
|
28
28
|
> 提炼契约现在只有一份中文语义契约(旧版英文 B3 提示词已删除),首轮、主题选择、候选修复和 `semantic_maintenance` 使用同一套保留价值判断;同一件事只写一次。
|
|
29
29
|
> Core 不再因为归属、日期、任务依据或跨项目而丢弃整条候选:归属缺失会归一到证据里唯一被点名的项目、否则落 `global`;日期只接受能在本候选证据里找到出处、且能锚定成 ISO 的写法(`明天`、`周五`、`月底` 都会被解析),无法解析时只丢掉日期字段,记忆照常写入;未获授权的改归属沿用目标原归属。
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[中文](README.md) · [PyPI](https://pypi.org/project/memleaf/) · [GitHub](https://github.com/miffyblueboo/memleaf)
|
|
6
6
|
|
|
7
|
-
> **Version: 0.2.
|
|
7
|
+
> **Version: 0.2.72.**
|
|
8
8
|
> Automatic extraction and review share one future-value standard: the model weighs likely reuse, information gain, direct usability when read again, and the cost of forgetting. It keeps only the smallest core that can materially help future understanding, decisions, or actions; information without clear value is not extracted, and no business-specific exclusion rule is hard-coded.
|
|
9
9
|
> A single Chinese semantic contract is sent for extraction, topic selection, candidate repair, and `semantic_maintenance`; the older English B3 prompt has been deleted and B3 remains only as a compatibility read format. One topic is written once.
|
|
10
10
|
> Core no longer discards a whole candidate over ownership, dates, task basis or cross-project wording. A missing owner normalizes to the single project named by that candidate's own evidence, otherwise `global`. A deadline is accepted only when it appears in the candidate's own evidence and can be anchored to ISO (`明天`, `周五`, `月底` all resolve); when it cannot, only the date field is dropped and the memory is still written. An unauthorized ownership change keeps the target's existing scope.
|
|
@@ -4,7 +4,7 @@
|
|
|
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.
|
|
7
|
+
> **版本:0.2.72。**
|
|
8
8
|
> 自动提炼和复核共用同一条“未来记忆价值”标准:模型综合未来复用、信息增量、再次读取时的直接可用性和忘记成本,只保留对未来理解、判断或行动有实质影响的最小核心;没有明确价值的信息不提炼,不按具体业务场景硬编码排除。
|
|
9
9
|
> 提炼契约现在只有一份中文语义契约(旧版英文 B3 提示词已删除),首轮、主题选择、候选修复和 `semantic_maintenance` 使用同一套保留价值判断;同一件事只写一次。
|
|
10
10
|
> Core 不再因为归属、日期、任务依据或跨项目而丢弃整条候选:归属缺失会归一到证据里唯一被点名的项目、否则落 `global`;日期只接受能在本候选证据里找到出处、且能锚定成 ISO 的写法(`明天`、`周五`、`月底` 都会被解析),无法解析时只丢掉日期字段,记忆照常写入;未获授权的改归属沿用目标原归属。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Local-first Markdown memory core for AI agents."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.2.
|
|
3
|
+
__version__ = "0.2.72"
|
|
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
|
|
@@ -37,6 +37,7 @@ def _sync_source_metadata(messages: Any, user_content: str, assistant_content: s
|
|
|
37
37
|
Strings supplied to sync_turn remain the only captured conversation bodies.
|
|
38
38
|
"""
|
|
39
39
|
from datetime import datetime
|
|
40
|
+
from math import isfinite
|
|
40
41
|
|
|
41
42
|
if not isinstance(messages, list) or not messages:
|
|
42
43
|
return {}
|
|
@@ -55,8 +56,17 @@ def _sync_source_metadata(messages: Any, user_content: str, assistant_content: s
|
|
|
55
56
|
value = item.get(name, item.get("id") if name == "message_id" else None)
|
|
56
57
|
if isinstance(value, str) and 0 < len(value) <= 800 and not any(c in value for c in "\x00\r\n"):
|
|
57
58
|
row[name] = value
|
|
58
|
-
timestamp = item.get("source_time"
|
|
59
|
-
if
|
|
59
|
+
timestamp = item.get("source_time")
|
|
60
|
+
if timestamp is None:
|
|
61
|
+
timestamp = item.get("timestamp")
|
|
62
|
+
if type(timestamp) in (int, float) and isfinite(timestamp) and 0 <= timestamp < 253402300800:
|
|
63
|
+
# Hermes persists Unix seconds (not an ISO string). Keep the host's
|
|
64
|
+
# local offset: a UTC rendering can shift "today" across midnight.
|
|
65
|
+
try:
|
|
66
|
+
row["source_time"] = datetime.fromtimestamp(timestamp).astimezone().isoformat()
|
|
67
|
+
except (OverflowError, OSError, ValueError):
|
|
68
|
+
pass
|
|
69
|
+
elif isinstance(timestamp, str) and len(timestamp) <= 80:
|
|
60
70
|
try:
|
|
61
71
|
parsed = datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
|
|
62
72
|
if parsed.tzinfo is not None and parsed.utcoffset() is not None:
|
|
@@ -1414,7 +1424,7 @@ class MemleafMemoryProvider(MemoryProvider):
|
|
|
1414
1424
|
continue
|
|
1415
1425
|
payload = _tool_result_for_call(call, calls, results, search_results_used)
|
|
1416
1426
|
if payload is not _CALL_FAILED:
|
|
1417
|
-
status = _hermes_search_status(payload)
|
|
1427
|
+
status = _hermes_search_status(payload, tool_name=call["name"])
|
|
1418
1428
|
observation_key = _tool_observation_key(call, search_ordinal)
|
|
1419
1429
|
if not _record_tool_observation(seen_call_keys, observation_key):
|
|
1420
1430
|
continue
|
|
@@ -426,7 +426,7 @@ def _scope_context(
|
|
|
426
426
|
return prefix + "\n".join(lines) + suffix, len(lines)
|
|
427
427
|
|
|
428
428
|
|
|
429
|
-
def _hermes_search_status(value: Any) -> str:
|
|
429
|
+
def _hermes_search_status(value: Any, *, tool_name: str = "mcp__memleaf__search") -> str:
|
|
430
430
|
"""Classify an observed MCP search result without retaining its content."""
|
|
431
431
|
|
|
432
432
|
if value is _CALL_FAILED or value is None:
|
|
@@ -461,14 +461,18 @@ def _hermes_search_status(value: Any) -> str:
|
|
|
461
461
|
if status is not None or results is not None:
|
|
462
462
|
if status not in {"found", "no_match"} or not isinstance(results, list):
|
|
463
463
|
return "error"
|
|
464
|
+
allowed_fields = {"memory_id", "title", "due_date"}
|
|
465
|
+
if tool_name == "mcp__memleaf__list_todos":
|
|
466
|
+
allowed_fields.add("history")
|
|
464
467
|
valid_results = all(
|
|
465
468
|
isinstance(item, Mapping)
|
|
466
|
-
and
|
|
469
|
+
and {"memory_id", "title"} <= set(item) <= allowed_fields
|
|
467
470
|
and isinstance(item.get("memory_id"), str)
|
|
468
471
|
and bool(item.get("memory_id"))
|
|
469
472
|
and isinstance(item.get("title"), str)
|
|
470
473
|
and bool(item.get("title"))
|
|
471
474
|
and ("due_date" not in item or item.get("due_date") is None or isinstance(item.get("due_date"), str))
|
|
475
|
+
and ("history" not in item or type(item.get("history")) is bool)
|
|
472
476
|
for item in results
|
|
473
477
|
)
|
|
474
478
|
if not valid_results:
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Calendar conversion for *model-selected* expressions, never deadline inference.
|
|
2
|
+
|
|
3
|
+
The incremental compiler supplies an exact supported expression. This module
|
|
4
|
+
retains it even outside the bounded calendar grammar; it never scans the whole
|
|
5
|
+
message to guess which date is an action deadline.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from typing import Any, Mapping
|
|
12
|
+
|
|
13
|
+
from .validation import calendar_tokens, normalize_relative_calendar_text, _RELATIVE_CALENDAR_EXPRESSION
|
|
14
|
+
|
|
15
|
+
_CLOCK_AFTER_DATE = re.compile(r"\s*(?:(?:日|号|上午|下午|晚上|中午|凌晨|约|在|at|T)\s*){0,2}(\d{1,2}:[0-5]\d)(?!\d)", re.IGNORECASE)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _dated_clocks(text: str) -> set[tuple[str, str]]:
|
|
19
|
+
pairs = set()
|
|
20
|
+
for token in calendar_tokens(text):
|
|
21
|
+
clock = _CLOCK_AFTER_DATE.match(text, token.end)
|
|
22
|
+
if token.canonical and clock:
|
|
23
|
+
pairs.add((token.canonical, clock.group(1)))
|
|
24
|
+
return pairs
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def parse_source_time(value: Any) -> datetime | None:
|
|
28
|
+
if value is None:
|
|
29
|
+
return None
|
|
30
|
+
if not isinstance(value, str):
|
|
31
|
+
raise ValueError("invalid_source_time")
|
|
32
|
+
try:
|
|
33
|
+
result = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
34
|
+
except ValueError:
|
|
35
|
+
raise ValueError("invalid_source_time") from None
|
|
36
|
+
if result.tzinfo is None or result.utcoffset() is None:
|
|
37
|
+
raise ValueError("invalid_source_time")
|
|
38
|
+
return result
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def reading_text(value: str) -> str:
|
|
42
|
+
# Presentation-only normalization. Keep parentheses, numbers and qualifiers.
|
|
43
|
+
return re.sub(r"\*\*|__|`", "", value)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def source_basis(evidence: Mapping[str, Any]) -> dict[str, Any]:
|
|
47
|
+
value = {key: evidence[key] for key in (
|
|
48
|
+
"source", "session_id", "event_key", "message_id", "message_revision",
|
|
49
|
+
"source_time", "source_sequence",
|
|
50
|
+
) if key in evidence and evidence[key] is not None}
|
|
51
|
+
if evidence.get("explicit_input") is not None:
|
|
52
|
+
from .explicit_text_source import validate_origin
|
|
53
|
+
origin = validate_origin(evidence["explicit_input"])
|
|
54
|
+
value.update(input_kind="explicit_text", origin_session_id=origin["session_id"],
|
|
55
|
+
origin_turn_key=origin["turn_key"])
|
|
56
|
+
return value
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def calendar_hints(evidence: Mapping[str, Any]) -> list[dict[str, str]]:
|
|
60
|
+
"""Small, source-local calendar facts for the same model call (no processing clock)."""
|
|
61
|
+
anchor = parse_source_time(evidence.get("source_time"))
|
|
62
|
+
if anchor is None:
|
|
63
|
+
return []
|
|
64
|
+
text = reading_text(str(evidence["text"]))
|
|
65
|
+
result: list[dict[str, str]] = []
|
|
66
|
+
for match in _RELATIVE_CALENDAR_EXPRESSION.finditer(text):
|
|
67
|
+
resolved = normalize_relative_calendar_text(match.group(), anchor.date())
|
|
68
|
+
if resolved and resolved != match.group():
|
|
69
|
+
hint = {"text": match.group(), "date": resolved}
|
|
70
|
+
if hint not in result:
|
|
71
|
+
result.append(hint)
|
|
72
|
+
return result[:20]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def invalid_content_dates(fields: Mapping[str, Any], events: list[Mapping[str, Any]],
|
|
76
|
+
preserved: Mapping[str, Any] | None = None) -> bool:
|
|
77
|
+
"""Reject unsupported generated dates, including misdated relative clock pairs.
|
|
78
|
+
|
|
79
|
+
Only cited sources and the selected update target may ground new wording.
|
|
80
|
+
Existing memory wording is preserved without a new source claim.
|
|
81
|
+
"""
|
|
82
|
+
from .process_common import _summary_date_grounding_violations
|
|
83
|
+
|
|
84
|
+
source_texts = [reading_text(str(event["text"])) for event in events]
|
|
85
|
+
grounded: set[str] = set()
|
|
86
|
+
clock_dates: dict[str, set[str]] = {}
|
|
87
|
+
for event, source in zip(events, source_texts):
|
|
88
|
+
anchor = parse_source_time(event.get("source_time"))
|
|
89
|
+
local_day = anchor.date() if anchor is not None else None
|
|
90
|
+
normalized = normalize_relative_calendar_text(source, local_day) if local_day else None
|
|
91
|
+
grounded.update(token.canonical for token in calendar_tokens(source, local_day) if token.canonical)
|
|
92
|
+
if normalized is not None:
|
|
93
|
+
grounded.update(token.canonical for token in calendar_tokens(normalized, local_day) if token.canonical)
|
|
94
|
+
for match in _RELATIVE_CALENDAR_EXPRESSION.finditer(source):
|
|
95
|
+
clock = _CLOCK_AFTER_DATE.match(source, match.end())
|
|
96
|
+
if clock:
|
|
97
|
+
resolved = normalize_relative_calendar_text(match.group(), local_day)
|
|
98
|
+
if resolved:
|
|
99
|
+
clock_dates.setdefault(clock.group(1), set()).add(resolved)
|
|
100
|
+
# An explicit source date at the same clock makes a relative binding
|
|
101
|
+
# ambiguous; in that case the guard must not claim which event it is.
|
|
102
|
+
for date_value, clock_value in _dated_clocks(source):
|
|
103
|
+
clock_dates.setdefault(clock_value, set()).add(date_value)
|
|
104
|
+
prior = preserved or {}
|
|
105
|
+
texts = {key: fields[key] for key in ("title", "body") if key in fields}
|
|
106
|
+
if _summary_date_grounding_violations(texts, grounded_dates=grounded, source_texts=source_texts,
|
|
107
|
+
preserved_texts=[prior.get("title"), prior.get("body")]):
|
|
108
|
+
return True
|
|
109
|
+
preserved_pairs = set().union(*(_dated_clocks(text) for text in (prior.get("title"), prior.get("body"))
|
|
110
|
+
if isinstance(text, str)))
|
|
111
|
+
for value in texts.values():
|
|
112
|
+
for date_value, clock_value in _dated_clocks(value):
|
|
113
|
+
expected = clock_dates.get(clock_value, set())
|
|
114
|
+
if len(expected) == 1 and date_value not in expected and (date_value, clock_value) not in preserved_pairs:
|
|
115
|
+
return True
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def selected_calendar(text: str, evidence: Mapping[str, Any]) -> dict[str, Any]:
|
|
120
|
+
"""Return a lossless text/anchor projection plus an optional calendar date.
|
|
121
|
+
|
|
122
|
+
Pass a local *date* to the legacy calendar grammar, which otherwise converts
|
|
123
|
+
datetime anchors to UTC. A midnight-offset message must keep its source day.
|
|
124
|
+
No configured processing clock or capture time is used as a substitute.
|
|
125
|
+
"""
|
|
126
|
+
if not isinstance(text, str) or not text.strip() or len(text) > 512:
|
|
127
|
+
raise ValueError("invalid_time_expression")
|
|
128
|
+
if reading_text(text) not in reading_text(str(evidence.get("text", ""))):
|
|
129
|
+
raise ValueError("unsupported_time_quote")
|
|
130
|
+
anchor = parse_source_time(evidence.get("source_time"))
|
|
131
|
+
local_day = anchor.date() if anchor is not None else None
|
|
132
|
+
view = reading_text(text)
|
|
133
|
+
converted = normalize_relative_calendar_text(view, local_day)
|
|
134
|
+
if local_day is None and not _RELATIVE_CALENDAR_EXPRESSION.search(view):
|
|
135
|
+
converted = view
|
|
136
|
+
tokens = calendar_tokens(converted if converted is not None else view, local_day)
|
|
137
|
+
dates = {token.canonical for token in tokens}
|
|
138
|
+
# Relative expressions which cannot be resolved must not borrow another date.
|
|
139
|
+
resolved = (converted is not None and bool(tokens)
|
|
140
|
+
and None not in dates and len(dates) == 1)
|
|
141
|
+
return {
|
|
142
|
+
"date": next(iter(dates)) if resolved else None,
|
|
143
|
+
"text": text,
|
|
144
|
+
"anchor": source_basis(evidence),
|
|
145
|
+
"status": "resolved" if resolved else "unresolved",
|
|
146
|
+
}
|
|
@@ -15,7 +15,8 @@ from copy import deepcopy
|
|
|
15
15
|
from dataclasses import dataclass
|
|
16
16
|
from typing import Any, Mapping
|
|
17
17
|
|
|
18
|
-
from .incremental_dates import
|
|
18
|
+
from .incremental_dates import (calendar_hints, invalid_content_dates, parse_source_time,
|
|
19
|
+
reading_text, selected_calendar, source_basis)
|
|
19
20
|
from .models import Memory
|
|
20
21
|
from .scope_state import validate_scope_key
|
|
21
22
|
from .turn_plan import revision_digest
|
|
@@ -352,6 +353,13 @@ class PlanningSnapshot:
|
|
|
352
353
|
fields["observed"] = {}
|
|
353
354
|
# No real memory IDs, revisions, local paths, source lists or journals.
|
|
354
355
|
memories.append(fields)
|
|
356
|
+
projected_evidence = []
|
|
357
|
+
for event in state["evidence"]:
|
|
358
|
+
projection = {key: event[key] for key in ("ref", "use", "role", "text", "source_time", "source_sequence") if key in event}
|
|
359
|
+
hints = calendar_hints(event)
|
|
360
|
+
if hints:
|
|
361
|
+
projection["calendar_hints"] = hints
|
|
362
|
+
projected_evidence.append(projection)
|
|
355
363
|
return {
|
|
356
364
|
"protocol_version": PROTOCOL_VERSION, "request_kind": state["request_kind"],
|
|
357
365
|
**({"retention_request": state["retention_request"]} if "retention_request" in state else {}),
|
|
@@ -359,8 +367,7 @@ class PlanningSnapshot:
|
|
|
359
367
|
**({"scope_aliases": {reverse_scope[s]: a for s, a in state["scope_aliases"].items()}}
|
|
360
368
|
if state.get("scope_aliases") else {}),
|
|
361
369
|
"allow_new_scopes": state["allow_new_scopes"], "context_complete": state["context_complete"],
|
|
362
|
-
"evidence":
|
|
363
|
-
for e in state["evidence"]], "memories": memories,
|
|
370
|
+
"evidence": projected_evidence, "memories": memories,
|
|
364
371
|
}
|
|
365
372
|
|
|
366
373
|
|
|
@@ -495,6 +502,9 @@ def _parse_row(row: Any, state: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
495
502
|
if key in fields:
|
|
496
503
|
_text(fields[key], 256 if key == "title" else 16384,
|
|
497
504
|
empty=key == "body" and fields.get("validity") == "retracted")
|
|
505
|
+
if invalid_content_dates(fields, [evidence[ref] for ref in refs],
|
|
506
|
+
state["targets"][result["target_ref"]]["memory"] if action == "UPDATE" else None):
|
|
507
|
+
raise ValueError("ungrounded_content_date")
|
|
498
508
|
if "status" in fields and fields["status"] not in ("active", "completed", "cancelled"):
|
|
499
509
|
raise ValueError("invalid_status")
|
|
500
510
|
if "validity" in fields and fields["validity"] not in ("valid", "retracted"):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memleaf
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.72
|
|
4
4
|
Summary: A local-first Markdown memory core for AI agents
|
|
5
5
|
Author: memleaf contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,7 +23,7 @@ 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.
|
|
26
|
+
> **版本:0.2.72。**
|
|
27
27
|
> 自动提炼和复核共用同一条“未来记忆价值”标准:模型综合未来复用、信息增量、再次读取时的直接可用性和忘记成本,只保留对未来理解、判断或行动有实质影响的最小核心;没有明确价值的信息不提炼,不按具体业务场景硬编码排除。
|
|
28
28
|
> 提炼契约现在只有一份中文语义契约(旧版英文 B3 提示词已删除),首轮、主题选择、候选修复和 `semantic_maintenance` 使用同一套保留价值判断;同一件事只写一次。
|
|
29
29
|
> Core 不再因为归属、日期、任务依据或跨项目而丢弃整条候选:归属缺失会归一到证据里唯一被点名的项目、否则落 `global`;日期只接受能在本候选证据里找到出处、且能锚定成 ISO 的写法(`明天`、`周五`、`月底` 都会被解析),无法解析时只丢掉日期字段,记忆照常写入;未获授权的改归属沿用目标原归属。
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
"""Calendar conversion for *model-selected* expressions, never deadline inference.
|
|
2
|
-
|
|
3
|
-
The incremental compiler supplies an exact supported expression. This module
|
|
4
|
-
retains it even outside the bounded calendar grammar; it never scans the whole
|
|
5
|
-
message to guess which date is an action deadline.
|
|
6
|
-
"""
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import re
|
|
10
|
-
from datetime import datetime
|
|
11
|
-
from typing import Any, Mapping
|
|
12
|
-
|
|
13
|
-
from .validation import calendar_tokens, normalize_relative_calendar_text, _RELATIVE_CALENDAR_EXPRESSION
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def parse_source_time(value: Any) -> datetime | None:
|
|
17
|
-
if value is None:
|
|
18
|
-
return None
|
|
19
|
-
if not isinstance(value, str):
|
|
20
|
-
raise ValueError("invalid_source_time")
|
|
21
|
-
try:
|
|
22
|
-
result = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
23
|
-
except ValueError:
|
|
24
|
-
raise ValueError("invalid_source_time") from None
|
|
25
|
-
if result.tzinfo is None or result.utcoffset() is None:
|
|
26
|
-
raise ValueError("invalid_source_time")
|
|
27
|
-
return result
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def reading_text(value: str) -> str:
|
|
31
|
-
# Presentation-only normalization. Keep parentheses, numbers and qualifiers.
|
|
32
|
-
return re.sub(r"\*\*|__|`", "", value)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def source_basis(evidence: Mapping[str, Any]) -> dict[str, Any]:
|
|
36
|
-
value = {key: evidence[key] for key in (
|
|
37
|
-
"source", "session_id", "event_key", "message_id", "message_revision",
|
|
38
|
-
"source_time", "source_sequence",
|
|
39
|
-
) if key in evidence and evidence[key] is not None}
|
|
40
|
-
if evidence.get("explicit_input") is not None:
|
|
41
|
-
from .explicit_text_source import validate_origin
|
|
42
|
-
origin = validate_origin(evidence["explicit_input"])
|
|
43
|
-
value.update(input_kind="explicit_text", origin_session_id=origin["session_id"],
|
|
44
|
-
origin_turn_key=origin["turn_key"])
|
|
45
|
-
return value
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def selected_calendar(text: str, evidence: Mapping[str, Any]) -> dict[str, Any]:
|
|
49
|
-
"""Return a lossless text/anchor projection plus an optional calendar date.
|
|
50
|
-
|
|
51
|
-
Pass a local *date* to the legacy calendar grammar, which otherwise converts
|
|
52
|
-
datetime anchors to UTC. A midnight-offset message must keep its source day.
|
|
53
|
-
No configured processing clock or capture time is used as a substitute.
|
|
54
|
-
"""
|
|
55
|
-
if not isinstance(text, str) or not text.strip() or len(text) > 512:
|
|
56
|
-
raise ValueError("invalid_time_expression")
|
|
57
|
-
if reading_text(text) not in reading_text(str(evidence.get("text", ""))):
|
|
58
|
-
raise ValueError("unsupported_time_quote")
|
|
59
|
-
anchor = parse_source_time(evidence.get("source_time"))
|
|
60
|
-
local_day = anchor.date() if anchor is not None else None
|
|
61
|
-
view = reading_text(text)
|
|
62
|
-
converted = normalize_relative_calendar_text(view, local_day)
|
|
63
|
-
if local_day is None and not _RELATIVE_CALENDAR_EXPRESSION.search(view):
|
|
64
|
-
converted = view
|
|
65
|
-
tokens = calendar_tokens(converted if converted is not None else view, local_day)
|
|
66
|
-
dates = {token.canonical for token in tokens}
|
|
67
|
-
# Relative expressions which cannot be resolved must not borrow another date.
|
|
68
|
-
resolved = (converted is not None and bool(tokens)
|
|
69
|
-
and None not in dates and len(dates) == 1)
|
|
70
|
-
return {
|
|
71
|
-
"date": next(iter(dates)) if resolved else None,
|
|
72
|
-
"text": text,
|
|
73
|
-
"anchor": source_basis(evidence),
|
|
74
|
-
"status": "resolved" if resolved else "unresolved",
|
|
75
|
-
}
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|