@furongjun1999/dsh-memory 0.6.1 → 0.7.0

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 (48) hide show
  1. package/README.md +48 -18
  2. package/docs/README.md +1 -0
  3. package/docs/eval//345/207/272/350/264/247/351/235/242/345/206/222/347/203/237_/350/277/233/350/264/247/351/227/250/347/246/201_v1.0.md +460 -0
  4. package/docs/eval//345/217/221/345/270/20308_/350/207/252/350/277/255/344/273/243/344/270/216/347/235/241/347/234/240_/345/233/276/346/243/200/347/264/242/350/267/257/344/270/216/346/235/203/351/207/215_v1.0.md +97 -0
  5. package/docs/eval//345/275/222/344/270/200/345/261/202/347/274/272/347/234/201/347/277/273/345/205/263_/344/277/256/345/244/215/350/256/260/345/275/225_v1.0.md +458 -0
  6. package/docs/hive//346/243/200/347/264/242/347/256/227/346/263/225/345/217/243/345/276/204/345/257/271/347/205/247_v0.1.md +136 -11
  7. package/docs/hive//346/243/200/347/264/242/350/267/257/345/276/204/344/270/216/350/256/244/347/237/245/347/273/223/346/236/204/345/245/221/347/272/246_v0.1.md +32 -2
  8. package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.10.md +88 -0
  9. package/docs/mdcg//345/212/237/350/203/275/350/260/203/347/224/250/346/230/240/345/260/204/350/241/250_v0.1.md +40 -40
  10. package/docs/mdcg//345/217/221/345/270/203/351/227/250/347/246/201/351/223/276_v0.1.md +47 -11
  11. package/docs/mdcg//347/235/241/347/234/240/345/221/250/346/234/237_/350/277/220/347/273/264/345/211/215/346/217/220/344/270/216/347/273/264/346/212/244/346/214/207/345/215/227_v1.0.md +183 -0
  12. package/docs/plans//347/235/241/347/234/240/344/270/216/350/207/252/350/277/255/344/273/243_/345/212/237/350/203/275/344/274/230/345/214/226/350/256/276/350/256/241_v0.4.md +547 -0
  13. package/md_cg/bench_e2e_locomo_qa.py +11 -4
  14. package/md_cg/chain.py +47 -0
  15. package/md_cg/freshness.py +511 -0
  16. package/md_cg/generation.py +409 -0
  17. package/md_cg/hotcache.py +4 -1
  18. package/md_cg/mcp_server.py +128 -19
  19. package/md_cg/mdcg.py +509 -22
  20. package/md_cg/mdcos.py +203 -22
  21. package/md_cg/nodefile.py +74 -1
  22. package/md_cg/semantic/canonical.py +22 -0
  23. package/md_cg/semantic/unify.py +73 -22
  24. package/md_cg/semantic/unify_fixture.json +25 -0
  25. package/md_cg/sleep.py +1297 -0
  26. package/md_cg/sustain.py +146 -9
  27. package/md_cg/test_auto_defaults.py +424 -0
  28. package/md_cg/test_boundary_hit.py +410 -0
  29. package/md_cg/test_en_pipeline.py +22 -13
  30. package/md_cg/test_generation_guard.py +352 -0
  31. package/md_cg/test_h4_sustain_snapshot.py +14 -4
  32. package/md_cg/test_n212_n213_n224_generation_gates.py +14 -8
  33. package/md_cg/test_n230_dirty_replay.py +375 -0
  34. package/md_cg/test_p2_six_elements.py +463 -0
  35. package/md_cg/test_p3_legacy_closure.py +433 -0
  36. package/md_cg/test_p4_freshness.py +680 -0
  37. package/md_cg/test_p8_subgraph_chain.py +14 -1
  38. package/md_cg/test_rank_parity_score_mode.py +6 -0
  39. package/md_cg/test_semantic_canonical.py +5 -3
  40. package/md_cg/test_sleep.py +611 -0
  41. package/md_cg/test_sleep_p1.py +784 -0
  42. package/md_cg/test_time_core_lint.py +968 -0
  43. package/md_cg/test_unify_default_off.py +701 -0
  44. package/md_cg/test_unify_scope.py +182 -0
  45. package/md_cg/whitebox_kb/aeis_core/time_core.py +8 -0
  46. package/package.json +3 -2
  47. package/skills/plugin.json +1 -1
  48. package/utf8_boot.py +237 -0
