hy-memory 2.0.5.dev4__tar.gz → 2.0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. {hy_memory-2.0.5.dev4/hy_memory.egg-info → hy_memory-2.0.6}/PKG-INFO +2 -1
  2. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/README.md +1 -0
  3. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/__init__.py +13 -0
  4. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/agent/prompts_zh.py +14 -7
  5. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/agent/reconciler.py +105 -3
  6. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/client.py +1367 -742
  7. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/coding/store.py +192 -4
  8. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/coding/writer.py +9 -2
  9. hy_memory-2.0.6/hy_memory/embedding.py +53 -0
  10. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/diversity.py +22 -0
  11. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/base.py +5 -4
  12. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/reader_base.py +18 -5
  13. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/registry.py +9 -3
  14. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/writer.py +31 -85
  15. hy_memory-2.0.6/hy_memory/retrieval.py +23 -0
  16. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/server.py +45 -7
  17. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/utils/lang_detect.py +68 -6
  18. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6/hy_memory.egg-info}/PKG-INFO +2 -1
  19. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory.egg-info/SOURCES.txt +2 -0
  20. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/pyproject.toml +1 -1
  21. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/LICENSE +0 -0
  22. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/MANIFEST.in +0 -0
  23. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/agent/__init__.py +0 -0
  24. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/agent/abstractor.py +0 -0
  25. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/agent/emotion_analyzer.py +0 -0
  26. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/agent/episode_summary.py +0 -0
  27. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/agent/extractor.py +0 -0
  28. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/agent/intention_detector.py +0 -0
  29. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/agent/llm_provider.py +0 -0
  30. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/agent/mem_agent.py +0 -0
  31. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/agent/reflector.py +0 -0
  32. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/agent/tools/__init__.py +0 -0
  33. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/agent/tools/base.py +0 -0
  34. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/agent/tools/basic_profile.py +0 -0
  35. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/coding/__init__.py +0 -0
  36. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/coding/chain_store.py +0 -0
  37. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/coding/extractor.py +0 -0
  38. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/coding/fact_reconciler.py +0 -0
  39. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/coding/judge.py +0 -0
  40. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/coding/lexical.py +0 -0
  41. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/coding/preproc.py +0 -0
  42. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/coding/prompts_zh.py +0 -0
  43. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/coding/reconciler.py +0 -0
  44. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/coding/types.py +0 -0
  45. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/config.py +0 -0
  46. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/core/__init__.py +0 -0
  47. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/core/embed_service.py +0 -0
  48. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/core/merger.py +0 -0
  49. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/core/scorer.py +0 -0
  50. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/core/workload.py +0 -0
  51. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/__init__.py +0 -0
  52. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/cache.py +0 -0
  53. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/cache_base.py +0 -0
  54. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/cache_mysql.py +0 -0
  55. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/cache_sqlite.py +0 -0
  56. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/graph_store.py +0 -0
  57. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/graph_store_base.py +0 -0
  58. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/graph_store_kuzu.py +0 -0
  59. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/graph_store_neo4j.py +0 -0
  60. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/history_store.py +0 -0
  61. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/kv_store.py +0 -0
  62. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/rdb.py +0 -0
  63. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/vector_store.py +0 -0
  64. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/vector_store_base.py +0 -0
  65. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/vector_store_chroma.py +0 -0
  66. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/vector_store_faiss.py +0 -0
  67. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/data/vector_store_qdrant.py +0 -0
  68. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/inspector.py +0 -0
  69. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/metrics.py +0 -0
  70. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/models/__init__.py +0 -0
  71. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/models/memory.py +0 -0
  72. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/models/requests.py +0 -0
  73. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/__init__.py +0 -0
  74. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_graph/__init__.py +0 -0
  75. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_graph/projector.py +0 -0
  76. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_graph/rerank.py +0 -0
  77. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_graph/topic_summary.py +0 -0
  78. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/__init__.py +0 -0
  79. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/bm25.py +0 -0
  80. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/bm25_fastembed.py +0 -0
  81. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/candidate_guards.py +0 -0
  82. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/config.py +0 -0
  83. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/date_view.py +0 -0
  84. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/dedup.py +0 -0
  85. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/entities.py +0 -0
  86. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/entity_store.py +0 -0
  87. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/evolution.py +0 -0
  88. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/hierarchy_bundle.py +0 -0
  89. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/intent.py +0 -0
  90. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/intention.py +0 -0
  91. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/lemmatize.py +0 -0
  92. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/local_rerank.py +0 -0
  93. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/memory_format.py +0 -0
  94. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/multi_seed_bundle.py +0 -0
  95. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/ner.py +0 -0
  96. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/profile_evidence.py +0 -0
  97. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/profile_owner_gate.py +0 -0
  98. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/query_expand.py +0 -0
  99. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/reconcile_retrieval.py +0 -0
  100. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/record_date_view.py +0 -0
  101. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/retrieval_clauses.py +0 -0
  102. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/rrf.py +0 -0
  103. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/scoring.py +0 -0
  104. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/series_tail_guard.py +0 -0
  105. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/session_episode.py +0 -0
  106. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/strength.py +0 -0
  107. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/supplemental_propositions.py +0 -0
  108. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/tag_index.py +0 -0
  109. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/temporal.py +0 -0
  110. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/topic_candidates.py +0 -0
  111. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/trace.py +0 -0
  112. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/underflow.py +0 -0
  113. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/_retrieval/user_level_l0.py +0 -0
  114. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/balanced_catalog.py +0 -0
  115. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/cross_domain_sweeper.py +0 -0
  116. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/reader.py +0 -0
  117. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/reader_hybrid.py +0 -0
  118. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/reader_legacy.py +0 -0
  119. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/specific_supplement.py +0 -0
  120. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/system2_agent.py +0 -0
  121. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/system2_tools.py +0 -0
  122. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/system2_writer.py +0 -0
  123. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/pipelines/user_profile_scope.py +0 -0
  124. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/runtime.py +0 -0
  125. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/snapshot.py +0 -0
  126. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/structure.py +0 -0
  127. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/utils/async_executor.py +0 -0
  128. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/utils/clone_references.py +0 -0
  129. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/utils/keyword_ties.py +0 -0
  130. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/utils/log_setup.py +0 -0
  131. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/utils/metrics.py +0 -0
  132. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/utils/model_call_context.py +0 -0
  133. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/utils/pipeline_log_writer.py +0 -0
  134. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/utils/pipeline_observability.py +0 -0
  135. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/utils/source_filter.py +0 -0
  136. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/utils/time_window.py +0 -0
  137. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/utils/token_count.py +0 -0
  138. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/utils/trace_file_writer.py +0 -0
  139. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory/utils/tracer.py +0 -0
  140. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory.egg-info/dependency_links.txt +0 -0
  141. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory.egg-info/requires.txt +0 -0
  142. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/hy_memory.egg-info/top_level.txt +0 -0
  143. {hy_memory-2.0.5.dev4 → hy_memory-2.0.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hy-memory
3
- Version: 2.0.5.dev4
3
+ Version: 2.0.6
4
4
  Summary: HY Memory - Industrial-grade dual-system cognitive memory framework
5
5
  Author: alvinfei
6
6
  License-Expression: Apache-2.0
@@ -304,6 +304,7 @@ That version is usually not in the working tree or at HEAD either, so it cannot
304
304
  - [Graph memory design](docs/pro_graph_design.md) — graph projection, rerank, relation paths, and deployment limits
305
305
  - [Environment template](.env.example) — a copy-ready configuration template
306
306
  - [MemHub README](memhub/README.md) — local-first desktop & CLI product overview, runtime commands, and privacy boundaries
307
+ - [Hermes native memory provider](plugins/hermes/README.md) — explicit owner/agent scope; separate HTTP service; automatic capture and recall opt-in.
307
308
  - [OpenClaw plugin](plugins/openclaw/README.md) — agent integrations live under `plugins/` (overview: [plugins/README.md](plugins/README.md))
308
309
 
309
310
  ### License
@@ -253,6 +253,7 @@ That version is usually not in the working tree or at HEAD either, so it cannot
253
253
  - [Graph memory design](docs/pro_graph_design.md) — graph projection, rerank, relation paths, and deployment limits
254
254
  - [Environment template](.env.example) — a copy-ready configuration template
255
255
  - [MemHub README](memhub/README.md) — local-first desktop & CLI product overview, runtime commands, and privacy boundaries
256
+ - [Hermes native memory provider](plugins/hermes/README.md) — explicit owner/agent scope; separate HTTP service; automatic capture and recall opt-in.
256
257
  - [OpenClaw plugin](plugins/openclaw/README.md) — agent integrations live under `plugins/` (overview: [plugins/README.md](plugins/README.md))
257
258
 
258
259
  ### License
@@ -65,6 +65,12 @@ from .snapshot import (
65
65
 
66
66
  # ====== 配置 ======
67
67
  from .config import MemoryConfig
68
+ from .embedding import (
69
+ EmbeddingDimensionMismatchError,
70
+ EmbeddingService,
71
+ EmbeddingServiceProtocol,
72
+ )
73
+ from .retrieval import AmbiguousMemoryIdError, MemoryKind, MemoryVariant
68
74
 
69
75
  # ====== Embedding 端点覆盖(per-call,不改 model/dims,只换接入端点)======
70
76
  from .core.embed_service import embed_endpoint_override
@@ -125,6 +131,13 @@ __all__ = [
125
131
  "snapshot_local_data_plane",
126
132
  # 配置
127
133
  "MemoryConfig",
134
+ # 公共扩展协议 / 精确读取类型
135
+ "EmbeddingService",
136
+ "EmbeddingServiceProtocol",
137
+ "EmbeddingDimensionMismatchError",
138
+ "MemoryKind",
139
+ "MemoryVariant",
140
+ "AmbiguousMemoryIdError",
128
141
  # Embedding 端点覆盖
129
142
  "embed_endpoint_override",
130
143
  # 高级 API
@@ -743,6 +743,7 @@ RECONCILE_PROMPT_ZH = """你是一个记忆整合系统(Memory Reconciler)
743
743
 
744
744
  * content
745
745
  * owner
746
+ * memory_at(发生时间)
746
747
  * tags
747
748
 
748
749
  new memories 没有 ID。
@@ -791,6 +792,7 @@ new memories 没有 ID。
791
792
  * 无损合并
792
793
  * 保留所有有效信息
793
794
  * 不要产生重复 memory
795
+ * 两者在某个值上不一致时,保留 memory_at 更晚的那个值;绝不用更早的 memory 覆盖更新的值
794
796
 
795
797
  ### 同一次事件的补充 vs 另一次发生
796
798
 
@@ -823,6 +825,7 @@ new memories 没有 ID。
823
825
  * 新 memory 与旧 memory 在同一维度冲突
824
826
  * 两者不能同时为真
825
827
  * 旧 memory 已不再代表当前状态
828
+ * 新 memory 的 memory_at 不早于旧 memory 的 memory_at
826
829
 
827
830
  例如:
828
831
 
@@ -837,6 +840,7 @@ new memories 没有 ID。
837
840
  * wording 差异
838
841
  * 同义表达
839
842
  * 可共存偏好
843
+ * 新 memory 的 memory_at 早于旧 memory:它是更早的状态,绝不能取代更新的状态(见「时间规则」)
840
844
 
841
845
  要求:
842
846
 
@@ -859,7 +863,7 @@ new memories 没有 ID。
859
863
 
860
864
  ### 把多条旧记忆折叠进同一条链
861
865
 
862
- 如果**两条或更多**已有记忆描述的是**同一维度**,而新记忆是该维度上的最新变化,
866
+ 如果**两条或更多**已有记忆描述的是**同一维度**,而新记忆是该维度上的最新变化(它的 memory_at 晚于所有被折叠的记忆),
863
867
  你可以把它们折叠进同一条演化链。此时用 `memory_ids`(有序列表,**旧→新**)
864
868
  代替单个 `memory_id`:
865
869
 
@@ -951,13 +955,16 @@ SUPERSEDE 不需要复制旧内容,因为旧节点仍存在。
951
955
 
952
956
  ## 时间规则
953
957
 
954
- `memory_at: null`
958
+ 每条 memory(新的和已有的)都带 memory_at,表示它发生的时间。记忆**不一定按发生顺序写入**:
959
+ 历史可能晚导入,导入还没处理完时用户也可能手动添加记忆。所以一条 new memory 可能**比已有记忆更早**。
955
960
 
956
- 表示时间未知。
957
-
958
- 不要编造时间顺序。
959
-
960
- 不要仅因为“看起来更新”就 supersede。
961
+ * 新旧**只看 memory_at**。"New memories" 只表示"正在写入",不表示"更近"。
962
+ * 更早的 memory 绝不能取代更新的 memory:不能 SUPERSEDE 一条更新的记忆,也不能用 UPDATE 拿更早的值覆盖更新的值。
963
+ * 如果 new memory 比与它冲突的已有记忆更早:保留已有记忆作为当前状态;把 new memory 以"过去状态"ADD
964
+ (用过去时,并带上时间,例如"2023 年用户住在上海"),没有新信息就不输出。
965
+ * `memory_at: null` 表示时间未知。未知**不算更新**:不要用 memory_at 为 null 的 memory 去 SUPERSEDE 一条有时间的记忆。
966
+ * 不要编造时间顺序。
967
+ * 不要仅因为“看起来更新”就 supersede。
961
968
 
962
969
  ---
963
970
 
@@ -221,6 +221,8 @@ Rules:
221
221
  * merge losslessly
222
222
  * preserve all useful information
223
223
  * do NOT create duplicates
224
+ * if the two disagree on a value, keep the value from the memory with the later
225
+ `memory_at`; never let an older memory overwrite a newer value
224
226
 
225
227
  ### Same occurrence vs. another occurrence
226
228
 
@@ -255,6 +257,7 @@ Use ONLY when:
255
257
  * the new memory contradicts an existing memory
256
258
  * both cannot be true at the same time
257
259
  * the old memory would now give the wrong current-state answer
260
+ * the new memory's `memory_at` is the same as or LATER than the existing memory's `memory_at`
258
261
 
259
262
  Examples:
260
263
 
@@ -268,6 +271,8 @@ Do NOT use for:
268
271
  * compatible preferences
269
272
  * wording differences
270
273
  * semantic equivalence
274
+ * a new memory whose `memory_at` is EARLIER than the existing memory's — it is an
275
+ older state and must never replace a newer one (see "Time order")
271
276
 
272
277
  Rules:
273
278
 
@@ -289,7 +294,8 @@ Rules:
289
294
  ### Folding several existing memories into one chain
290
295
 
291
296
  If TWO OR MORE existing memories track the SAME dimension and the new memory is
292
- the latest change on that dimension, you may fold them into a single evolution
297
+ the latest change on that dimension — its `memory_at` is later than every folded
298
+ memory's `memory_at` — you may fold them into a single evolution
293
299
  chain. Use `memory_ids` (an ordered list, OLDEST → NEWEST) instead of a single
294
300
  `memory_id`:
295
301
 
@@ -318,8 +324,7 @@ older memories' text into it; they remain queryable in the chain history.
318
324
  * Separate dated occurrences stay separate ADDs; use `same_series` only for the
319
325
  same subject and same concrete activity/event type.
320
326
  * Keep each memory focused on one coherent topic.
321
- * Do not invent chronology.
322
- * `memory_at: null` means unknown time.
327
+ * Follow "Time order" below.
323
328
  * **NEVER merge or supersede across different subjects.** The subject is whoever
324
329
  the content is about: "The user ..." vs "John ..." vs "Mary ...". Memories about
325
330
  different people are independent facts even when the sentence is otherwise
@@ -332,6 +337,27 @@ older memories' text into it; they remain queryable in the chain history.
332
337
 
333
338
  ---
334
339
 
340
+ # Time order
341
+
342
+ Every memory, new or existing, has `memory_at`: when it happened. Memories are
343
+ NOT always written in the order they happened — history can be imported late,
344
+ and a memory can be added while older history is still being processed. A new
345
+ memory can therefore be OLDER than an existing one.
346
+
347
+ * Decide which memory is newer ONLY by `memory_at`. "New memories" means "being
348
+ written now", not "more recent".
349
+ * An older memory must never replace a newer one: no SUPERSEDE of a newer
350
+ memory, and no UPDATE that overwrites a newer value with an older one.
351
+ * If a new memory is older than an existing memory it conflicts with, keep the
352
+ existing memory as the current state. ADD the new one as a past state, worded
353
+ in the past with its time (for example "In 2023 the user lived in Shanghai"),
354
+ or output nothing if it adds no information.
355
+ * `memory_at: null` means unknown time. Unknown is NOT newer: never SUPERSEDE a
356
+ memory that has a known `memory_at` with one whose `memory_at` is null.
357
+ * Do not invent chronology.
358
+
359
+ ---
360
+
335
361
  # Optional: `related`
336
362
 
337
363
  On any op you MAY add a `related` array recording how the memory being written
@@ -441,6 +467,81 @@ def append_reconcile_structure_prompt(
441
467
  return prompt + RECONCILE_STRUCTURE_METADATA_PROMPT_EN
442
468
 
443
469
 
470
+ def _as_aware(value: Any) -> Optional[datetime]:
471
+ """memory_at as a comparable aware datetime; None when unknown or unreadable."""
472
+ if isinstance(value, str) and value:
473
+ try:
474
+ value = datetime.fromisoformat(value)
475
+ except (TypeError, ValueError):
476
+ return None
477
+ if not isinstance(value, datetime):
478
+ return None
479
+ # Stored nodes rebuild memory_at from epoch seconds as naive local time
480
+ # (MemoryNode.from_dict), so a naive value is local, not UTC.
481
+ return value if value.tzinfo is not None else value.astimezone()
482
+
483
+
484
+ def enforce_time_order(
485
+ ops: List["ReconcileOp"],
486
+ new_memory_at: Any,
487
+ existing: Dict[str, Any],
488
+ ) -> List["ReconcileOp"]:
489
+ """An older memory must never replace a newer one, whatever the model says.
490
+
491
+ Memories are not always written in the order they happened: history is
492
+ imported late, and a person can add a memory while older history is still
493
+ being processed. The prompt tells the model to compare `memory_at`; this
494
+ makes it hold. A SUPERSEDE or UPDATE aimed at an existing memory whose
495
+ `memory_at` is later than the memory being written is turned into an ADD,
496
+ so the newer memory stays the current state. Unknown times are not
497
+ compared. A content-less UPDATE (the legacy DELETE mapping) is dropped.
498
+ """
499
+ written_at = _as_aware(new_memory_at)
500
+ if written_at is None:
501
+ return ops
502
+ kept: List[ReconcileOp] = []
503
+ for op in ops:
504
+ if op.op not in ("SUPERSEDE", "UPDATE"):
505
+ kept.append(op)
506
+ continue
507
+ targets = list(op.memory_ids) if op.memory_ids else [op.memory_id]
508
+ newer = [
509
+ target for target in targets
510
+ if target and (node := existing.get(target)) is not None
511
+ and (target_at := _as_aware(getattr(node, "memory_at", None))) is not None
512
+ and target_at > written_at
513
+ ]
514
+ if not newer:
515
+ kept.append(op)
516
+ continue
517
+ logger.warning(
518
+ "[reconciler] %s blocked: memory_at %s is older than existing %s; "
519
+ "kept the newer memory",
520
+ op.op, written_at.isoformat(), ",".join(newer),
521
+ )
522
+ if not op.content:
523
+ continue
524
+ kept.append(ReconcileOp(
525
+ op="ADD",
526
+ content=op.content,
527
+ layer=op.layer,
528
+ owner=op.owner,
529
+ tags=list(op.tags),
530
+ tag_ners=list(op.tag_ners),
531
+ topic_paths=list(op.topic_paths),
532
+ event_facet=dict(op.event_facet),
533
+ topic_assignments=list(op.topic_assignments),
534
+ structure_references=list(op.structure_references),
535
+ topic_paths_provided=op.topic_paths_provided,
536
+ event_facet_provided=op.event_facet_provided,
537
+ topic_assignments_provided=op.topic_assignments_provided,
538
+ speculate=op.speculate,
539
+ related=list(op.related),
540
+ reason=f"time_order: older than {','.join(newer)}; {op.reason}"[:500],
541
+ ))
542
+ return kept
543
+
544
+
444
545
  def build_reconcile_prompt(
445
546
  *,
446
547
  new_memories: List[str],
@@ -1399,6 +1500,7 @@ class MemoryReconciler:
1399
1500
  _tok_total += response.tokens_used
1400
1501
  raw_response = response.content
1401
1502
  ops = self._parse_ops(raw_response)
1503
+ ops = enforce_time_order(ops, current_time, candidate_map)
1402
1504
  ops = self._inherit_exact_input_structure(
1403
1505
  ops, new_memories_with_meta,
1404
1506
  )