memleaf 0.2.93__tar.gz → 0.2.94__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.93 → memleaf-0.2.94}/CHANGELOG.md +23 -0
- {memleaf-0.2.93/src/memleaf.egg-info → memleaf-0.2.94}/PKG-INFO +2 -2
- {memleaf-0.2.93 → memleaf-0.2.94}/README.en.md +1 -1
- {memleaf-0.2.93 → memleaf-0.2.94}/README.md +1 -1
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/incremental-planner-preview.md +5 -3
- {memleaf-0.2.93 → memleaf-0.2.94}/pyproject.toml +1 -1
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/__init__.py +1 -1
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/hermes_provider/_provider.py +35 -7
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/hermes_provider/plugin.yaml +1 -1
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_prompts.py +1 -1
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_protocol.py +9 -6
- {memleaf-0.2.93 → memleaf-0.2.94/src/memleaf.egg-info}/PKG-INFO +2 -2
- {memleaf-0.2.93 → memleaf-0.2.94}/LICENSE +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/MANIFEST.in +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/acceptance-evidence-guide.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/automatic-processing-route.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/body-compaction.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/candidate-reconciliation.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/capture-budget-design.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/config-migrations.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/core-refactor.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/development-closeout.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/evidence-retention.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/explicit-memory-update.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/extraction-latency.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/failed-run-recovery.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/gate-evidence-boundary.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/general-processing.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/hermes-mcp-runtime.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/hermes-provider-compatibility.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/hermes-source-comparison-context.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/host-acceptance-runbook.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/immutable-release-assets.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/incremental-commit-contract.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/incremental-execution.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/incremental-native-comparison.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/incremental-partial-recovery.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/incremental-runtime-contract.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/incremental-scope-registration.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/incremental-selected-retention.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/installed-artifact-verification.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/memory-lifecycle-tools.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/memory-state-contract.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/migration-preflight.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/processing-quality-acceptance.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/public-query-integrity.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/query-performance.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/remember-incremental-route.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/runtime-state-retention.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/semantic-extraction-protocol.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/source-work-contract.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/docs/v0.2.26-processing-status.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/examples/README.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/examples/basic_usage.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/examples/incremental_acceptance.json +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/examples/mcp_stdio.ndjson +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/examples/query_benchmark.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/install.ps1 +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/install.sh +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/setup.cfg +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/__main__.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/acceptance.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/adapters/__init__.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/adapters/antigravity.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/adapters/base.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/adapters/codex.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/adapters/hermes.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/admission.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/batch_review.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/budget.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/capture.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/cli.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/compaction.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/config.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/create_coordinator.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/credentials.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/evidence_budget.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/evidence_policy.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/evidence_structure.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/evidence_syntax.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/explicit_text_source.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/extraction_budget.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/extraction_capability.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/extraction_work_state.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/frontmatter.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/hermes_provider/README.md +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/hermes_provider/__init__.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/hermes_provider/_mcp_client.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/hermes_provider/_shared.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/hermes_provider/evidence_budget.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/hermes_provider/provider_compatibility.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/hermes_runtime.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/historical_context.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/host_events.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/host_retention.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/host_runtime.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/host_turn_identity.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/inbox.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_commit.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_dates.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_execution.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_explicit.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_failed_recovery.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_journal.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_merge.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_native.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_partial.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_preview.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_recovery.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_run_state.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_runtime.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_scopes.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_selection.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/incremental_semantics.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/index.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/inspection.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/installer.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/legacy_update_journal.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/lifecycle_maintenance.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/llm/__init__.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/llm/base.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/llm/claude_compatible.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/llm/gemini.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/llm/openai_compatible.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/llm/router.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/llm/thinking.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/locking.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/mcp_server.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/memory_commit.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/memory_planner.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/memory_retraction.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/memory_update.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/memory_writer.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/migration.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/model_capabilities.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/model_discovery.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/model_execution.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/models.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/native_index.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/native_registration.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/parallel_model.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/planning_candidates.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/planning_context.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/process_common.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/process_jobs.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/process_journal.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/process_owner.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/processing.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/processing_route.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/prompts.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/provenance.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/provider_compatibility.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/query_clock.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/query_progress.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/query_scan.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/receipt_codec.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/recording_policy.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/redaction.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/remember_route.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/retention.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/retrieval.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/retrieval_gate.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/retrieval_lifecycle.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/runtime_retention.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/scope_maintenance.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/scope_state.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/semantic_maintenance.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/semantic_protocol.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/service.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/single_pass_memory_planner.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/single_pass_plan.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/source_policy.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/state_layout.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/subprocess_flags.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/summary_batch.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/target_reconciliation.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/turn_audit.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/turn_plan.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/update_coordinator.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/update_review.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/validation.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf/vault.py +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf.egg-info/SOURCES.txt +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf.egg-info/dependency_links.txt +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf.egg-info/entry_points.txt +0 -0
- {memleaf-0.2.93 → memleaf-0.2.94}/src/memleaf.egg-info/top_level.txt +0 -0
|
@@ -5,6 +5,29 @@ All notable changes to memleaf are documented here.
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
## 0.2.94 — 2026-10-05
|
|
9
|
+
|
|
10
|
+
### Reopen existing memories without redundant model fields
|
|
11
|
+
|
|
12
|
+
- Treat an evidence-backed UPDATE with status=active as the reopen intent for a
|
|
13
|
+
closed memory. Keep legacy reopen=true compatible and reject known older or
|
|
14
|
+
same-observation updates; missing source ordering stays unknown. Apply the
|
|
15
|
+
same ordering rule to explicitly restoring retracted facts.
|
|
16
|
+
- Preserve an independently verified user's source time when the final assistant
|
|
17
|
+
display cannot be matched. Keep assistant metadata unknown and distinguish
|
|
18
|
+
verified user deliveries while keeping repeated callbacks idempotent. Never
|
|
19
|
+
infer timestamps, borrow an older matching user message, or weaken target,
|
|
20
|
+
permission, conflict or semantic-review checks.
|
|
21
|
+
- Remove the redundant reopen flag requirement from extraction instructions.
|
|
22
|
+
Add no model request, retry or stage; the existing request budget is unchanged.
|
|
23
|
+
- Validation: 267 applicable checks and four isolated frozen-fault replays passed
|
|
24
|
+
against the current contract. Eight pre-existing obsolete test expectations
|
|
25
|
+
were recorded separately. An isolated live-model acceptance reopened one
|
|
26
|
+
existing memory with the correct deadline and no source time or reopen flag:
|
|
27
|
+
two successful requests, 3,089 observed tokens. Installed Hermes acceptance
|
|
28
|
+
remains separate from source and package verification.
|
|
29
|
+
|
|
30
|
+
|
|
8
31
|
## 0.2.93 — 2026-10-05
|
|
9
32
|
|
|
10
33
|
### Keep useful body compaction when another candidate has no net savings
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memleaf
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.94
|
|
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.94。**
|
|
27
27
|
> 自动提炼和复核共用同一条“未来记忆价值”标准:模型综合未来复用、信息增量、再次读取时的直接可用性和忘记成本,只保留对未来理解、判断或行动有实质影响的最小核心;没有明确价值的信息不提炼,不按具体业务场景硬编码排除。
|
|
28
28
|
> 提炼先比较旧事项中的编号、目标、进展、责任和期限;补全未知信息也应维护原事项,另建独立待办不能替代项目事实更新。确认重复的记忆在现有处理请求中合并,保留历史与来源;项目和独立任务保持各自生命周期,Core 不按标题强行合并。
|
|
29
29
|
> 新请求兼容等价的字段表示;未知执行人保持 `null`,新增或变更负责人和期限需要真实来源;等待条件在正文维护,不再提炼 `waiting_on`。行动及相关字段经过有界语义复核,提炼与复核共用每份工作的最多五次模型请求额度,成功即停止;复核失败不写入。来源时间用于解析相对日期,不补造事实日期。
|
|
@@ -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.94.**
|
|
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
|
> Extraction compares existing identifiers, objectives, progress, responsibility and deadlines first. Filling an unknown also maintains the original item; a separate task does not replace updating shared project facts. Confirmed duplicates can be merged in the existing processing requests with their sources and history preserved. Independent project and task lifecycles remain separate; Core does not merge matching titles.
|
|
10
10
|
> New requests accept equivalent field representations. Unknown executors remain `null`; new or changed responsibility and deadlines require actual source evidence. Dependencies belong in the body; `waiting_on` is no longer extracted. Proposed actions and related fields undergo bounded semantic verification within a shared five-request limit per work item, stopping on success; failed verification blocks writes. Source time resolves relative dates without inventing factual dates.
|
|
@@ -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.94。**
|
|
8
8
|
> 自动提炼和复核共用同一条“未来记忆价值”标准:模型综合未来复用、信息增量、再次读取时的直接可用性和忘记成本,只保留对未来理解、判断或行动有实质影响的最小核心;没有明确价值的信息不提炼,不按具体业务场景硬编码排除。
|
|
9
9
|
> 提炼先比较旧事项中的编号、目标、进展、责任和期限;补全未知信息也应维护原事项,另建独立待办不能替代项目事实更新。确认重复的记忆在现有处理请求中合并,保留历史与来源;项目和独立任务保持各自生命周期,Core 不按标题强行合并。
|
|
10
10
|
> 新请求兼容等价的字段表示;未知执行人保持 `null`,新增或变更负责人和期限需要真实来源;等待条件在正文维护,不再提炼 `waiting_on`。行动及相关字段经过有界语义复核,提炼与复核共用每份工作的最多五次模型请求额度,成功即停止;复核失败不写入。来源时间用于解析相对日期,不补造事实日期。
|
|
@@ -150,9 +150,11 @@ scanned and auto-filled as deadlines. This does **not** prove that a model-selec
|
|
|
150
150
|
ISO date was semantically a deadline: that remains a live-model acceptance item.
|
|
151
151
|
|
|
152
152
|
`field_basis` is self-contained source metadata, never eN or a list offset.
|
|
153
|
-
Provably older conflicting field changes are rejected.
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
Provably older conflicting field changes are rejected. Reopening uses UPDATE
|
|
154
|
+
with `patch.status=active`; the legacy `reopen:true` flag remains optional.
|
|
155
|
+
Reopen/restore reject a known older or identical observation. Missing optional
|
|
156
|
+
source time/order remains unknown and does not block the proposed transition;
|
|
157
|
+
the normal source review checks whether the change is supported. Unknown completion
|
|
156
158
|
time is not replaced with observation time. Future effective state changes are
|
|
157
159
|
blocked, while a fact describing a plan can still be proposed. No timer executes
|
|
158
160
|
future plans. This is bounded observation-order checking, not a bitemporal database.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Local-first Markdown memory core for AI agents."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.2.
|
|
3
|
+
__version__ = "0.2.94"
|
|
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
|
|
@@ -135,6 +135,18 @@ def _sync_delivery_digest(messages: Any, users: Any, user_content: str, assistan
|
|
|
135
135
|
identities = []
|
|
136
136
|
break
|
|
137
137
|
identities.append((uid, timestamp))
|
|
138
|
+
elif (isinstance(messages, list) and messages
|
|
139
|
+
and isinstance(messages[-1], Mapping) and messages[-1].get("role") == "assistant"
|
|
140
|
+
and not messages[-1].get("tool_calls")):
|
|
141
|
+
user = next((row for row in reversed(messages[:-1])
|
|
142
|
+
if isinstance(row, Mapping) and row.get("role") == "user"), None)
|
|
143
|
+
if user is not None and user.get("content") == user_content:
|
|
144
|
+
uid = user.get("message_uid") or user.get("message_id")
|
|
145
|
+
timestamp = _independent_source_time(user, "user").get("user", {}).get("source_time")
|
|
146
|
+
if uid or timestamp:
|
|
147
|
+
# Only the verified user identifies this delivery; do not
|
|
148
|
+
# bind the host's transformed reply to an assistant source.
|
|
149
|
+
identities.append((uid, timestamp))
|
|
138
150
|
payload = f"{user_content}\x00{_capture_assistant_text(assistant_content)}"
|
|
139
151
|
if identities:
|
|
140
152
|
payload += "\x00" + json.dumps(identities, ensure_ascii=False, separators=(",", ":"))
|
|
@@ -155,9 +167,12 @@ def _sync_source_metadata(messages: Any, user_content: str, assistant_content: s
|
|
|
155
167
|
logger.info("memleaf source-metadata reason=messages_missing")
|
|
156
168
|
return {}
|
|
157
169
|
assistant = messages[-1]
|
|
158
|
-
|
|
170
|
+
assistant_matches = _assistant_source_matches(messages, assistant_content)
|
|
171
|
+
if not assistant_matches:
|
|
159
172
|
logger.info("memleaf source-metadata reason=assistant_mismatch")
|
|
160
|
-
|
|
173
|
+
if (not isinstance(assistant, Mapping) or assistant.get("role") != "assistant"
|
|
174
|
+
or assistant.get("tool_calls")):
|
|
175
|
+
return {}
|
|
161
176
|
# Tool rounds do not define a turn boundary. Stop at the nearest user,
|
|
162
177
|
# even if its text mismatches; never borrow an older matching message.
|
|
163
178
|
preceding = reversed(messages)
|
|
@@ -166,10 +181,15 @@ def _sync_source_metadata(messages: Any, user_content: str, assistant_content: s
|
|
|
166
181
|
if isinstance(item, Mapping) and item.get("role") == "user"), None)
|
|
167
182
|
if user is None:
|
|
168
183
|
logger.info("memleaf source-metadata reason=user_missing")
|
|
169
|
-
return _independent_assistant_time(assistant, assistant_content)
|
|
184
|
+
return _independent_assistant_time(assistant, assistant_content) if assistant_matches else {}
|
|
170
185
|
if user.get("content") != user_content:
|
|
171
186
|
logger.info("memleaf source-metadata reason=user_mismatch")
|
|
172
|
-
return _independent_assistant_time(assistant, assistant_content)
|
|
187
|
+
return _independent_assistant_time(assistant, assistant_content) if assistant_matches else {}
|
|
188
|
+
if not assistant_matches:
|
|
189
|
+
# Host warnings/display transformations cannot invalidate an exact
|
|
190
|
+
# current user source. Keep its time independently, without binding
|
|
191
|
+
# the unverified assistant or inventing paired identities/order.
|
|
192
|
+
return _independent_source_time(user, "user")
|
|
173
193
|
result: dict[str, dict[str, Any]] = {}
|
|
174
194
|
for role, item in (("user", user), ("assistant", assistant)):
|
|
175
195
|
row: dict[str, Any] = {}
|
|
@@ -213,11 +233,15 @@ def _sync_source_metadata(messages: Any, user_content: str, assistant_content: s
|
|
|
213
233
|
|
|
214
234
|
|
|
215
235
|
def _independent_assistant_time(assistant: Mapping[str, Any], content: str) -> dict[str, dict[str, Any]]:
|
|
236
|
+
return _independent_source_time(assistant, "assistant")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _independent_source_time(message: Mapping[str, Any], role: str) -> dict[str, dict[str, Any]]:
|
|
216
240
|
from datetime import datetime
|
|
217
241
|
from math import isfinite
|
|
218
|
-
value =
|
|
242
|
+
value = message.get("source_time")
|
|
219
243
|
if value is None:
|
|
220
|
-
value =
|
|
244
|
+
value = message.get("timestamp")
|
|
221
245
|
parsed = None
|
|
222
246
|
try:
|
|
223
247
|
if type(value) in (int, float) and isfinite(value) and 0 <= value < 253402300800:
|
|
@@ -225,7 +249,7 @@ def _independent_assistant_time(assistant: Mapping[str, Any], content: str) -> d
|
|
|
225
249
|
elif isinstance(value, str) and len(value) <= 80:
|
|
226
250
|
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
227
251
|
if parsed is not None and parsed.tzinfo is not None and parsed.utcoffset() is not None:
|
|
228
|
-
return {
|
|
252
|
+
return {role: {"source_time": parsed.isoformat()}}
|
|
229
253
|
except (ValueError, OSError, OverflowError):
|
|
230
254
|
pass
|
|
231
255
|
return {}
|
|
@@ -1152,6 +1176,7 @@ class MemleafMemoryProvider(MemoryProvider):
|
|
|
1152
1176
|
user_content: str,
|
|
1153
1177
|
assistant_content: str,
|
|
1154
1178
|
delivery_digest: Optional[str] = None,
|
|
1179
|
+
source_fallback: bool = False,
|
|
1155
1180
|
) -> str:
|
|
1156
1181
|
pair_digest = delivery_digest or sha256(f"{user_content}\x00{assistant_content}".encode("utf-8")).hexdigest()[:16]
|
|
1157
1182
|
pair_key = (session_id, pair_digest)
|
|
@@ -1160,6 +1185,8 @@ class MemleafMemoryProvider(MemoryProvider):
|
|
|
1160
1185
|
self._turn_ids_by_pair.move_to_end(pair_key)
|
|
1161
1186
|
return existing
|
|
1162
1187
|
resolved = _turn_id(turn_number, user_content, assistant_content)
|
|
1188
|
+
if source_fallback and delivery_digest and resolved.startswith("turn-fallback-"):
|
|
1189
|
+
resolved = "turn-fallback-" + delivery_digest
|
|
1163
1190
|
if existing is not None and existing == resolved:
|
|
1164
1191
|
self._turn_ids_by_pair.move_to_end(pair_key)
|
|
1165
1192
|
return existing
|
|
@@ -1965,6 +1992,7 @@ class MemleafMemoryProvider(MemoryProvider):
|
|
|
1965
1992
|
user_content,
|
|
1966
1993
|
captured_assistant_content,
|
|
1967
1994
|
delivery_digest=delivery_digest,
|
|
1995
|
+
source_fallback=not _assistant_source_matches(messages, raw_assistant_content),
|
|
1968
1996
|
)
|
|
1969
1997
|
source_metadata = ({} if host_events is not None or correction_users else
|
|
1970
1998
|
_sync_source_metadata(messages, user_content, raw_assistant_content))
|
|
@@ -27,7 +27,7 @@ NO_MEMORY:仅 action;自动模式整轮无值得维护/新增的内容时独
|
|
|
27
27
|
字段:
|
|
28
28
|
title 标识事项,不放交期/发生日/进度,改存正文与结构字段;旧标题交期一并清理。名称固有年度等身份日期保留。
|
|
29
29
|
type=fact|todo|preference|project|event|identity|other。有独立完成条件的持续行动设 actionable:true(todo 隐含);事实类别与行动属性可分开表达。
|
|
30
|
-
status=active|completed|cancelled;完成/取消/交接同时纠正冲突的 title/body
|
|
30
|
+
status=active|completed|cancelled;完成/取消/交接同时纠正冲突的 title/body;独立未完行动不能被兄弟项完成吞掉。重开原 target:UPDATE patch.status=active,须有 new 依据。
|
|
31
31
|
assignee 是实际执行人,只有明确由用户本人执行才 user,其他执行人用明确名称,未知为 null。用户记录/转发/协调不证明本人执行。等待依赖、卡点仅在正文维护,按最新事实替换过时内容,不生成等待字段。
|
|
32
32
|
scope 采用输入引用;通用为 global,归属未知为 unscoped;只有 allow_new_scopes 才可提出 project:新名称。不把 scopes 当搜索提示,不扩大 write_scopes。
|
|
33
33
|
patch 只含 title/body/scope/status/actionable/assignee/deadline/validity;type 沿用目标。
|
|
@@ -717,6 +717,8 @@ def _parse_row(row: Any, state: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
717
717
|
def _order(new: Mapping[str, Any], old: Mapping[str, Any]) -> int | None:
|
|
718
718
|
# These fields prove observation order, not inferred business effectiveness.
|
|
719
719
|
same_stream = all(new.get(k) is not None and new.get(k) == old.get(k) for k in ("source", "session_id"))
|
|
720
|
+
if same_stream and new.get("event_key") and new.get("event_key") == old.get("event_key"):
|
|
721
|
+
return 0 # The same observation cannot revive its own closed state.
|
|
720
722
|
if same_stream and type(new.get("source_sequence")) is int and type(old.get("source_sequence")) is int:
|
|
721
723
|
a, b = new["source_sequence"], old["source_sequence"]
|
|
722
724
|
return (a > b) - (a < b)
|
|
@@ -768,15 +770,16 @@ def _compile_group(rows: list[dict[str, Any]], state: Mapping[str, Any]) -> dict
|
|
|
768
770
|
if boundary is not None and (set(chosen) - set(boundary) or (target and set(old["scopes"]) - set(boundary))):
|
|
769
771
|
raise ValueError("blocked_scope")
|
|
770
772
|
if old.get("status") in {"completed", "cancelled"} and fields.get("status") == "active":
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
773
|
+
# The explicit patch already expresses reopening. A model flag cannot
|
|
774
|
+
# prove source support; the existing semantic review checks the change.
|
|
775
|
+
# Missing optional time/order stays unknown, not an automatic veto.
|
|
776
|
+
if _order(bases.get("status", {}), old.get("field_basis", {}).get("status", {})) in (-1, 0):
|
|
777
|
+
raise ValueError("stale_observation")
|
|
775
778
|
if old.get("validity") == "retracted":
|
|
776
779
|
if fields.get("validity") != "valid" or not fields.get("body"):
|
|
777
780
|
raise ValueError("explicit_restore_required")
|
|
778
|
-
if _order(bases.get("validity", {}), old.get("field_basis", {}).get("validity", {}))
|
|
779
|
-
raise ValueError("
|
|
781
|
+
if _order(bases.get("validity", {}), old.get("field_basis", {}).get("validity", {})) in (-1, 0):
|
|
782
|
+
raise ValueError("stale_observation")
|
|
780
783
|
if fields.get("validity") == "retracted":
|
|
781
784
|
# Retraction is an explicit lifecycle operation. An explanatory body
|
|
782
785
|
# belongs to the audit metadata, never to the active assertion. Retain
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memleaf
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.94
|
|
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.94。**
|
|
27
27
|
> 自动提炼和复核共用同一条“未来记忆价值”标准:模型综合未来复用、信息增量、再次读取时的直接可用性和忘记成本,只保留对未来理解、判断或行动有实质影响的最小核心;没有明确价值的信息不提炼,不按具体业务场景硬编码排除。
|
|
28
28
|
> 提炼先比较旧事项中的编号、目标、进展、责任和期限;补全未知信息也应维护原事项,另建独立待办不能替代项目事实更新。确认重复的记忆在现有处理请求中合并,保留历史与来源;项目和独立任务保持各自生命周期,Core 不按标题强行合并。
|
|
29
29
|
> 新请求兼容等价的字段表示;未知执行人保持 `null`,新增或变更负责人和期限需要真实来源;等待条件在正文维护,不再提炼 `waiting_on`。行动及相关字段经过有界语义复核,提炼与复核共用每份工作的最多五次模型请求额度,成功即停止;复核失败不写入。来源时间用于解析相对日期,不补造事实日期。
|
|
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
|
|
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
|