@@ -180,7 +180,9 @@ TOOLS = [
180
180
  "「跳过超大、继续试更小的」在预算紧张时淘汰最有价值的详实条目。"
181
181
  "会话开始或重要工作前调用。可选启用第 5 路模糊召回(分级隶属度)"
182
182
  "与第 6 路条件语义路(条件结构驱动),并注入调用方 LLM 的查询"
183
- "扩展词(索引侧始终白箱)。",
183
+ "扩展词(索引侧始终白箱)。缺省另含第 7 路因果路(沿 edges 多跳)"
184
+ "与第 8 路时间路(时间邻近度)——按裁定「全进默认检索」缺省开,"
185
+ "可用 causal=false / temporal=false 单独关。",
184
186
  "inputSchema": _s("", query=_p("string", "描述当前任务的查询", True),
185
187
  budget_tokens=_p("integer", "token 预算(默认 1200)"),
186
188
  max_item_tokens=_p("integer", "单条上限(默认 250);超限条目截断纳入。"
@@ -203,12 +205,22 @@ TOOLS = [
203
205
  goal=_p("string", "当前目标(第 5 篇第 3 章):启用 goal 路给召回定向;"
204
206
  "省略则自动取活跃目标"),
205
207
  goal_path=_p("boolean", "启用目标定向路(默认否;给 goal 即自动启用)"),
208
+ causal=_p("boolean", "因果路(P3,设计稿 §6.2):以词法/实体命中"
209
+ "为种子沿 edges 多跳扩散,**缺省开**(全进默认"
210
+ "检索);传 false 单独关本路。无 edges 的库上"
211
+ "自然为空"),
212
+ temporal=_p("boolean", "时间路(P3,设计稿 §6.3):按时间邻近度"
213
+ "排序的排名项,核走 time_core.cred_factor;"
214
+ "**缺省开**;传 false 单独关本路。无时间算子/"
215
+ "区间时恒空"),
206
216
  include_recent=_p("boolean", "是否附「近期事件」窗口(默认否)"),
207
217
  recent_limit=_p("integer", "近期事件条数(默认 10)"),
208
218
  fusion=_p("string", "融合模式:sum(经典 RRF,奖励多路共识)"
209
219
  "| max(取各路最高贡献,不奖励共识)。"
210
220
  "fuzzy=true 时缺省 max——实测 sum 会低估"
211
- "「只有模糊路捞到」的目标,self@1 −10.1%")),
221
+ "「只有模糊路捞到」的目标,self@1 −10.1%;"
222
+ "因果路(causal,缺省开)同为单路独有召回型,"
223
+ "故一并缺省 max")),
212
224
  },
