memleaf 0.2.82__tar.gz → 0.2.84__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.82 → memleaf-0.2.84}/CHANGELOG.md +44 -0
- {memleaf-0.2.82/src/memleaf.egg-info → memleaf-0.2.84}/PKG-INFO +9 -6
- {memleaf-0.2.82 → memleaf-0.2.84}/README.en.md +8 -5
- {memleaf-0.2.82 → memleaf-0.2.84}/README.md +8 -5
- memleaf-0.2.84/docs/hermes-source-comparison-context.md +60 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/incremental-planner-preview.md +30 -0
- memleaf-0.2.84/docs/memory-lifecycle-tools.md +26 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/pyproject.toml +1 -1
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/__init__.py +1 -1
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/capture.py +39 -1
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/hermes_provider/_provider.py +176 -9
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/hermes_provider/plugin.yaml +1 -1
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/inbox.py +3 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_preview.py +15 -3
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_prompts.py +7 -4
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_protocol.py +63 -6
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_recovery.py +3 -1
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/mcp_server.py +71 -8
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/retrieval_gate.py +19 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/service.py +4 -0
- {memleaf-0.2.82 → memleaf-0.2.84/src/memleaf.egg-info}/PKG-INFO +9 -6
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf.egg-info/SOURCES.txt +2 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/LICENSE +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/MANIFEST.in +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/acceptance-evidence-guide.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/automatic-processing-route.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/body-compaction.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/candidate-reconciliation.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/capture-budget-design.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/config-migrations.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/core-refactor.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/development-closeout.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/evidence-retention.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/explicit-memory-update.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/extraction-latency.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/failed-run-recovery.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/gate-evidence-boundary.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/general-processing.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/hermes-mcp-runtime.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/hermes-provider-compatibility.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/host-acceptance-runbook.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/immutable-release-assets.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/incremental-commit-contract.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/incremental-execution.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/incremental-native-comparison.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/incremental-partial-recovery.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/incremental-runtime-contract.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/incremental-scope-registration.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/incremental-selected-retention.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/installed-artifact-verification.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/memory-state-contract.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/migration-preflight.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/processing-quality-acceptance.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/public-query-integrity.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/query-performance.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/remember-incremental-route.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/runtime-state-retention.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/semantic-extraction-protocol.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/source-work-contract.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/docs/v0.2.26-processing-status.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/examples/README.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/examples/basic_usage.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/examples/incremental_acceptance.json +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/examples/mcp_stdio.ndjson +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/examples/query_benchmark.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/install.ps1 +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/install.sh +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/setup.cfg +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/__main__.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/acceptance.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/adapters/__init__.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/adapters/antigravity.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/adapters/base.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/adapters/codex.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/adapters/hermes.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/admission.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/batch_review.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/budget.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/cli.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/compaction.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/config.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/create_coordinator.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/credentials.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/evidence_budget.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/evidence_policy.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/evidence_structure.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/evidence_syntax.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/explicit_text_source.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/extraction_budget.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/extraction_capability.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/extraction_work_state.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/frontmatter.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/hermes_provider/README.md +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/hermes_provider/__init__.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/hermes_provider/_mcp_client.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/hermes_provider/_shared.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/hermes_provider/evidence_budget.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/hermes_provider/provider_compatibility.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/hermes_runtime.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/host_events.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/host_runtime.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_commit.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_dates.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_execution.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_explicit.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_failed_recovery.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_journal.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_native.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_partial.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_run_state.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_runtime.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_scopes.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/incremental_selection.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/index.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/inspection.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/installer.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/legacy_update_journal.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/llm/__init__.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/llm/base.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/llm/claude_compatible.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/llm/gemini.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/llm/openai_compatible.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/llm/router.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/llm/thinking.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/locking.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/memory_commit.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/memory_planner.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/memory_retraction.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/memory_update.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/memory_writer.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/migration.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/model_capabilities.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/model_discovery.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/model_execution.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/models.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/native_index.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/native_registration.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/parallel_model.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/planning_context.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/process_common.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/process_jobs.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/process_journal.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/process_owner.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/processing.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/processing_route.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/prompts.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/provenance.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/provider_compatibility.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/query_clock.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/query_progress.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/query_scan.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/receipt_codec.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/recording_policy.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/redaction.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/remember_route.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/retention.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/retrieval.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/runtime_retention.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/scope_maintenance.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/scope_state.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/semantic_maintenance.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/semantic_protocol.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/single_pass_memory_planner.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/single_pass_plan.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/source_policy.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/state_layout.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/subprocess_flags.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/summary_batch.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/target_reconciliation.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/turn_audit.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/turn_plan.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/update_coordinator.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/update_review.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/validation.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf/vault.py +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf.egg-info/dependency_links.txt +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf.egg-info/entry_points.txt +0 -0
- {memleaf-0.2.82 → memleaf-0.2.84}/src/memleaf.egg-info/top_level.txt +0 -0
|
@@ -4,6 +4,50 @@ All notable changes to memleaf are documented here.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.2.84 — 2026-09-30
|
|
8
|
+
|
|
9
|
+
### Task lifecycle, Hermes source times and read-linked comparison
|
|
10
|
+
|
|
11
|
+
- Expose revision-bound MCP `update_memory` for completion, cancellation and
|
|
12
|
+
reopening; retain the memory body, other fields and version history. Both MCP
|
|
13
|
+
permanent-forgetting tools now require strict `confirm_delete=true`.
|
|
14
|
+
- Verify Hermes' native file-mutation footer as an exact display transformation
|
|
15
|
+
instead of losing both message times. Verify legacy correction rows against
|
|
16
|
+
their native scaffold and full merge format; capture each source event with
|
|
17
|
+
its own timestamp. Unknown inputs remain unknown.
|
|
18
|
+
- Recognize a repeated source delivery before consuming another same-text turn;
|
|
19
|
+
copy messages at callback invocation and retain the queued retrieval identity.
|
|
20
|
+
- Freeze successful current-turn read IDs in assistant capture metadata and
|
|
21
|
+
immutable receipts. Prioritize those targets for automatic extraction; reject
|
|
22
|
+
mismatched source/session/turn context and block missing or over-budget targets.
|
|
23
|
+
Recover captured context after Inbox/receipt interruption even if the gate expires.
|
|
24
|
+
- Require structured deadline maintenance before NO_CHANGE when current evidence
|
|
25
|
+
verifies an existing textual deadline whose date is absent or unresolved.
|
|
26
|
+
Advance semantic protocol to v11 without changing legacy snapshot field contracts.
|
|
27
|
+
- No production memory migration or automatic retry of historical runs is performed.
|
|
28
|
+
|
|
29
|
+
## 0.2.83 — 2026-09-30
|
|
30
|
+
|
|
31
|
+
### Reviewed extraction fields and existing-item maintenance
|
|
32
|
+
|
|
33
|
+
- Compare established identifiers and other facts against existing unknowns
|
|
34
|
+
before NO_CHANGE, including shared project facts when creating an independent
|
|
35
|
+
task. Project/task identity remains model-owned; Core does not merge titles.
|
|
36
|
+
- Add the `field-reviewed-v1` request contract: CREATE/UPDATE explicitly decide
|
|
37
|
+
whether to select a structural deadline or leave it absent/unchanged. CREATE
|
|
38
|
+
explicitly records unknown executors as null; changed non-null responsibility
|
|
39
|
+
fields require a verifiable source quote.
|
|
40
|
+
- Preserve selected deadline and responsibility provenance. Unknown CREATE
|
|
41
|
+
responsibility and unchanged inherited responsibility do not advance its basis.
|
|
42
|
+
Project existing `actionable` metadata and clarify new/context boundaries.
|
|
43
|
+
- Advance semantic protocol to v10. Legacy snapshot digests remain readable;
|
|
44
|
+
repair retains its original field contract and replan uses the current one.
|
|
45
|
+
Existing runtime protocol gates still apply to historical partial runs.
|
|
46
|
+
- Validation: 86 focused checks, six real-model synthetic scenarios and six
|
|
47
|
+
isolated capture/commit/repeated-replay scenarios passed. The 182-check local
|
|
48
|
+
comparison has nine pre-existing date assertions failing on both versions,
|
|
49
|
+
with no new failures. No formal Vault or historical run was changed.
|
|
50
|
+
|
|
7
51
|
## 0.2.82 — 2026-09-30
|
|
8
52
|
|
|
9
53
|
### Hermes installation tool discovery
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memleaf
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.84
|
|
4
4
|
Summary: A local-first Markdown memory core for AI agents
|
|
5
5
|
Author: memleaf contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,10 +23,10 @@ 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.84。**
|
|
27
27
|
> 自动提炼和复核共用同一条“未来记忆价值”标准:模型综合未来复用、信息增量、再次读取时的直接可用性和忘记成本,只保留对未来理解、判断或行动有实质影响的最小核心;没有明确价值的信息不提炼,不按具体业务场景硬编码排除。
|
|
28
|
-
>
|
|
29
|
-
>
|
|
28
|
+
> 提炼先比较旧事项中的编号、目标、进展、责任和期限;补全未知信息也应维护原事项,另建独立待办不能替代项目事实更新。Core 不按标题强行合并。
|
|
29
|
+
> 新请求使用 `field-reviewed-v1`:CREATE/UPDATE 显式判断结构化期限;未知执行人填 `null`,新增或变更非空负责人必须引用来源原文。Core 校验字段和来源,模型判断业务含义;非法行进入现有 partial 处理。来源时间用于解析相对日期,不补造事实日期。
|
|
30
30
|
> 待办 `due_date` 只表示待办动作本身明确声明的截止日;属于待办主题或预期结果的日期不会被误作截止日。项目归属由模型按语义判断,不要求项目名在证据里逐字出现。复核阶段会保留首轮已选定的更新目标,避免“状态变更”被降级成新增记忆。Markdown 仍是唯一事实源。
|
|
31
31
|
> **当前版本支持 Hermes 和 Codex。** Antigravity(反重力)不检测、不安装、不配置。
|
|
32
32
|
|
|
@@ -283,11 +283,14 @@ python -m memleaf.mcp_server --vault "$HOME/.memleaf"
|
|
|
283
283
|
| `process_status` | 只读查询异步处理 job 状态 |
|
|
284
284
|
| `recover_failed_run` | 预览并显式恢复终态传输失败,保留原请求预算 |
|
|
285
285
|
| `remember` | 用户明确要求时创建或更新记忆 |
|
|
286
|
-
| `
|
|
287
|
-
| `
|
|
286
|
+
| `update_memory` | 携带当前 revision 办结、取消或重新激活任务,保留正文与历史 |
|
|
287
|
+
| `forget_memory` | 用户明确要求遗忘时,携带 `confirm_delete=true` 按精确 ID 永久删除 |
|
|
288
|
+
| `forget_about` | 携带 `confirm_delete=true` 遗忘明确主题;有歧义时只返回候选 |
|
|
288
289
|
| `rebuild_index` | 重建可重建的本地派生索引 |
|
|
289
290
|
| `stats` | 返回 Vault 计数和诊断统计 |
|
|
290
291
|
|
|
292
|
+
任务办结与永久遗忘的接口和兼容性见 [生命周期工具](docs/memory-lifecycle-tools.md);来源时间及本轮比较目标的捕获边界见 [Hermes 来源与比较上下文](docs/hermes-source-comparison-context.md)。
|
|
293
|
+
|
|
291
294
|
`search` 的候选只是线索,标题不能单独作为事实依据。受管理检索必须使用同一条检索链的 `retrieval_id` 完成 `search → read`。Hermes/Codex Hook 可直接提供该 token;只有 MCP 的客户端可在第一条 `search` / `list_todos` 省略 token,并使用返回值继续分页和读取。带 cursor 的后续页以及所有 `read` 都不能省略 `retrieval_id`。工具错误、Scope 冲突或读取失败都应如实处理。
|
|
292
295
|
|
|
293
296
|
## Python API
|
|
@@ -4,10 +4,10 @@
|
|
|
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.84.**
|
|
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
|
-
>
|
|
10
|
-
>
|
|
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. Core does not merge matching titles.
|
|
10
|
+
> New requests use `field-reviewed-v1`: CREATE/UPDATE explicitly decide structural deadlines; unknown executors use `null`, and new or changed non-null responsibility requires a source quote. Core validates fields and provenance while the model judges business meaning; invalid rows use existing partial handling. Source time resolves relative dates without inventing factual dates.
|
|
11
11
|
> A todo's `due_date` means only the explicit deadline of the todo action; dates describing its subject or desired outcome are not deadlines. Project ownership remains the model's semantic judgement and needs no literal name match. The review stage now keeps the update target chosen by the first pass, so a state change is no longer downgraded into a new memory. Markdown remains the sole source of truth.
|
|
12
12
|
> **The current release supports Hermes and Codex.** Antigravity is not detected, installed, or configured.
|
|
13
13
|
|
|
@@ -264,11 +264,14 @@ The server exposes the following tools:
|
|
|
264
264
|
| `process_status` | Read the status of a background processing job |
|
|
265
265
|
| `recover_failed_run` | Preview and explicitly recover terminal transport failures within the original request budget |
|
|
266
266
|
| `remember` | Create or update memory after an explicit request |
|
|
267
|
-
| `
|
|
268
|
-
| `
|
|
267
|
+
| `update_memory` | Complete, cancel or reopen a task using its current revision; retain body and history |
|
|
268
|
+
| `forget_memory` | Permanently delete an exact ID after explicit user forgetting, with `confirm_delete=true` |
|
|
269
|
+
| `forget_about` | Forget an unambiguous topic with `confirm_delete=true`; return candidates when ambiguous |
|
|
269
270
|
| `rebuild_index` | Rebuild local derived indexes |
|
|
270
271
|
| `stats` | Return Vault counts and diagnostic statistics |
|
|
271
272
|
|
|
273
|
+
See [Lifecycle tools](docs/memory-lifecycle-tools.md) and [Hermes source and comparison context](docs/hermes-source-comparison-context.md) for operation and capture boundaries.
|
|
274
|
+
|
|
272
275
|
Search results are clues; a title alone is not a business fact. Managed retrieval must use the same `retrieval_id` for `search → read`. Tool errors, Scope conflicts, retrieval-turn violations, and read page/version errors must be handled as such.
|
|
273
276
|
|
|
274
277
|
## Python API
|
|
@@ -4,10 +4,10 @@
|
|
|
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.84。**
|
|
8
8
|
> 自动提炼和复核共用同一条“未来记忆价值”标准:模型综合未来复用、信息增量、再次读取时的直接可用性和忘记成本,只保留对未来理解、判断或行动有实质影响的最小核心;没有明确价值的信息不提炼,不按具体业务场景硬编码排除。
|
|
9
|
-
>
|
|
10
|
-
>
|
|
9
|
+
> 提炼先比较旧事项中的编号、目标、进展、责任和期限;补全未知信息也应维护原事项,另建独立待办不能替代项目事实更新。Core 不按标题强行合并。
|
|
10
|
+
> 新请求使用 `field-reviewed-v1`:CREATE/UPDATE 显式判断结构化期限;未知执行人填 `null`,新增或变更非空负责人必须引用来源原文。Core 校验字段和来源,模型判断业务含义;非法行进入现有 partial 处理。来源时间用于解析相对日期,不补造事实日期。
|
|
11
11
|
> 待办 `due_date` 只表示待办动作本身明确声明的截止日;属于待办主题或预期结果的日期不会被误作截止日。项目归属由模型按语义判断,不要求项目名在证据里逐字出现。复核阶段会保留首轮已选定的更新目标,避免“状态变更”被降级成新增记忆。Markdown 仍是唯一事实源。
|
|
12
12
|
> **当前版本支持 Hermes 和 Codex。** Antigravity(反重力)不检测、不安装、不配置。
|
|
13
13
|
|
|
@@ -264,11 +264,14 @@ python -m memleaf.mcp_server --vault "$HOME/.memleaf"
|
|
|
264
264
|
| `process_status` | 只读查询异步处理 job 状态 |
|
|
265
265
|
| `recover_failed_run` | 预览并显式恢复终态传输失败,保留原请求预算 |
|
|
266
266
|
| `remember` | 用户明确要求时创建或更新记忆 |
|
|
267
|
-
| `
|
|
268
|
-
| `
|
|
267
|
+
| `update_memory` | 携带当前 revision 办结、取消或重新激活任务,保留正文与历史 |
|
|
268
|
+
| `forget_memory` | 用户明确要求遗忘时,携带 `confirm_delete=true` 按精确 ID 永久删除 |
|
|
269
|
+
| `forget_about` | 携带 `confirm_delete=true` 遗忘明确主题;有歧义时只返回候选 |
|
|
269
270
|
| `rebuild_index` | 重建可重建的本地派生索引 |
|
|
270
271
|
| `stats` | 返回 Vault 计数和诊断统计 |
|
|
271
272
|
|
|
273
|
+
任务办结与永久遗忘的接口和兼容性见 [生命周期工具](docs/memory-lifecycle-tools.md);来源时间及本轮比较目标的捕获边界见 [Hermes 来源与比较上下文](docs/hermes-source-comparison-context.md)。
|
|
274
|
+
|
|
272
275
|
`search` 的候选只是线索,标题不能单独作为事实依据。受管理检索必须使用同一条检索链的 `retrieval_id` 完成 `search → read`。Hermes/Codex Hook 可直接提供该 token;只有 MCP 的客户端可在第一条 `search` / `list_todos` 省略 token,并使用返回值继续分页和读取。带 cursor 的后续页以及所有 `read` 都不能省略 `retrieval_id`。工具错误、Scope 冲突或读取失败都应如实处理。
|
|
273
276
|
|
|
274
277
|
## Python API
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Hermes source and comparison context
|
|
2
|
+
|
|
3
|
+
## Message times
|
|
4
|
+
|
|
5
|
+
`source_time` comes from the corresponding message's `source_time` or
|
|
6
|
+
`timestamp`. Missing or invalid values stay unknown. Capturing or processing
|
|
7
|
+
an event does not create a source time.
|
|
8
|
+
|
|
9
|
+
The legacy callback checks the final assistant and the nearest user. It has no
|
|
10
|
+
32-message lookback limit. It does not borrow a matching user from an older turn.
|
|
11
|
+
|
|
12
|
+
Some Hermes versions persist a reply before adding their file-mutation verifier
|
|
13
|
+
footer. The Provider accepts that display transformation only when the installed
|
|
14
|
+
Hermes formatter reconstructs the entire footer from uniquely associated,
|
|
15
|
+
current-turn file-tool receipts and the full resulting reply matches. Arbitrary
|
|
16
|
+
prefixes, suffixes, unsupported formatters and ambiguous receipts remain unknown.
|
|
17
|
+
Tool receipts supply no times or memory facts. The displayed reply remains the
|
|
18
|
+
captured assistant content.
|
|
19
|
+
|
|
20
|
+
For legacy corrections, the Provider verifies the real user rows, native replay
|
|
21
|
+
scaffold and the exact full merged callback string. It never splits arbitrary
|
|
22
|
+
user text by a separator. A bounded chain must have a queued original admission;
|
|
23
|
+
then each source row is captured separately with its own time. It does not assign
|
|
24
|
+
the final correction's time to the combined user text. A repeated source delivery
|
|
25
|
+
is recognized before consuming another same-text admission. Host-v1 admission
|
|
26
|
+
snapshots remain the preferred interface for compaction and transformations that
|
|
27
|
+
legacy callbacks cannot verify. The list is copied at callback invocation; this
|
|
28
|
+
cannot recover a host snapshot already lost before the callback arrives.
|
|
29
|
+
|
|
30
|
+
## Successful reads as comparison targets
|
|
31
|
+
|
|
32
|
+
Hermes uses the retrieval identity frozen at `on_turn_start` for final capture.
|
|
33
|
+
The assistant capture includes `retrieval_id` and `retrieval_turn_id`; the latter
|
|
34
|
+
must equal its `turn_id`. Core checks that the existing gate belongs to exactly
|
|
35
|
+
the declared source, session and turn. It never resolves a session's latest gate
|
|
36
|
+
on behalf of an older callback. Live search and read still reject stale tokens.
|
|
37
|
+
|
|
38
|
+
Core freezes only successfully read memory IDs into `comparison_context` on the
|
|
39
|
+
assistant event, including the immutable capture receipt and payload digest.
|
|
40
|
+
It accepts no caller-supplied list of IDs through this interface. The capsule is
|
|
41
|
+
comparison metadata, not factual evidence, and its token is not projected into
|
|
42
|
+
the model request. Automatic preparation gives these required targets priority
|
|
43
|
+
alongside explicit-write receipts before filling the bounded candidate budget.
|
|
44
|
+
A missing target or insufficient budget blocks preparation instead of silently
|
|
45
|
+
replacing or dropping the required target. No model call is added to capture.
|
|
46
|
+
|
|
47
|
+
A delayed callback can use its own unexpired gate even after a later turn starts.
|
|
48
|
+
Once the capsule is captured, gate expiry cannot erase it or break a duplicate
|
|
49
|
+
capture. If Inbox was saved but receipt persistence failed, retries first recover
|
|
50
|
+
the capsule from Inbox before consulting a live gate. An uncaptured, expired or
|
|
51
|
+
mismatched gate cannot supply comparison context.
|
|
52
|
+
|
|
53
|
+
The extraction prompt also requires structured deadline maintenance before
|
|
54
|
+
NO_CHANGE when current evidence makes an existing unstructured deadline
|
|
55
|
+
verifiable. Unchanged wording does not mean a missing due_date is complete.
|
|
56
|
+
This rule advances the semantic protocol to v11.
|
|
57
|
+
|
|
58
|
+
This supplies the model with the old targets. It does not force semantic UPDATE,
|
|
59
|
+
merge similar titles, or automatically alter historical memories. Recovery of
|
|
60
|
+
existing production runs and deleted records is a separate operation.
|
|
@@ -59,6 +59,36 @@ CREATE proposals have no permanent ID. UPDATE proposals carry the original
|
|
|
59
59
|
|
|
60
60
|
The version is `incremental-items-v1`; only `{"items": [...]}` is accepted.
|
|
61
61
|
|
|
62
|
+
New prepared requests also carry `extraction_contract=field-reviewed-v1`:
|
|
63
|
+
|
|
64
|
+
- Every CREATE/UPDATE includes row-level `deadline_decision`. Use `selected`
|
|
65
|
+
with `memory.deadline` for CREATE or `patch.deadline` for UPDATE; use `none`
|
|
66
|
+
for CREATE without an established deadline, or `unchanged` for UPDATE without
|
|
67
|
+
a deadline change. A deadline mentioned only in prose is insufficient.
|
|
68
|
+
- CREATE explicitly supplies `memory.assignee`, using null when the executor
|
|
69
|
+
is unknown. New or changed non-null `assignee`/`waiting_on` requires row-level
|
|
70
|
+
`responsibility_basis`, keyed by that field, with `{ref, text}` citing an exact
|
|
71
|
+
source quote in the row's evidence. An unchanged inherited owner needs no new
|
|
72
|
+
quote. Field provenance uses the selected source rather than the generic row
|
|
73
|
+
observation; merely inheriting responsibility does not advance its basis.
|
|
74
|
+
- The model compares established identifiers and other new facts against old
|
|
75
|
+
unknowns before choosing NO_CHANGE, including project facts when it also
|
|
76
|
+
creates an independent task. Existing `actionable` metadata is projected.
|
|
77
|
+
|
|
78
|
+
Core checks explicit choices, shape and source provenance; it does not classify
|
|
79
|
+
the meaning of a quote or automatically merge similar titles. Model semantic
|
|
80
|
+
errors remain possible. Invalid rows become unresolved through the existing
|
|
81
|
+
partial handling, without an additional model dispatch.
|
|
82
|
+
|
|
83
|
+
Frozen snapshots without this marker retain their original contract and digest.
|
|
84
|
+
Partial repair retains the original contract; replanning uses the current one.
|
|
85
|
+
The semantic request version advances so a pre-commit request prepared under an
|
|
86
|
+
older prompt cannot silently continue under this new prompt.
|
|
87
|
+
These snapshot rules preserve capsule reading and reconstruction; the existing
|
|
88
|
+
runtime protocol gate still applies. An older partial run may return
|
|
89
|
+
`partial_protocol_upgrade_required` before repair/replan; this change does not
|
|
90
|
+
authorize continuing that historical run across protocol versions.
|
|
91
|
+
|
|
62
92
|
| action | Required fields besides action/evidence | Behavior |
|
|
63
93
|
|---|---|---|
|
|
64
94
|
| CREATE | memory.type/scope/title/body; todo also status | Independent new assertion; Core defaults validity=valid |
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# 任务办结与永久遗忘
|
|
2
|
+
|
|
3
|
+
MCP `update_memory` 用于完成、取消或重新打开现有事项。它调用 Core 的 revision-bound 更新接口,保留原正文、未修改字段及历史版本,不调用模型。任务办结不等于删除记忆。
|
|
4
|
+
|
|
5
|
+
先通过 `search` / `list_todos` 和 `read` 确认目标。使用 `read` 返回的 `revision`,而不是分页用的 `version`:
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"memory_id": "mem-example",
|
|
10
|
+
"expected_revision": "<read 返回的 revision>",
|
|
11
|
+
"patch": {"status": "completed"}
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
- 已完成或关闭的事项使用 `completed`,放弃的事项使用 `cancelled`。
|
|
16
|
+
- 本 MCP 接口仅接受 `patch.status`,不修改正文、类型或范围。
|
|
17
|
+
- 重新打开事项使用 `patch.status=active` 和 `reopen=true`,应有用户明确重新打开的要求。
|
|
18
|
+
- 只有明确知道事项的完成事件时间时才传 `source_time`(带时区的 ISO 时间);完成时会保存为 `completed_at`。未知时省略,不使用当前执行时间补造完成时间。
|
|
19
|
+
- 相同请求可以安全重试,不重复创建历史。旧 revision 的不同修改会被拒绝,应重新读取目标。
|
|
20
|
+
- 混合了项目事实和待办的记忆,办结只改变状态,仍保留项目事实。
|
|
21
|
+
|
|
22
|
+
`forget_memory` 与 `forget_about` 永久删除选中的记忆及其历史,没有“删除后收档”的历史副本。两个 MCP 工具均要求 `confirm_delete=true`,缺失或不是布尔值 `true` 时服务器拒绝执行。
|
|
23
|
+
|
|
24
|
+
这个参数只能表示调用方确认用户明确要求永久遗忘。完成、关闭、取消或归档任务不能作为设置它的理由。布尔参数本身不是用户自然语言授权的证明,也不能保证模型永远正确选择工具。需要撤销任务完成状态时,应通过 `update_memory` 重新打开;已永久删除的内容不能用此接口恢复。
|
|
25
|
+
|
|
26
|
+
这是 MCP 删除接口的兼容性变更:旧客户端应只在已有明确永久遗忘要求时补传 `confirm_delete=true`。Python Core 的 `forget_memory` / `forget_about` 原有删除语义及参数保持不变。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Local-first Markdown memory core for AI agents."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.2.
|
|
3
|
+
__version__ = "0.2.84"
|
|
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
|
|
@@ -199,6 +199,7 @@ def _append_event(
|
|
|
199
199
|
final: bool | None,
|
|
200
200
|
tool_evidence: list[dict[str, str]] | None = None,
|
|
201
201
|
explicit_input: dict[str, Any] | None = None,
|
|
202
|
+
comparison_context: dict[str, Any] | None = None,
|
|
202
203
|
) -> str:
|
|
203
204
|
if not existing:
|
|
204
205
|
existing = _new_session_text(source, session_id, captured_at)
|
|
@@ -232,6 +233,8 @@ def _append_event(
|
|
|
232
233
|
metadata["final"] = final
|
|
233
234
|
if explicit_input is not None:
|
|
234
235
|
metadata["explicit_input"] = explicit_input
|
|
236
|
+
if comparison_context is not None:
|
|
237
|
+
metadata["comparison_context"] = comparison_context
|
|
235
238
|
if tool_evidence:
|
|
236
239
|
metadata["tool_evidence"] = [dict(item) for item in tool_evidence]
|
|
237
240
|
metadata_line = json.dumps(metadata, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
|
@@ -359,6 +362,8 @@ def _payload_digest(metadata: Mapping[str, Any], content: str) -> str:
|
|
|
359
362
|
if metadata.get("explicit_input") is not None:
|
|
360
363
|
from .explicit_text_source import validate_origin
|
|
361
364
|
value["explicit_input"] = validate_origin(metadata["explicit_input"])
|
|
365
|
+
if metadata.get("comparison_context") is not None:
|
|
366
|
+
value["comparison_context"] = metadata["comparison_context"]
|
|
362
367
|
value["content"] = content.rstrip("\n")
|
|
363
368
|
return hashlib.sha256(json.dumps(value, ensure_ascii=False, sort_keys=True,
|
|
364
369
|
separators=(",", ":")).encode("utf-8")).hexdigest()
|
|
@@ -414,7 +419,7 @@ def recover_capture_receipts_unlocked(vault: Vault, processed: dict, path: Path)
|
|
|
414
419
|
turn_key_value=metadata["turn_key"], turn_index=metadata["turn_index"],
|
|
415
420
|
message_id=metadata["message_id"], message_revision=metadata["message_revision"],
|
|
416
421
|
)
|
|
417
|
-
entry = {field: metadata[field] for field in (*_IMMUTABLE_EVENT_FIELDS, "turn_id", "turn_index", "captured_at", "explicit_input")
|
|
422
|
+
entry = {field: metadata[field] for field in (*_IMMUTABLE_EVENT_FIELDS, "turn_id", "turn_index", "captured_at", "explicit_input", "comparison_context")
|
|
418
423
|
if field in metadata}
|
|
419
424
|
entry.update(event_key=key, payload_digest=digest)
|
|
420
425
|
events[key] = entry
|
|
@@ -447,6 +452,8 @@ def capture_event(
|
|
|
447
452
|
previous_message_id: Optional[str] = None,
|
|
448
453
|
final: Optional[bool] = None,
|
|
449
454
|
_explicit_input: dict[str, Any] | None = None,
|
|
455
|
+
retrieval_id: Optional[str] = None,
|
|
456
|
+
retrieval_turn_id: Optional[str] = None,
|
|
450
457
|
) -> CaptureResult:
|
|
451
458
|
"""Capture one visible event; all persisted text is redacted first."""
|
|
452
459
|
|
|
@@ -478,6 +485,9 @@ def capture_event(
|
|
|
478
485
|
# Opting into source envelopes does not implicitly certify completion.
|
|
479
486
|
# Old callers with no source metadata retain their legacy boundary.
|
|
480
487
|
final = False
|
|
488
|
+
if (retrieval_id is None) != (retrieval_turn_id is None) or (
|
|
489
|
+
retrieval_id is not None and (role != "assistant" or retrieval_turn_id != turn_id)):
|
|
490
|
+
raise ValueError("invalid_comparison_binding")
|
|
481
491
|
resolved_event_id, resolved_message_id, resolved_event_key = _event_identity(
|
|
482
492
|
source, session_id, turn_id, role, event_id, message_id, message_revision
|
|
483
493
|
)
|
|
@@ -488,6 +498,31 @@ def capture_event(
|
|
|
488
498
|
if not visible or role not in ("user", "assistant"):
|
|
489
499
|
return CaptureResult(resolved_event_id, stored=False, duplicate=False, content=safe_content)
|
|
490
500
|
|
|
501
|
+
comparison_context = None
|
|
502
|
+
if retrieval_id is not None:
|
|
503
|
+
# Never hold the vault lock while acquiring the retrieval lock: gated
|
|
504
|
+
# reads acquire them in the opposite order. Durable retries reuse their
|
|
505
|
+
# immutable receipt even after the short-lived gate has expired.
|
|
506
|
+
with vault.lock():
|
|
507
|
+
recovered = _read_processed(vault.processed_state_path)
|
|
508
|
+
from .recording_policy import recording_allowed
|
|
509
|
+
comparison_permitted = record and recording_allowed(recovered, source, session_id, resolved_turn_key)
|
|
510
|
+
if comparison_permitted:
|
|
511
|
+
path = vault.session_path(source, session_id)
|
|
512
|
+
if path.is_symlink():
|
|
513
|
+
raise ValueError("unsafe inbox session path")
|
|
514
|
+
if recover_capture_receipts_unlocked(vault, recovered, path):
|
|
515
|
+
atomic_write_json(vault.processed_state_path, recovered)
|
|
516
|
+
receipt = recovered.get("events", {}).get(resolved_event_key, {})
|
|
517
|
+
saved = receipt.get("comparison_context") if isinstance(receipt, Mapping) else None
|
|
518
|
+
if (isinstance(saved, dict) and saved.get("retrieval_id") == retrieval_id
|
|
519
|
+
and saved.get("turn_id") == retrieval_turn_id):
|
|
520
|
+
comparison_context = saved
|
|
521
|
+
elif comparison_permitted:
|
|
522
|
+
from .retrieval_gate import snapshot_read_context
|
|
523
|
+
comparison_context = snapshot_read_context(vault, retrieval_id,
|
|
524
|
+
source=source, session_id=session_id, turn_id=retrieval_turn_id)
|
|
525
|
+
|
|
491
526
|
with vault.lock():
|
|
492
527
|
processed = _read_processed(vault.processed_state_path)
|
|
493
528
|
if _explicit_input is not None:
|
|
@@ -526,6 +561,8 @@ def capture_event(
|
|
|
526
561
|
"previous_message_revision": previous_message_revision, "source_sequence": source_sequence,
|
|
527
562
|
"previous_message_id": previous_message_id, "source_time": source_time, "final": final,
|
|
528
563
|
}
|
|
564
|
+
if retrieval_id is not None:
|
|
565
|
+
incoming["comparison_context"] = comparison_context
|
|
529
566
|
if _explicit_input is not None:
|
|
530
567
|
incoming["explicit_input"] = _explicit_input
|
|
531
568
|
digest = _payload_digest(incoming, safe_content)
|
|
@@ -584,6 +621,7 @@ def capture_event(
|
|
|
584
621
|
final=final,
|
|
585
622
|
tool_evidence=safe_tool_evidence,
|
|
586
623
|
explicit_input=_explicit_input,
|
|
624
|
+
comparison_context=comparison_context,
|
|
587
625
|
)
|
|
588
626
|
atomic_write_text(path, updated)
|
|
589
627
|
|