213
225
  {
214
226
  "name": "mdcg_search",
@@ -1567,6 +1579,7 @@ def _sustain_call(cg, a):
1567
1579
  路线图「常驻服务:会话/心跳/自愈」。原则:诊断只读;自愈只碰派生物
1568
1580
  (索引/临时文件/日志边界),永不删节点;缺密钥属于权限事实,只报告不修。
1569
1581
  """
1582
+ from . import sleep as _sleep
1570
1583
  from . import sustain
1571
1584
  act = (a.get("action") or "status").strip().lower()
1572
1585
  name = a.get("name") or os.environ.get("MDCG_SUSTAIN_NAME") or "md_cg"
@@ -1627,16 +1640,27 @@ def _sustain_call(cg, a):
1627
1640
  or sustain.DEFAULT_BEAT_INTERVAL),
1628
1641
  heal_interval=float(a.get("heal_interval")
1629
1642
  or sustain.DEFAULT_HEAL_INTERVAL),
1630
- auto_heal=bool(a.get("auto_heal", True)),
1643
+ # 四档 auto_* 一律经 sustain 的**单一真源读取器**(P0-2)——此处
1644
+ # 不得再写缺省字面量:op 路径原 `auto_tidy` 缺省 False 与 env 路径
1645
+ # 的 True 相反,同 (root,name) 走两条入口语义不一致。显式入参仍优先。
1646
+ auto_heal=sustain.auto_from_args("auto_heal", a),
1631
1647
  scrub_interval=float(a.get("scrub_interval")
1632
1648
  or sustain.DEFAULT_SCRUB_INTERVAL),
1633
- auto_scrub=bool(a.get("auto_scrub", False)),
1649
+ auto_scrub=sustain.auto_from_args("auto_scrub", a),
1634
1650
  evolve_interval=float(a.get("evolve_interval")
1635
1651
  or sustain.DEFAULT_EVOLVE_INTERVAL),
1636
- auto_evolve=bool(a.get("auto_evolve", False)),
1652
+ auto_evolve=sustain.auto_from_args("auto_evolve", a),
1637
1653
  tidy_interval=float(a.get("tidy_interval")
1638
1654
  or sustain.DEFAULT_TIDY_INTERVAL),
1639
- auto_tidy=bool(a.get("auto_tidy", False)))
1655
+ auto_tidy=sustain.auto_from_args("auto_tidy", a),
1656
+ # 第六档睡眠周期(§4.7):五个值一律经 `md_cg/sleep.py` 的**单一真源
1657
+ # 读取器**(此处不得再写缺省字面量,也不得再写 env 键名字面量——
1658
+ # 键名真源是 `sleep.SLEEP_ENV_KEYS`)。与 env 路径逐字同一组调用。
1659
+ sleep_interval=_sleep.sleep_interval(),
1660
+ auto_sleep=_sleep.sleep_enabled(),
1661
+ sleep_merge=_sleep.sleep_merge_mode(),
1662
+ sleep_window=_sleep.sleep_window(),
1663
+ sleep_scrub_apply=_sleep.sleep_scrub_apply())
1640
1664
  return {"loop": lp.start().status()}
1641
1665
  if act in ("stop", "down"):
1642
1666
  lp = sustain.get_loop(cg, name)
@@ -2220,6 +2244,21 @@ def _cg_dispatch(cg, a):
2220
2244
  h["write_policy"] = audit.unavailable_report(_pe)
2221
2245
  h["theory"] = _th.check()
2222
2246
  h["links"] = _lk.ls()
2247
+ # 代校验(纯增量,2026-09-30):长驻进程可被问「你现在这一代是不是盘面那一代」。
2248
+ # 根因(第4条取证):升级后长驻进程仍持有启动代模块,与升级后惰性导入的新代
2249
+ # 模块跨代混用(实测 cg 写入 ImportError: cannot import name 'mint_auto_id'
2250
+ # from 'md_cg.mdcg',而盘面正常)——盘面无从自证,须由进程自报。
2251
+ # 落点=generation.report()(单点):code_generation / disk_generation /
2252
+ # stale_on_disk / restart_required / hint。**纯增量**:既有键名、键值、
2253
+ # 嵌套结构一字不动(本行只追加五个新键,键名与既有键无冲突)。
2254
+ # 失败不得把 op=info 拖崩(同为诊断面契约):异常在此收口成不可用形状。
2255
+ from . import generation as _gen
2256
+ try:
2257
+ h.update(_gen.report())
2258
+ except Exception as _ge: # noqa: BLE001
2259
+ h.update({"code_generation": None, "disk_generation": None,
2260
+ "stale_on_disk": None, "restart_required": None,
2261
+ "hint": "代校验不可用:%s: %s" % (type(_ge).__name__, _ge)})
2223
2262
  return h
2224
2263
 
2225
2264
  if op == "route":
@@ -3229,6 +3268,25 @@ _MDCG_OP_REQUIRE = {
3229
3268
  }
3230
3269
 
3231
3270
 
3271
+ # 生效条件:四个布尔入参任意组合下无条件返回 "max"(当 use_fuzzy 或 use_semantic 或 use_goal 或 use_causal 任一为真)或 None(全为假);use_temporal 不参与判据;不做 IO、不看环境变量;
3272
+ def recall_fusion_default(use_fuzzy, use_semantic, use_goal, use_causal,
3273
+ use_temporal=False):
3274
+ """`mdcg_recall` 的**缺省融合口径**判据(唯一单点,供守卫断言)。
3275
+
3276
+ P3-遗留 A(2026-10-01 补披露):`use_causal` **缺省 True** ⇒ 默认 recall 的
3277
+ 融合口径由 `sum` 变成 **`max`**(影响**所有**默认召回调用,与库是否有边无关)。
3278
+ 该口径符合设计稿 §6.2「因果路融合缺省 max」与既有先例表达式
3279
+ (fuzzy/semantic/goal 同款),故**保留**;本函数只把那个表达式从调用点提为
3280
+ **可断言的名字**——否则「缺省到底是 sum 还是 max」只能靠读表达式,
3281
+ 静默漂移(少写一个 `or use_causal`)无守卫可抓。
3282
+
3283
+ `use_temporal` **刻意不入判据**:时间路是**排名项**(§6.3(e):与 S4 层级
3284
+ 激活同类,不是单路独有召回型),它开着也不改变融合口径。
3285
+ """
3286
+ return ("max" if (use_fuzzy or use_semantic or use_goal or use_causal)
3287
+ else None)
3288
+
3289
+
3232
3290
  # 生效条件:name 为已注册工具名之一(cg / stg / mdcg_whitebox / mdcg_service_info / mdcg_remember 等);name 属 _MDCG_OP_REQUIRE 且 cg.principal 具 require_op 属性时先 require_op(越权抛 AccessDenied),principal 为 None 或无该方法时跳过;cg 走 _cg_call、stg 走 _stg_call、whitebox 走 _whitebox_call;未识别的 name 返回含 error 的响应字典而不抛异常,进程不因此中断;
3233
3291
  def _dispatch(cg, name, args):
3234
3292
  a = args or {}
@@ -3332,7 +3390,16 @@ def _dispatch(cg, name, args):
3332
3390
  use_fuzzy = bool(a.get("fuzzy"))
3333
3391
  use_semantic = bool(a.get("semantic"))
3334
3392
  use_goal = bool(a.get("goal_path") or a.get("goal"))
3335
- if use_fuzzy or use_semantic or use_goal:
3393
+ # P3(裁定 9「全进默认检索」):因果路(chain)/ 时间路(temporal)**进缺省集**,
3394
+ # 但**每路可单独关**——显式传 causal=false / temporal=false 即从本次调用剔除。
3395
+ # 未显式传(None)= 缺省进路;与既有 fuzzy/semantic/goal 的「显式启用」语义相反,
3396
+ # 这是刻意的非对称:这两路在无 edges / 无时间参数的库上自然为空,缺省开不产噪声。
3397
+ use_causal = a.get("causal")
3398
+ use_temporal = a.get("temporal")
3399
+ use_causal = True if use_causal is None else bool(use_causal)
3400
+ use_temporal = True if use_temporal is None else bool(use_temporal)
3401
+ if (use_fuzzy or use_semantic or use_goal
3402
+ or not use_causal or not use_temporal):
3336
3403
  paths = ["lexical", "bucket", "entity", "graph"]
3337
3404
  if use_fuzzy:
3338
3405
  paths.append("fuzzy")
@@ -3340,13 +3407,22 @@ def _dispatch(cg, name, args):
3340
3407
  paths.append("semantic")
3341
3408
  if use_goal:
3342
3409
  paths.append("goal")
3410
+ if use_causal:
3411
+ paths.append("chain")
3412
+ if use_temporal:
3413
+ paths.append("temporal")
3343
3414
  paths = tuple(paths)
3344
3415
  else:
3345
- paths = None
3416
+ paths = None # 缺省六路(mdcos.search_rrf 的缺省集)
3346
3417
  # fuzzy 路缺省用 max 融合:实测(memory-bench-1000,870 查询)sum 会把
3347
3418
  # self@1 拉低 10.1%,因为求和奖励「多路共识」、低估「模糊路独有」的目标。
3348
3419
  # semantic 路同理:条件结构命中常是「独有召回」,故一并缺省 max。
3349
- fusion = a.get("fusion") or ("max" if (use_fuzzy or use_semantic or use_goal) else None)
3420
+ # P3:因果路按设计稿 §6.2「融合口径」同为**单路独有召回型**,照抄本条先例
3421
+ # 形态(同一表达式内追加条件,不另起第二套判据)。
3422
+ # P3-遗留 A(2026-10-01):表达式提为 `recall_fusion_default` 单点,
3423
+ # 使「缺省口径 = max」可被守卫直接断言(防静默漂移)。
3424
+ fusion = a.get("fusion") or recall_fusion_default(
3425
+ use_fuzzy, use_semantic, use_goal, use_causal)
3350
3426
  # 默认单条上限从 mdcos 取(该模块只在 main() 里惰性导入,模块级没有名字,
3351
3427
  # 直接引用 mdcos.DEFAULT_MAX_ITEM_TOKENS 会 NameError —— 故此处按需导入)。
3352
3428
  from .mdcos import DEFAULT_MAX_ITEM_TOKENS as _DEFAULT_MAX_ITEM
@@ -3555,6 +3631,7 @@ def _start_sustain(cg):
3555
3631
  """启动常驻自维持循环(MDCG_SUSTAIN=0 关闭;间隔可用环境变量调)。"""
3556
3632
  if os.environ.get("MDCG_SUSTAIN", "1") in ("0", "false", "False"):
3557
3633
  return None
3634
+ from . import sleep as _sleep
3558
3635
  from . import sustain
3559
3636
  name = os.environ.get("MDCG_SUSTAIN_NAME") or "md_cg"
3560
3637
  lp = sustain.ensure_loop(
@@ -3563,22 +3640,28 @@ def _start_sustain(cg):
3563
3640
  or sustain.DEFAULT_BEAT_INTERVAL),
3564
3641
  heal_interval=float(os.environ.get("MDCG_SUSTAIN_HEAL")
3565
3642
  or sustain.DEFAULT_HEAL_INTERVAL),
3566
- auto_heal=os.environ.get("MDCG_SUSTAIN_AUTOHEAL", "1")
3567
- not in ("0", "false", "False"),
3643
+ # 四档 auto_* 一律经 sustain 的**单一真源读取器**(P0-2):缺省值只在
3644
+ # `sustain.AUTO_DEFAULTS` 定义一处,此处不得再写字面量(含 env 键名——
3645
+ # 键名真源是 `sustain.AUTO_ENVS`)。整理巡检缺省开:确定性动作、
3646
+ # 永不删除节点(可逆可审计);opt-out `MDCG_AUTO_TIDY=0`。
3647
+ auto_heal=sustain.auto_from_env("auto_heal"),
3568
3648
  scrub_interval=float(os.environ.get("MDCG_SCRUB_INTERVAL")
3569
3649
  or sustain.DEFAULT_SCRUB_INTERVAL),
3570
- auto_scrub=os.environ.get("MDCG_AUTO_SCRUB", "0")
3571
- not in ("0", "false", "False"),
3650
+ auto_scrub=sustain.auto_from_env("auto_scrub"),
3572
3651
  evolve_interval=float(os.environ.get("MDCG_EVOLVE_INTERVAL")
3573
3652
  or sustain.DEFAULT_EVOLVE_INTERVAL),
3574
- auto_evolve=os.environ.get("MDCG_AUTO_EVOLVE", "0")
3575
- not in ("0", "false", "False"),
3653
+ auto_evolve=sustain.auto_from_env("auto_evolve"),
3576
3654
  tidy_interval=float(os.environ.get("MDCG_TIDY_INTERVAL")
3577
3655
  or sustain.DEFAULT_TIDY_INTERVAL),
3578
- # 整理巡检(contextual 同构组聚合+成员降权)默认开:确定性动作、
3579
- # 永不删除节点(可逆可审计);MDCG_AUTO_TIDY=0 关闭
3580
- auto_tidy=os.environ.get("MDCG_AUTO_TIDY", "1")
3581
- not in ("0", "false", "False"))
3656
+ auto_tidy=sustain.auto_from_env("auto_tidy"),
3657
+ # 第六档睡眠周期(§4.7):五个值一律经 `md_cg/sleep.py` 的**单一真源
3658
+ # 读取器**——键名真源是 `sleep.SLEEP_ENV_KEYS`、缺省真源是
3659
+ # `sleep.SLEEP_ENV_DEFAULTS`,本处(与 op 路径)都不得再写字面量。
3660
+ sleep_interval=_sleep.sleep_interval(),
3661
+ auto_sleep=_sleep.sleep_enabled(),
3662
+ sleep_merge=_sleep.sleep_merge_mode(),
3663
+ sleep_window=_sleep.sleep_window(),
3664
+ sleep_scrub_apply=_sleep.sleep_scrub_apply())
3582
3665
  lp.start()
3583
3666
  return lp
3584
3667
 
@@ -4109,6 +4192,32 @@ def main():
4109
4192
  "该进程的代际将无法被外部机械判定)\n")
4110
4193
  except Exception as _exc: # noqa: BLE001 —— 自报不得阻塞启动
4111
4194
  sys.stderr.write("[mdcg-mcp] 自报异常(不阻塞启动): %r\n" % (_exc,))
4195
+ # 代校验(generation 单点,2026-09-30):stdio 服务**真正开始服务之前**,把盘面
4196
+ # 上所有「函数内延迟导入」在本进程的模块面上试解析一遍(导入目标模块 → getattr
4197
+ # 取名字)。根因(第4条取证):长驻进程只在模块首次导入时读盘,而 md_cg 内有
4198
+ # 大量写在函数里的延迟导入(AST 实测 328 处,相当一部分是为避开循环导入)——
4199
+ # 升级后旧代已载入模块与新代惰性模块跨代混用,实测表现为 cg 写入返回
4200
+ # `ImportError: cannot import name 'mint_auto_id' from 'md_cg.mdcg'` 而盘面完全
4201
+ # 正常(该延迟导入要到运行期才炸,且报错点离根因很远)。此处提前到启动期显式
4202
+ # 判一次:**名字缺失即拒绝启动**(fail-closed,消息自带「请重启常驻 MCP 进程」
4203
+ # 指引);外部可选模块缺失(不在盘面的可选降级面,如 whitebox_kb/aeis_core 的
4204
+ # 惰性身体面)非致命,但计数如实上报,不静默。
4205
+ # 调用点必须在**函数体内**:这些延迟导入里相当一部分是为避开循环导入才写在函数
4206
+ # 里的,本校验若放到模块顶层会把避环结构重新咬回来(同族守卫
4207
+ # md_cg/test_generation_guard.py 有 AST 断言钉死这一点)。
4208
+ try:
4209
+ from . import generation as _gen
4210
+ _dep = _gen.verify_delegations()
4211
+ except Exception as _dep_exc: # noqa: BLE001 —— 未通过即拒绝服务,不带病运行
4212
+ sys.stderr.write("[mdcg-mcp] 代校验未通过(拒绝启动,fail-closed):%s\n%s\n"
4213
+ % (type(_dep_exc).__name__, _dep_exc))
4214
+ return 4
4215
+ sys.stderr.write(
4216
+ "[mdcg-mcp] 代校验:延迟导入 %d 项 / 目标模块 %d 个 / 已解析 %d 项 / "
4217
+ "可选缺失 %d 项 · 代 %s\n"
4218
+ % (_dep.get("delegations"), _dep.get("target_modules"),
4219
+ _dep.get("resolved"), len(_dep.get("unavailable") or []),
4220
+ _gen.STARTUP_FINGERPRINT[:12]))
4112
4221
  _start_sustain(cg)
4113
4222
 
4114
4223
  for line in sys.stdin: