@furongjun1999/dsh-memory 0.4.7 → 0.4.8
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.
- package/README.md +92 -37
- package/codebuddy/CODEBUDDY.md +195 -185
- package/docs/Pi/345/217/257/345/255/246/344/271/240/344/274/230/347/202/271_/347/201/265/346/236/242/345/244/247/350/204/221/346/224/271/350/277/233/344/272/244/346/216/245_20260915.md +144 -0
- package/docs/README.md +111 -0
- package/docs/discipline/harnesses.yaml +215 -152
- package/docs/discipline/templates/full.md.tmpl +60 -58
- package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_LoCoMo_/347/201/265/346/236/242_.md +127 -0
- package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_LoCoMo_/347/201/265/346/236/242_.png +0 -0
- package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_/347/201/265/346/236/242_vs_dejavu_/347/273/237/344/270/200/350/257/204/345/210/206_v7.md +202 -0
- package/docs/experiments/m4-role-probe/census.py +86 -0
- package/docs/experiments/m4-role-probe/probe.py +89 -0
- package/docs/experiments/mapped_confidence/bench_mapped_conf.py +216 -0
- package/docs/experiments/mapped_confidence/post_check.py +75 -0
- package/docs/experiments/mapped_confidence/repro_thirdparty_tol.py +83 -0
- package/docs/experiments/mapped_confidence/result_mapped_conf.json +215 -0
- package/docs/{ → hive/}/350/234/202/345/267/242/345/267/245/344/275/234/350/256/260/345/277/206_/351/241/271/347/233/256/350/256/241/345/210/222.md +14 -2
- package/docs/{README → mdcg/README}/350/257/246/347/273/206/347/211/210_v0.4.5.md +9 -9
- package/docs/{ → mdcg/}/344/270/273/344/273/243/347/220/206/345/255/220/344/273/243/347/220/206/350/256/260/345/277/206/346/236/266/346/236/204/350/256/276/350/256/241.md +1 -1
- 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 +261 -0
- package/docs/{ → mdcg/}/345/215/225/345/205/203/350/207/252/346/210/221/351/224/232/347/202/271_/347/263/273/347/273/237/346/217/220/347/244/272/350/257/215/346/240/207/345/207/206_v0.3.md +1 -1
- package/docs/mdcg//345/255/220/344/273/243/347/220/206/351/205/215/347/275/256/346/240/207/345/207/206_v0.4.md +148 -0
- package/docs/{ → mdcg/}/346/272/220/347/240/201/347/272/247/346/236/266/346/236/204/345/256/241/350/256/241_GPT/346/211/271/350/257/204/345/257/271/347/205/247_v1.0.md +2 -2
- package/docs/{ → mdcg/}/347/201/265/346/236/242/344/270/211/345/261/202/346/213/206/345/210/206/350/247/204/345/210/222_v0.1.md +181 -181
- package/docs/{ → mdcg/}/347/274/272/345/217/243/345/215/225_P0/346/224/266/345/217/243_v0.1.md +2 -2
- package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_G4-G8/347/274/272/345/217/243/350/243/201/345/256/232/345/215/225_v0.1.md +1 -1
- package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_/347/264/242/345/274/225/344/270/216/345/267/245/347/250/213/350/247/204/350/214/203/345/214/226_/350/256/241/345/210/222_v0.1.md +4 -4
- package/docs/{ → mdcg/}/350/256/260/345/277/206/346/223/215/344/275/234/347/263/273/347/273/237_MdCGOS/344/270/216MCP/346/216/245/345/205/245_v0.1.md +2 -2
- package/docs/plans/GridWorld/346/234/200/345/260/217/351/227/255/347/216/257/350/247/204/346/240/274_v0.1.md +176 -0
- package/docs/{ → swarm/}/350/234/202/347/276/244/344/272/222/350/201/224_v0.1.md +3 -3
- package/docs/swarm//350/234/202/347/276/244/345/220/214/351/224/231/346/243/200/346/265/213/345/256/236/351/252/214/345/215/217/350/256/256_v0.1.md +242 -0
- package/docs/theory//345/215/225/347/272/277/347/250/213/344/270/216/346/263/250/346/204/217/345/212/233/351/233/206/344/270/255_/346/227/240/344/272/211/350/256/256/347/220/206/350/256/272/346/226/207/346/241/243_v1.0.md +129 -0
- package/docs/theory//345/271/266/345/217/221/345/277/205/347/204/266/346/200/247/347/220/206/350/256/272_v0.2.md +134 -0
- package/docs/theory//346/246/202/345/277/265/345/210/206/345/261/202/345/257/271/351/275/220/350/241/250_v0.1.md +145 -0
- package/docs/{ → theory/}/347/220/206/350/256/272_/346/234/272/345/210/266_/344/273/243/347/240/201_/345/256/236/351/252/214_/347/274/272/345/217/243/347/237/251/351/230/265_v0.1.md +3 -3
- package/docs/{ → theory/}/347/220/206/350/256/272/344/273/223/346/213/206/345/210/206/344/270/216/347/231/275/347/256/261/347/237/245/350/257/206/345/272/223/345/206/205/350/277/201_v0.1.md +1 -1
- package/docs/theory//350/256/244/347/237/245/344/273/243/347/220/206/344/270/216/346/224/266/346/225/233/347/273/223/346/236/204_/346/235/241/344/273/266/350/256/272/351/207/215/346/236/204_v0.1.md +221 -0
- package/docs/world_model//344/270/226/347/225/214/346/250/241/345/236/213_/347/245/236/347/273/217/347/275/221/347/273/234/345/272/225/345/261/202/346/236/266/346/236/204_v1.0.md +237 -0
- package/docs//345/255/230/347/256/227/344/270/200/344/275/223/346/236/266/346/236/204/345/255/246/344/271/240_/345/244/226/351/203/250/347/220/206/350/256/272/345/257/271/347/205/247/344/270/216/350/267/257/347/272/277/344/272/244/346/216/245_20260916.md +98 -0
- package/docs//345/255/230/347/256/227/344/270/200/344/275/223/347/245/236/347/273/217/347/275/221/347/273/234/346/236/266/346/236/204_/346/200/273/347/272/262/344/270/216/344/272/244/346/216/245_20260916.md +109 -0
- package/docs//345/267/245/344/275/234/347/272/252/345/276/213_/350/256/244/347/237/245/345/233/276/346/235/241/347/233/256_v1.1.json +34 -6
- package/docs//347/231/275/347/256/261/345/214/226/347/272/262/351/242/206_/344/270/211/351/241/271/347/233/256/345/255/246/344/271/240/346/200/273/346/236/266/346/236/204/344/270/216Ghidra/344/272/244/346/216/245_20260916.md +104 -0
- package/docs//350/256/260/345/277/206/350/257/204/345/256/241/347/263/273/347/273/237_/347/253/213/351/241/271/350/256/276/350/256/241/344/270/216/346/226/275/345/267/245/344/272/244/346/216/245_20260915.md +183 -0
- package/dsh/README.md +1 -1
- package/lib/hooks.d.ts +5 -0
- package/lib/hooks.js +10 -1
- package/lib/lib/prompt_safety.d.ts +48 -0
- package/lib/lib/prompt_safety.js +62 -0
- package/md_cg/audit.py +76 -2
- package/md_cg/bench_membench.py +10 -5
- package/md_cg/bench_progressive.py +48 -9
- package/md_cg/branches.py +275 -0
- package/md_cg/ccgc.py +884 -0
- package/md_cg/conformance.py +662 -0
- package/md_cg/consistency.py +1 -1
- package/md_cg/corpus.py +1 -1
- package/md_cg/eval_common.py +6 -5
- package/md_cg/forgetting.py +11 -3
- package/md_cg/fsutil.py +63 -1
- package/md_cg/identity.py +2 -2
- package/md_cg/insight.py +2 -2
- package/md_cg/lifecycle.py +262 -0
- package/md_cg/mcp_server.py +547 -140
- package/md_cg/mdcg.py +206 -18
- package/md_cg/mdcos.py +512 -50
- package/md_cg/metacognition.py +2 -2
- package/md_cg/mreview/__init__.py +25 -0
- package/md_cg/mreview/__main__.py +107 -0
- package/md_cg/mreview/bundle.py +170 -0
- package/md_cg/mreview/candidates.py +253 -0
- package/md_cg/mreview/govern.py +674 -0
- package/md_cg/mreview/locate.py +905 -0
- package/md_cg/mreview/pipeline.py +701 -0
- package/md_cg/mreview/rules/duplication.json +21 -0
- package/md_cg/mreview/rules/field_coverage.json +54 -0
- package/md_cg/mreview/rules/source_license.json +21 -0
- package/md_cg/mreview/rules/template_flow.json +21 -0
- package/md_cg/mreview/ruleset.py +238 -0
- package/md_cg/nodefile.py +38 -0
- package/md_cg/predict.py +24 -6
- package/md_cg/protect.py +4 -4
- package/md_cg/refindex.py +41 -2
- package/md_cg/scrub.py +5 -1
- package/md_cg/self_state.py +1 -1
- package/md_cg/semantic/en_normalizer.py +85 -25
- package/md_cg/sustain.py +18 -0
- package/md_cg/tasks.py +447 -0
- package/md_cg/test_action_derive.py +203 -0
- package/md_cg/test_audit_rotate.py +270 -0
- package/md_cg/test_branches.py +249 -0
- package/md_cg/test_ccg_perturb.py +1 -1
- package/md_cg/test_ccgc.py +423 -0
- package/md_cg/test_conformance.py +343 -0
- package/md_cg/test_en_pipeline.py +24 -0
- package/md_cg/test_health_scale.py +173 -0
- package/md_cg/test_identity_attribution.py +6 -0
- package/md_cg/test_index_durability.py +224 -0
- package/md_cg/test_lifecycle.py +309 -0
- package/md_cg/test_mr_m1.py +679 -0
- package/md_cg/test_mr_m2.py +587 -0
- package/md_cg/test_mr_m3.py +703 -0
- package/md_cg/test_mr_m4.py +485 -0
- package/md_cg/test_p1.py +1 -1
- package/md_cg/test_p11_consistency.py +1 -1
- package/md_cg/test_p12_metacognition.py +2 -2
- package/md_cg/test_p16_self_state.py +1 -1
- package/md_cg/test_p26_refindex.py +1 -1
- package/md_cg/test_p27_docindex.py +22 -2
- package/md_cg/test_p28_refcheck.py +1 -1
- package/md_cg/test_p29_session_ingest_export.py +1 -1
- package/md_cg/test_p2_mcp.py +7 -1
- package/md_cg/test_p38_contextualize.py +1 -1
- package/md_cg/test_p39_vision_evidence.py +1 -1
- package/md_cg/test_p40_refine_worklist.py +1 -1
- package/md_cg/test_p41_evolve_patrol.py +1 -1
- package/md_cg/test_p42_provenance.py +1 -1
- package/md_cg/test_p43_pooling.py +41 -13
- package/md_cg/test_read_clip.py +137 -0
- package/md_cg/test_review_conformance.py +310 -0
- package/md_cg/test_tasks.py +409 -0
- package/md_cg/test_tool_face.py +189 -0
- package/md_cg/test_twophase.py +286 -0
- package/md_cg/test_writepipe.py +210 -0
- package/md_cg/theory.py +1 -1
- package/md_cg/tokens.py +95 -5
- package/md_cg/tool_face.py +250 -0
- package/md_cg/twophase.py +221 -0
- package/md_cg/units.py +546 -0
- package/md_cg/vision_evidence.py +1 -1
- package/md_cg/weights.py +1 -1
- package/md_cg/whitebox.py +1 -1
- package/md_cg/whitebox_kb/data/verify_cache.json +28 -0
- package/md_cg/whitebox_kb/data/verify_savings.jsonl +46 -0
- package/md_cg/whitebox_kb/wisdom/audit_log/chain_heat.json +10 -10
- package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db-shm +0 -0
- package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db-wal +0 -0
- package/md_cg/writelimit.py +19 -3
- package/md_cg/writepipe.py +372 -0
- package/package.json +1 -1
- package/src/hooks.ts +10 -1
- package/src/lib/prompt_safety.ts +62 -0
- package/zcode/AGENTS.md +195 -185
- package/docs//345/212/237/350/203/275/350/260/203/347/224/250/346/230/240/345/260/204/350/241/250_v0.1.md +0 -221
- /package/docs/{AGI → eval/AGI}/344/270/203/347/273/264/350/257/204/345/210/206/346/212/245/345/221/212_md_cg_v1.0.md" +0 -0
- /package/docs/{AGI → eval/AGI}/344/270/203/347/273/264/350/257/204/345/210/206/346/212/245/345/221/212_md_cg_v2.0.md" +0 -0
- /package/docs/{ → eval/}/345/256/236/351/252/214/346/226/271/346/241/210_/345/255/246/344/271/240/351/227/255/347/216/257AB/344/270/216/346/250/252/350/257/204_v1.0.md" +0 -0
- /package/docs/{ → eval/}/346/250/252/350/257/204_/345/205/255/345/256/266100/351/242/230/344/270/255/350/213/261/345/217/214/346/237/245_v1.0.md" +0 -0
- /package/docs/{guardrail-charter.md → mdcg/guardrail-charter.md} +0 -0
- /package/docs/{lingshu_tutorial.html → mdcg/lingshu_tutorial.html} +0 -0
- /package/docs/{memory-assessment.html → mdcg/memory-assessment.html} +0 -0
- /package/docs/{memory_score.html → mdcg/memory_score.html} +0 -0
- /package/docs/{memory_score.png → mdcg/memory_score.png} +0 -0
- /package/docs/{release_v0.3.0.md → mdcg/release_v0.3.0.md} +0 -0
- /package/docs/{release_v0.4.5.md → mdcg/release_v0.4.5.md} +0 -0
- /package/docs/{tool_table_v0.3.0.md → mdcg/tool_table_v0.3.0.md} +0 -0
- /package/docs/{ → mdcg/}/344/273/244/347/211/214/344/270/216/350/247/222/350/211/262/346/235/203/350/201/214/345/210/206/347/246/273_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/347/201/265/346/236/24282/345/267/245/345/205/267_/345/212/237/350/203/275/346/225/264/347/220/206/344/270/216/350/277/201/347/247/273/346/230/240/345/260/204_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/347/201/265/346/236/242MCP/345/267/245/345/205/267/346/200/273/350/241/250_v3.4.md" +0 -0
- /package/docs/{ → mdcg/}/347/201/265/346/236/242_/350/207/252/346/210/221/345/261/202/345/256/232/344/271/211.md" +0 -0
- /package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_MD/347/233/256/345/275/225/346/226/271/346/241/210_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_/346/235/241/344/273/266/347/251/272/351/227/264/345/220/210/346/210/220/344/270/216/347/224/237/346/225/210/346/235/241/344/273/266/345/217/243/345/276/204_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276/344/275/234/344/270/272/350/256/260/345/277/206/346/223/215/344/275/234/347/263/273/347/273/237_/350/257/204/344/274/260/344/270/216/350/267/257/347/272/277_v0.1.md" +0 -0
- /package/docs/{ → plans/}/345/221/275/345/220/215/346/262/273/347/220/206_/351/241/271/347/233/256/350/256/241/345/210/222.md" +0 -0
- /package/docs/{ → plans/}/347/237/245/350/257/206/345/233/276/350/260/261/351/241/271/347/233/256_/344/272/244/346/216/245/346/226/207/346/241/243_20260914.md" +0 -0
- /package/docs/{ → swarm/}/350/234/202/347/276/244/345/244/232/346/231/272/350/203/275/344/275/223_/344/275/277/347/224/250/346/212/245/345/221/212_20260913.md" +0 -0
- /package/docs/{ → swarm/}/350/234/202/347/276/244/345/244/232/346/231/272/350/203/275/344/275/223_/345/212/237/350/203/275/350/257/264/346/230/216_v0.6.md" +0 -0
- /package/docs/{ → swarm/}/350/234/202/347/276/244/347/233/262/345/214/272/346/240/207/350/256/260_v1.0.md" +0 -0
- /package/docs/{ → theory/}/344/277/241/346/201/257/345/267/256/344/270/272/344/273/200/344/271/210/345/277/205/347/204/266/345/255/230/345/234/250/344/270/224/350/207/252/347/204/266/346/211/251/345/244/247.md" +0 -0
- /package/docs/{ → theory/}/346/231/272/350/203/275/347/232/204/345/205/254/347/220/206/345/214/226/345/237/272/347/237/263.md" +0 -0
- /package/docs/{ → theory/}/346/231/272/350/203/275/347/232/204/350/256/244/347/237/245/350/277/207/347/250/213.md" +0 -0
- /package/docs/{ → theory/}/346/231/272/350/203/275/350/256/2723.4.md" +0 -0
- /package/docs/{ → theory/}/347/231/275/347/256/261/346/231/272/350/203/275/346/230/257/344/273/200/344/271/210/357/274/237.md" +0 -0
- /package/docs/{ → theory/}/347/231/275/347/256/261/346/231/272/350/203/275/347/263/273/345/210/227/302/267/347/254/254/344/272/224/347/257/207/357/274/232/350/256/251AI/347/234/237/346/255/243/350/243/205/344/270/212/350/256/260/345/277/206.md" +0 -0
|
@@ -8048,3 +8048,49 @@
|
|
|
8048
8048
|
{"t": "2026-09-11 15:12:36", "payload_chars": 586, "table_chars": 423723, "saved_chars": 424309, "saved_tokens": 318231, "hit": true}
|
|
8049
8049
|
{"t": "2026-09-11 15:12:36", "payload_chars": 402, "table_chars": 423723, "saved_chars": 424125, "saved_tokens": 318093, "hit": true}
|
|
8050
8050
|
{"t": "2026-09-11 15:12:37", "payload_chars": 751, "table_chars": 423723, "saved_chars": 424474, "saved_tokens": 318355, "hit": true}
|
|
8051
|
+
{"t": "2026-09-16 10:51:36", "payload_chars": 526, "table_chars": 423723, "saved_chars": 424249, "saved_tokens": 318186, "hit": true}
|
|
8052
|
+
{"t": "2026-09-16 10:51:36", "payload_chars": 590, "table_chars": 423723, "saved_chars": 424313, "saved_tokens": 318234, "hit": true}
|
|
8053
|
+
{"t": "2026-09-16 10:51:36", "payload_chars": 372, "table_chars": 423723, "saved_chars": 424095, "saved_tokens": 318070, "hit": true}
|
|
8054
|
+
{"t": "2026-09-16 10:51:36", "payload_chars": 546, "table_chars": 423723, "saved_chars": 424269, "saved_tokens": 318201, "hit": false}
|
|
8055
|
+
{"t": "2026-09-16 10:51:36", "payload_chars": 606, "table_chars": 423723, "saved_chars": 424329, "saved_tokens": 318246, "hit": true}
|
|
8056
|
+
{"t": "2026-09-16 10:51:37", "payload_chars": 470, "table_chars": 423723, "saved_chars": 424193, "saved_tokens": 318144, "hit": true}
|
|
8057
|
+
{"t": "2026-09-16 10:51:37", "payload_chars": 713, "table_chars": 423723, "saved_chars": 424436, "saved_tokens": 318327, "hit": true}
|
|
8058
|
+
{"t": "2026-09-16 10:51:37", "payload_chars": 522, "table_chars": 423723, "saved_chars": 424245, "saved_tokens": 318183, "hit": true}
|
|
8059
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 516, "table_chars": 423723, "saved_chars": 424239, "saved_tokens": 318178, "hit": true}
|
|
8060
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 372, "table_chars": 423723, "saved_chars": 424095, "saved_tokens": 318070, "hit": true}
|
|
8061
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 456, "table_chars": 423723, "saved_chars": 424179, "saved_tokens": 318133, "hit": true}
|
|
8062
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 685, "table_chars": 423723, "saved_chars": 424408, "saved_tokens": 318306, "hit": true}
|
|
8063
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 437, "table_chars": 423723, "saved_chars": 424160, "saved_tokens": 318120, "hit": true}
|
|
8064
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 367, "table_chars": 423723, "saved_chars": 424090, "saved_tokens": 318067, "hit": true}
|
|
8065
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 586, "table_chars": 423723, "saved_chars": 424309, "saved_tokens": 318231, "hit": true}
|
|
8066
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 402, "table_chars": 423723, "saved_chars": 424125, "saved_tokens": 318093, "hit": true}
|
|
8067
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 506, "table_chars": 423723, "saved_chars": 424229, "saved_tokens": 318171, "hit": true}
|
|
8068
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 441, "table_chars": 423723, "saved_chars": 424164, "saved_tokens": 318123, "hit": true}
|
|
8069
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 543, "table_chars": 423723, "saved_chars": 424266, "saved_tokens": 318199, "hit": true}
|
|
8070
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 332, "table_chars": 423723, "saved_chars": 424055, "saved_tokens": 318040, "hit": true}
|
|
8071
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
8072
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
8073
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
8074
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
8075
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
8076
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
8077
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
8078
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
8079
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
8080
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
8081
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
8082
|
+
{"t": "2026-09-16 10:51:39", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
8083
|
+
{"t": "2026-09-16 10:51:40", "payload_chars": 631, "table_chars": 423723, "saved_chars": 424354, "saved_tokens": 318265, "hit": true}
|
|
8084
|
+
{"t": "2026-09-16 10:51:40", "payload_chars": 631, "table_chars": 423723, "saved_chars": 424354, "saved_tokens": 318265, "hit": true}
|
|
8085
|
+
{"t": "2026-09-16 10:51:40", "payload_chars": 631, "table_chars": 423723, "saved_chars": 424354, "saved_tokens": 318265, "hit": true}
|
|
8086
|
+
{"t": "2026-09-16 10:51:40", "payload_chars": 631, "table_chars": 423723, "saved_chars": 424354, "saved_tokens": 318265, "hit": true}
|
|
8087
|
+
{"t": "2026-09-16 10:51:40", "payload_chars": 569, "table_chars": 423723, "saved_chars": 424292, "saved_tokens": 318219, "hit": true}
|
|
8088
|
+
{"t": "2026-09-16 10:51:41", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
8089
|
+
{"t": "2026-09-16 10:51:41", "payload_chars": 1048, "table_chars": 423723, "saved_chars": 424771, "saved_tokens": 318577, "hit": true}
|
|
8090
|
+
{"t": "2026-09-16 10:51:41", "payload_chars": 1322, "table_chars": 423723, "saved_chars": 425045, "saved_tokens": 318783, "hit": true}
|
|
8091
|
+
{"t": "2026-09-16 10:51:41", "payload_chars": 372, "table_chars": 423723, "saved_chars": 424095, "saved_tokens": 318070, "hit": true}
|
|
8092
|
+
{"t": "2026-09-16 10:51:41", "payload_chars": 484, "table_chars": 423723, "saved_chars": 424207, "saved_tokens": 318154, "hit": true}
|
|
8093
|
+
{"t": "2026-09-16 10:51:41", "payload_chars": 637, "table_chars": 423723, "saved_chars": 424360, "saved_tokens": 318270, "hit": true}
|
|
8094
|
+
{"t": "2026-09-16 10:51:41", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
8095
|
+
{"t": "2026-09-16 10:51:41", "payload_chars": 590, "table_chars": 423723, "saved_chars": 424313, "saved_tokens": 318234, "hit": true}
|
|
8096
|
+
{"t": "2026-09-16 10:51:41", "payload_chars": 1181, "table_chars": 423723, "saved_chars": 424904, "saved_tokens": 318678, "hit": true}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"初中数学知识点内容(按骨架填充)→小学数学知识点内容(按骨架填充)→高中数学知识点内容(按骨架填充)": 2,
|
|
6
6
|
"单卡:初中数学知识点内容(按骨架填充)": 2,
|
|
7
7
|
"单卡:小学数学知识点内容(按骨架填充)": 2,
|
|
8
|
-
"单卡:高中数学知识点内容(按骨架填充)":
|
|
8
|
+
"单卡:高中数学知识点内容(按骨架填充)": 136,
|
|
9
9
|
"小学科学→数学→初中物理": 9,
|
|
10
10
|
"单卡:小学科学": 9,
|
|
11
11
|
"单卡:数学": 9,
|
|
@@ -14,15 +14,15 @@
|
|
|
14
14
|
"单卡:数学物理方法": 9,
|
|
15
15
|
"单卡:理论力学": 9,
|
|
16
16
|
"单卡:物理学·综合": 9,
|
|
17
|
-
"单卡:打喷嚏(健康与身体·通识知识卡)":
|
|
18
|
-
"单卡:质数(数学·通识知识卡)":
|
|
19
|
-
"常见家电的功率(物理学·通识知识卡)→日食与月食的成因(天文学·通识知识卡)":
|
|
20
|
-
"单卡:常见家电的功率(物理学·通识知识卡)":
|
|
21
|
-
"单卡:日食与月食的成因(天文学·通识知识卡)":
|
|
22
|
-
"数学分析知识点内容(按骨架填充)→高中数学知识点内容(按骨架填充)→测度论与物理离散性":
|
|
23
|
-
"单卡:数学分析知识点内容(按骨架填充)":
|
|
24
|
-
"单卡:测度论与物理离散性":
|
|
25
|
-
"高中数学知识点内容(按骨架填充)→数学分析知识点内容(按骨架填充)→测度论与物理离散性":
|
|
17
|
+
"单卡:打喷嚏(健康与身体·通识知识卡)": 134,
|
|
18
|
+
"单卡:质数(数学·通识知识卡)": 134,
|
|
19
|
+
"常见家电的功率(物理学·通识知识卡)→日食与月食的成因(天文学·通识知识卡)": 134,
|
|
20
|
+
"单卡:常见家电的功率(物理学·通识知识卡)": 134,
|
|
21
|
+
"单卡:日食与月食的成因(天文学·通识知识卡)": 134,
|
|
22
|
+
"数学分析知识点内容(按骨架填充)→高中数学知识点内容(按骨架填充)→测度论与物理离散性": 62,
|
|
23
|
+
"单卡:数学分析知识点内容(按骨架填充)": 134,
|
|
24
|
+
"单卡:测度论与物理离散性": 134,
|
|
25
|
+
"高中数学知识点内容(按骨架填充)→数学分析知识点内容(按骨架填充)→测度论与物理离散性": 72,
|
|
26
26
|
"协议-知识飞轮→白箱测试定义→价值理论与AI对齐": 1,
|
|
27
27
|
"单卡:协议-知识飞轮": 1,
|
|
28
28
|
"单卡:白箱测试定义": 1,
|
|
Binary file
|
|
File without changes
|
package/md_cg/writelimit.py
CHANGED
|
@@ -32,6 +32,7 @@ import os
|
|
|
32
32
|
import re
|
|
33
33
|
import time
|
|
34
34
|
|
|
35
|
+
from . import lifecycle
|
|
35
36
|
from .fsutil import FileLock, atomic_write
|
|
36
37
|
|
|
37
38
|
STATE_FILE = "_writelimit.json"
|
|
@@ -300,8 +301,17 @@ def tidy_contextual(cg, apply=False, min_group=3, actor="sustain_tidy",
|
|
|
300
301
|
return out
|
|
301
302
|
|
|
302
303
|
|
|
303
|
-
def _demote(cg, e: dict) -> None:
|
|
304
|
-
"""成员降权:tags += tidy:converged,importance×0.5(下限 0.1)。
|
|
304
|
+
def _demote(cg, e: dict, actor: str = "sustain_tidy") -> None:
|
|
305
|
+
"""成员降权:tags += tidy:converged,importance×0.5(下限 0.1)。
|
|
306
|
+
|
|
307
|
+
② 显式状态机收口(2026-09-16):降权动作同时把生命周期状态推进到
|
|
308
|
+
`converged`——`tidy:converged` **tag 保留为兼容别名**(既有测试与历史数据都
|
|
309
|
+
在读它),但「这节点已定型」从此由 `lifecycle_state` 这一显式字段承载,
|
|
310
|
+
迁移合法性由 `lifecycle.stamp` 单点裁决。状态与 tags/importance 走**同一次
|
|
311
|
+
写盘**(不额外多写一遍文件)。裁决失败(受保护成员,正常路径已被
|
|
312
|
+
`tidy_contextual` 跳过)**不阻断**降权本身:tags/importance 已生效,只是不改
|
|
313
|
+
状态——不假装成功。
|
|
314
|
+
"""
|
|
305
315
|
fm = dict(e.get("frontmatter") or {})
|
|
306
316
|
tags = list(fm.get("tags") or [])
|
|
307
317
|
if "tidy:converged" not in tags:
|
|
@@ -309,11 +319,17 @@ def _demote(cg, e: dict) -> None:
|
|
|
309
319
|
fm["tags"] = tags
|
|
310
320
|
fm["importance"] = round(max(0.1, float(fm.get("importance", 0.5)
|
|
311
321
|
or 0) * 0.5), 3)
|
|
322
|
+
_ok, _code, _why = lifecycle.stamp(
|
|
323
|
+
fm, "converged", reason="tidy 同构聚合已定型", actor=actor)
|
|
324
|
+
if _ok:
|
|
325
|
+
# 幂等迁移(已是 converged)不会写字段 → 补成显式,索引口径才一致
|
|
326
|
+
fm.setdefault(lifecycle.STATE_FIELD, "converged")
|
|
312
327
|
cg._write_node(e["id"], os.path.join(cg.root, e["path"]), fm,
|
|
313
328
|
e.get("content") or "")
|
|
314
329
|
entry = cg.index["nodes"].get(e["id"])
|
|
315
330
|
if entry is not None:
|
|
316
|
-
entry.update({"tags": tags, "importance": fm["importance"]
|
|
331
|
+
entry.update({"tags": tags, "importance": fm["importance"],
|
|
332
|
+
lifecycle.STATE_FIELD: fm.get(lifecycle.STATE_FIELD)})
|
|
317
333
|
cg._dirty[e["id"]] = entry
|
|
318
334
|
|
|
319
335
|
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""写入路径拦截器链(Pi 钩子化机制移植,交接文档 §3⑥「闸门即扩展」)。
|
|
3
|
+
|
|
4
|
+
pi 机制(packages/coding-agent/docs/extensions.md):全生命周期事件总线,
|
|
5
|
+
闸门/路径保护/审批全是以扩展形态叠加的,核心没有硬编码策略。灵枢对应:
|
|
6
|
+
write 的六道闸(audit 校验 / consistency 冲突 / review 审核 / gated 主动遗忘
|
|
7
|
+
/ writelimit 限流 / 权限)的次序与启停原先硬编码在 mcp_server._cg_dispatch
|
|
8
|
+
的 if/else 流程里,加一道闸要改核心文件。本模块把它重构为显式拦截器链:
|
|
9
|
+
|
|
10
|
+
- **before 链**:拦截器按注册序执行。返回 None = 放行(链继续);
|
|
11
|
+
返回 dict = 终态响应(短路——不落盘,或该拦截器已代为落盘/入队)。
|
|
12
|
+
- **after 链**:落盘成功后依次执行(观测者;返回值忽略;异常不吞——
|
|
13
|
+
写入已成功,钩子故障必须暴露而非静默)。
|
|
14
|
+
- **ctx 为可变 dict**:a=原始入参 / cg=实例 / nid=节点 id / verdict=校验闸
|
|
15
|
+
裁决 / cvd=冲突闸裁决。拦截器改写 ctx["a"]["content"] 等字段即实现
|
|
16
|
+
REWRITE(改写后传给后续链与执行器)。
|
|
17
|
+
- **链尾执行器(_executor)是常驻环节**,不在注册表中、不提供卸载 API;
|
|
18
|
+
它调 cg.add 落盘,而角色/层权限校验(require_layer_write)在
|
|
19
|
+
MdCGSecure.add **库层内部**——是落盘必经之路,结构上不可被任何拦截器
|
|
20
|
+
绕过(反面清单:不学 pi 的全权信任,信任必须结构强制)。
|
|
21
|
+
|
|
22
|
+
默认链(install_default_gates,与重构前 _cg_dispatch write 分支行为逐字
|
|
23
|
+
节一致):audit → consistency → gated → _executor。
|
|
24
|
+
|
|
25
|
+
验收口径(交接文档 §3⑥):全部既有写入测试零改动通过;新增/移除一个
|
|
26
|
+
拦截器不改核心文件(register_before / unregister_before 即插即拔)。
|
|
27
|
+
|
|
28
|
+
**③ 两段式提交(2026-09-16 叠加)**:链尾执行器与 gated 闸(两条**真实落盘**
|
|
29
|
+
路径)各自在执行落盘前调 `twophase.begin` 落 intent、落盘后调 `twophase.commit`
|
|
30
|
+
记 outcome;崩溃在两者之间时由 `twophase.reconcile` 据正文指纹补账/标记。
|
|
31
|
+
边界(如实):`_gate_audit` 的 REJECT(写负记忆)与各闸的 propose(**未落盘**,
|
|
32
|
+
仅入审核队列)不在两段式覆盖面内——前者是短小负记录、后者本就没有落盘动作。
|
|
33
|
+
|
|
34
|
+
**④ 写提交边界(2026-09-16 叠加)**:`execute` 的两条出口(before 链短路 /
|
|
35
|
+
链尾执行器 + after 链之后)统一调 `_commit_visibility`——把内存脏索引
|
|
36
|
+
`flush()` 到分片日志,使本次写入对**其他进程**立即可见。这是第16条「写入后
|
|
37
|
+
读回确认」的跨进程前置条件(server 级 `autoflush=1` 是同一问题的兜底,
|
|
38
|
+
覆盖不经本链的写入路径)。根因取证见 `_commit_visibility` 文档串。
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
import time
|
|
42
|
+
|
|
43
|
+
from . import twophase
|
|
44
|
+
|
|
45
|
+
__all__ = ["WritePipeline", "default_pipeline"]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _commit_visibility(cg, out):
|
|
49
|
+
"""写提交边界(2026-09-16):把内存脏索引落分片日志,使本次写入对其他进程立即可见。
|
|
50
|
+
|
|
51
|
+
根因(第4条取证):写入只经 `_stage` 入内存 + `_dirty`,须达 `autoflush`
|
|
52
|
+
(默认 64)或 `close()` 才 `flush()` 落 `_index_log/`;MCP server 常驻、
|
|
53
|
+
不 close,故单条写入在阈值前**对其他进程不可见**——`_load_index` 读的是
|
|
54
|
+
「快照 `_index.json` + 分片日志重放」,而快照只在 compact/rebuild 时重写。
|
|
55
|
+
症状即第16条「写入后读回确认」在跨进程读面上系统性误报(写入返回
|
|
56
|
+
committed=true,读回却检索不到)。
|
|
57
|
+
|
|
58
|
+
边界(如实):无脏数据时 `flush()` 是 no-op,成本只在「确有落盘」时产生;
|
|
59
|
+
失败**不抛异常**——写入内容已落盘,抛出去会让调用方误判「写入失败」而
|
|
60
|
+
重试(两段式账本已记 committed,重试即重复写入)。改为在响应里如实标记
|
|
61
|
+
`flush_error`,不静默。
|
|
62
|
+
"""
|
|
63
|
+
try:
|
|
64
|
+
cg.flush()
|
|
65
|
+
except Exception as exc: # noqa: BLE001 —— 索引可见性故障不得改写写入语义
|
|
66
|
+
if isinstance(out, dict):
|
|
67
|
+
out["flush_error"] = "%s: %s" % (type(exc).__name__, exc)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class WritePipeline:
|
|
71
|
+
"""写入拦截器链(实例级;default_pipeline() 提供进程级默认单例)。"""
|
|
72
|
+
|
|
73
|
+
def __init__(self):
|
|
74
|
+
self._before = [] # [(name, fn)]
|
|
75
|
+
self._after = [] # [(name, fn)]
|
|
76
|
+
|
|
77
|
+
# ---------- 注册表 ----------
|
|
78
|
+
|
|
79
|
+
def register_before(self, name, fn, position=None):
|
|
80
|
+
"""注册 before 拦截器(同名幂等替换;position=None 追加到链尾)。
|
|
81
|
+
|
|
82
|
+
fn(ctx) -> None | dict(终态响应,短路)。
|
|
83
|
+
"""
|
|
84
|
+
if not callable(fn):
|
|
85
|
+
raise TypeError(f"拦截器必须可调用:{name!r}")
|
|
86
|
+
self.unregister_before(name)
|
|
87
|
+
item = (str(name), fn)
|
|
88
|
+
if position is None:
|
|
89
|
+
self._before.append(item)
|
|
90
|
+
else:
|
|
91
|
+
self._before.insert(max(0, int(position)), item)
|
|
92
|
+
|
|
93
|
+
def unregister_before(self, name):
|
|
94
|
+
n0 = len(self._before)
|
|
95
|
+
self._before = [x for x in self._before if x[0] != str(name)]
|
|
96
|
+
return len(self._before) != n0
|
|
97
|
+
|
|
98
|
+
def register_after(self, name, fn):
|
|
99
|
+
"""注册 after 观察者:fn(ctx, out),落盘成功后按序调用。"""
|
|
100
|
+
if not callable(fn):
|
|
101
|
+
raise TypeError(f"after 钩子必须可调用:{name!r}")
|
|
102
|
+
self.unregister_after(name)
|
|
103
|
+
self._after.append((str(name), fn))
|
|
104
|
+
|
|
105
|
+
def unregister_after(self, name):
|
|
106
|
+
n0 = len(self._after)
|
|
107
|
+
self._after = [x for x in self._after if x[0] != str(name)]
|
|
108
|
+
return len(self._after) != n0
|
|
109
|
+
|
|
110
|
+
def names(self):
|
|
111
|
+
return {"before": [n for n, _f in self._before],
|
|
112
|
+
"after": [n for n, _f in self._after]}
|
|
113
|
+
|
|
114
|
+
# ---------- 执行 ----------
|
|
115
|
+
|
|
116
|
+
def execute(self, cg, a):
|
|
117
|
+
"""写入请求入口:跑 before 链 → 链尾执行器 → after 链。
|
|
118
|
+
|
|
119
|
+
before 链任一非 None 返回值即终态响应(与重构前各分支的 return
|
|
120
|
+
形态逐字节一致);链尾执行器产生落盘响应,after 链只观测不改写。
|
|
121
|
+
"""
|
|
122
|
+
a = a or {}
|
|
123
|
+
ctx = {"cg": cg, "a": a,
|
|
124
|
+
"nid": a.get("node_id")
|
|
125
|
+
or ("mem_" + str(int(time.time() * 1000))),
|
|
126
|
+
"verdict": None, "cvd": None}
|
|
127
|
+
for name, fn in self._before:
|
|
128
|
+
out = fn(ctx)
|
|
129
|
+
if out is not None:
|
|
130
|
+
ctx["halted_by"] = name
|
|
131
|
+
_commit_visibility(cg, out)
|
|
132
|
+
return out
|
|
133
|
+
# ③ 两段式:闸门**全部放行**(确认要写)→ 先落意图,再执行落盘,
|
|
134
|
+
# 最后记结果。崩溃若发生在两者之间,`reconcile` 能据正文指纹回答
|
|
135
|
+
# 「那笔写入到底落盘了没有」,而不是留下一条无痕的静默记忆。
|
|
136
|
+
tok = twophase.begin(cg, ctx["nid"], a.get("content", ""),
|
|
137
|
+
layer=a.get("layer") or "knowledge",
|
|
138
|
+
actor="writepipe:executor")
|
|
139
|
+
try:
|
|
140
|
+
out = _executor(ctx)
|
|
141
|
+
except BaseException as exc:
|
|
142
|
+
# 执行器抛异常(权限拒绝/校验失败)= 写入未完成 → 账本记 error,
|
|
143
|
+
# 异常照抛不吞(两段式只记账,不改写既有错误语义)。
|
|
144
|
+
twophase.commit(cg, tok, status=twophase.STATUS_ERROR,
|
|
145
|
+
reason=type(exc).__name__)
|
|
146
|
+
raise
|
|
147
|
+
ctx["out"] = out
|
|
148
|
+
twophase.commit(cg, tok, status=twophase.STATUS_COMMITTED,
|
|
149
|
+
reason="executor_ok")
|
|
150
|
+
for _name, fn in self._after:
|
|
151
|
+
fn(ctx, out)
|
|
152
|
+
_commit_visibility(cg, out)
|
|
153
|
+
return out
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# --------------------------------------------------------------------------
|
|
157
|
+
# 默认链(原 mcp_server._cg_dispatch op=="write" 分支,行为逐字节搬运)
|
|
158
|
+
# --------------------------------------------------------------------------
|
|
159
|
+
|
|
160
|
+
def _gate_audit(ctx):
|
|
161
|
+
"""校验闸:audit.audit 四态。ACCEPT 放行;REJECT 负记忆;其余入审核队列。"""
|
|
162
|
+
a = ctx["a"]
|
|
163
|
+
cg = ctx["cg"]
|
|
164
|
+
from . import audit
|
|
165
|
+
verdict = audit.audit(
|
|
166
|
+
(a.get("content_kind") or "").strip(),
|
|
167
|
+
{"content": a.get("content", ""), "action": a.get("action"),
|
|
168
|
+
"sensitivity": a.get("sensitivity"),
|
|
169
|
+
"topic": a.get("query") or a.get("intent")},
|
|
170
|
+
{"cg": cg, "principal": getattr(cg, "principal", None)})
|
|
171
|
+
ctx["verdict"] = verdict
|
|
172
|
+
st = verdict["state"]
|
|
173
|
+
if st == audit.ACCEPT:
|
|
174
|
+
return None
|
|
175
|
+
if st == audit.REJECT:
|
|
176
|
+
rid = cg.add_rejected((a.get("content") or "")[:200], verdict["evidence"],
|
|
177
|
+
verification_basis=verdict.get("basis") or "test",
|
|
178
|
+
tags=a.get("tags"))
|
|
179
|
+
return {"ok": False, "id": rid, "committed": False,
|
|
180
|
+
"moved_to": "rejected", "verdict": verdict,
|
|
181
|
+
"hint": "这是审核闸门的正常行为:内容未过内容政策审核(REJECT),"
|
|
182
|
+
"已记入负记忆——不是工具故障,重试同样结果;"
|
|
183
|
+
"拒绝依据见 verdict.evidence"}
|
|
184
|
+
from .mcp_server import _proposal_extras
|
|
185
|
+
pr = cg.propose(ctx["nid"], a.get("content", ""), info=True,
|
|
186
|
+
layer=a.get("layer") or "knowledge",
|
|
187
|
+
tags=a.get("tags"), condition_space=a.get("condition_space"),
|
|
188
|
+
**_proposal_extras(a, verdict))
|
|
189
|
+
out = {"ok": True, "id": ctx["nid"], "pid": pr["pid"], "committed": False,
|
|
190
|
+
"moved_to": "review_queue", "verdict": verdict,
|
|
191
|
+
"hint": "这是校验闸门的正常行为(verdict=%s):内容未达 ACCEPT,"
|
|
192
|
+
"已入审核队列——不需要重试;落盘须由设计者权限(can_admin)"
|
|
193
|
+
"对提案 pid 裁决(agent 端无裁决权是设计),转告使用者:"
|
|
194
|
+
"python scripts/review_cli.py list 后 accept/reject,"
|
|
195
|
+
"或 cg(op=review, pid=<pid>, decision=accept|reject|"
|
|
196
|
+
"edit|merge, reason=<理由>)" % verdict.get("state")}
|
|
197
|
+
if pr.get("dedup"):
|
|
198
|
+
out["dedup"] = True
|
|
199
|
+
out["dup_of"] = pr["pid"]
|
|
200
|
+
out["dup_status"] = pr.get("dup_status")
|
|
201
|
+
out["hint"] = (
|
|
202
|
+
"同内容提案已存在(pid=%s,状态=%s,幂等去重),"
|
|
203
|
+
"本次未重复入队——无需重试;落盘须由设计者权限(can_admin)"
|
|
204
|
+
"对该 pid 裁决:python scripts/review_cli.py list 后 accept/reject,"
|
|
205
|
+
"或 cg(op=review, pid=<pid>, decision=accept|reject|edit|merge, "
|
|
206
|
+
"reason=<理由>)" % (pr["pid"], pr.get("dup_status") or "pending"))
|
|
207
|
+
return out
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _gate_consistency(ctx):
|
|
211
|
+
"""冲突闸:节点间自动冲突检测(三级决策)。
|
|
212
|
+
|
|
213
|
+
不通过时按 on_conflict:reject=直接拒绝;defer=转入审核队列。
|
|
214
|
+
"""
|
|
215
|
+
a = ctx["a"]
|
|
216
|
+
cg = ctx["cg"]
|
|
217
|
+
if not bool(a.get("consistency", True)):
|
|
218
|
+
return None
|
|
219
|
+
from .mcp_server import _proposal_extras
|
|
220
|
+
oc = (a.get("on_conflict") or "defer").strip().lower()
|
|
221
|
+
cvd = cg.check_consistency(
|
|
222
|
+
a.get("content", ""),
|
|
223
|
+
layer=a.get("layer") or ("contextual" if a.get("gated")
|
|
224
|
+
else "knowledge"),
|
|
225
|
+
condition_space=a.get("condition_space"),
|
|
226
|
+
non_applicable_conditions=a.get("non_applicable_conditions"),
|
|
227
|
+
tags=a.get("tags"), exclude=ctx["nid"], auto_flywheel=True)
|
|
228
|
+
ctx["cvd"] = cvd
|
|
229
|
+
v = cvd.get("verdict")
|
|
230
|
+
blocked = ((v == "REJECT" and oc == "reject")
|
|
231
|
+
or (v in ("REJECT", "BLINDSPOT") and oc == "defer"))
|
|
232
|
+
if not blocked:
|
|
233
|
+
return None
|
|
234
|
+
if oc == "reject":
|
|
235
|
+
return {"ok": False, "id": ctx["nid"], "committed": False,
|
|
236
|
+
"moved_to": "conflict_rejected",
|
|
237
|
+
"consistency": cvd, "verdict": ctx["verdict"]}
|
|
238
|
+
pr = cg.propose(ctx["nid"], a.get("content", ""), info=True,
|
|
239
|
+
layer=a.get("layer") or "knowledge",
|
|
240
|
+
tags=a.get("tags"),
|
|
241
|
+
condition_space=a.get("condition_space"),
|
|
242
|
+
**_proposal_extras(a, ctx["verdict"]))
|
|
243
|
+
out = {"ok": False, "id": ctx["nid"], "pid": pr["pid"],
|
|
244
|
+
"committed": False,
|
|
245
|
+
"moved_to": "review_queue", "consistency": cvd,
|
|
246
|
+
"verdict": ctx["verdict"],
|
|
247
|
+
"hint": "这是冲突闸门的正常行为:本次写入与既有条件/纪律冲突"
|
|
248
|
+
"(on_conflict=defer),已转入审核队列待裁决——"
|
|
249
|
+
"不是工具故障,重试同样结果;"
|
|
250
|
+
"落盘须由设计者权限(can_admin)裁决,转告使用者:"
|
|
251
|
+
"python scripts/review_cli.py list 后 accept/reject,"
|
|
252
|
+
"或 cg(op=review, pid=<pid>, decision=accept|reject|"
|
|
253
|
+
"edit|merge, reason=<理由>)"}
|
|
254
|
+
if pr.get("dedup"):
|
|
255
|
+
out["dedup"] = True
|
|
256
|
+
out["dup_of"] = pr["pid"]
|
|
257
|
+
out["hint"] = (
|
|
258
|
+
"同内容提案已存在于审核队列(pid=%s,幂等去重),"
|
|
259
|
+
"本次未重复入队——无需重试;"
|
|
260
|
+
"落盘须由设计者权限(can_admin)对该 pid 裁决:"
|
|
261
|
+
"python scripts/review_cli.py list 后 accept/reject,"
|
|
262
|
+
"或 cg(op=review, pid=<pid>, decision=accept|reject|"
|
|
263
|
+
"edit|merge, reason=<理由>)" % pr["pid"])
|
|
264
|
+
return out
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _gate_gated(ctx):
|
|
268
|
+
"""主动遗忘闸(gated=true 时启用):writelimit 限流 + forgetting 三问四态。
|
|
269
|
+
|
|
270
|
+
本闸是「替代执行路径」:命中即由 remember_gated 代为落盘/合并/丢弃并
|
|
271
|
+
返回终态;未启用(gated 假值)放行给链尾执行器。
|
|
272
|
+
"""
|
|
273
|
+
a = ctx["a"]
|
|
274
|
+
cg = ctx["cg"]
|
|
275
|
+
if not a.get("gated"):
|
|
276
|
+
return None
|
|
277
|
+
hint = a.get("importance_hint")
|
|
278
|
+
if hint is None and a.get("importance") is not None:
|
|
279
|
+
hint = float(a["importance"])
|
|
280
|
+
# ③ 两段式:本闸是**替代执行路径**(自己落盘),意图必须由它先记——
|
|
281
|
+
# 若等 execute 在链后统一记,intent 会晚于本闸内部的写盘,「先行持久化」
|
|
282
|
+
# 就不成立了。落盘前的窗口因此仍然被账本覆盖。
|
|
283
|
+
tok = twophase.begin(cg, ctx["nid"], a.get("content", ""),
|
|
284
|
+
layer=a.get("layer") or "contextual",
|
|
285
|
+
actor="writepipe:gated")
|
|
286
|
+
res = cg.remember_gated(
|
|
287
|
+
ctx["nid"], a.get("content", ""), layer=a.get("layer") or "contextual",
|
|
288
|
+
role=a.get("role"), tags=a.get("tags"),
|
|
289
|
+
condition_space=a.get("condition_space"),
|
|
290
|
+
verification_basis=(a.get("verification_basis")
|
|
291
|
+
or (ctx.get("verdict") or {}).get("basis")),
|
|
292
|
+
non_applicable_conditions=a.get("non_applicable_conditions"),
|
|
293
|
+
importance_hint=hint, override=bool(a.get("override")),
|
|
294
|
+
consistency=False,
|
|
295
|
+
derived_from=_split_ids(a.get("derived_from")),
|
|
296
|
+
relation=a.get("relation"))
|
|
297
|
+
v = res.get("verdict")
|
|
298
|
+
committed = v == "ACCEPT"
|
|
299
|
+
# 结局如实记:ACCEPT=落盘完成;MERGE=内容并入既有节点(不再以本次内容成
|
|
300
|
+
# 文,指纹对账不适用,故直接记 committed 并注明去向);DROP/DEFER=未落盘。
|
|
301
|
+
if committed:
|
|
302
|
+
twophase.commit(cg, tok, status=twophase.STATUS_COMMITTED,
|
|
303
|
+
reason="gated_accept")
|
|
304
|
+
elif v == "MERGE":
|
|
305
|
+
twophase.commit(cg, tok, status=twophase.STATUS_COMMITTED,
|
|
306
|
+
reason="merged_into:%s" % res.get("merged_into"))
|
|
307
|
+
else:
|
|
308
|
+
twophase.commit(cg, tok, status=twophase.STATUS_ABORTED,
|
|
309
|
+
reason="gated_%s" % str(v).lower())
|
|
310
|
+
out = {"ok": committed, "id": ctx["nid"], "committed": committed,
|
|
311
|
+
"gate": res, "verdict": ctx["verdict"]}
|
|
312
|
+
if ctx.get("cvd") is not None:
|
|
313
|
+
out["consistency"] = ctx["cvd"]
|
|
314
|
+
if v == "MERGE":
|
|
315
|
+
out["moved_to"] = "merged_into:" + str(res.get("merged_into"))
|
|
316
|
+
elif v in ("DROP", "DEFER"):
|
|
317
|
+
out["moved_to"] = v.lower()
|
|
318
|
+
return out
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _executor(ctx):
|
|
322
|
+
"""链尾执行器(常驻不可卸载):cg.add 直写落盘。
|
|
323
|
+
|
|
324
|
+
角色/层权限校验(require_layer_write)在 MdCGSecure.add 库层内部,
|
|
325
|
+
结构上不可被拦截器绕过——拦截器只能裁决「写不写」,改不了「谁能写」。
|
|
326
|
+
"""
|
|
327
|
+
a = ctx["a"]
|
|
328
|
+
cg = ctx["cg"]
|
|
329
|
+
cg.add(ctx["nid"], a.get("content", ""),
|
|
330
|
+
layer=a.get("layer") or "knowledge",
|
|
331
|
+
tags=a.get("tags"), condition_space=a.get("condition_space"),
|
|
332
|
+
importance=float(a.get("importance", 0.5)),
|
|
333
|
+
verification_basis=a.get("verification_basis")
|
|
334
|
+
or (ctx.get("verdict") or {}).get("basis"),
|
|
335
|
+
non_applicable_conditions=a.get("non_applicable_conditions"),
|
|
336
|
+
override=bool(a.get("override")), consistency=False,
|
|
337
|
+
derived_from=_split_ids(a.get("derived_from")),
|
|
338
|
+
relation=a.get("relation"))
|
|
339
|
+
out = {"ok": True, "id": ctx["nid"], "committed": True,
|
|
340
|
+
"verdict": ctx["verdict"]}
|
|
341
|
+
if ctx.get("cvd") is not None:
|
|
342
|
+
out["consistency"] = ctx["cvd"]
|
|
343
|
+
return out
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _split_ids(value):
|
|
347
|
+
# 与 mcp_server._split_ids 同源(延迟导入,单一真源)
|
|
348
|
+
from .mcp_server import _split_ids as _f
|
|
349
|
+
return _f(value)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
# --------------------------------------------------------------------------
|
|
353
|
+
# 进程级默认链(单例;mcp_server op=write 一行分发到此)
|
|
354
|
+
# --------------------------------------------------------------------------
|
|
355
|
+
|
|
356
|
+
_DEFAULT = None
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def install_default_gates(pipe):
|
|
360
|
+
"""把默认六道闸以拦截器形态注册(幂等:同名替换,可重复调用)。"""
|
|
361
|
+
pipe.register_before("audit", _gate_audit)
|
|
362
|
+
pipe.register_before("consistency", _gate_consistency)
|
|
363
|
+
pipe.register_before("gated", _gate_gated)
|
|
364
|
+
return pipe
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def default_pipeline():
|
|
368
|
+
"""进程级默认写入链(单例)。自定义闸门 register_before 即插即拔。"""
|
|
369
|
+
global _DEFAULT
|
|
370
|
+
if _DEFAULT is None:
|
|
371
|
+
_DEFAULT = install_default_gates(WritePipeline())
|
|
372
|
+
return _DEFAULT
|
package/package.json
CHANGED
package/src/hooks.ts
CHANGED
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
* 模型请求组装 system prompt 时自动注入灵枢最近记忆
|
|
22
22
|
* (`stg(op=timeline)`,最近记忆节点时间线),让记忆"自动可用"而不只依赖
|
|
23
23
|
* Agent 主动调用 recall/think 工具。失败静默(不影响请求)。
|
|
24
|
+
*
|
|
25
|
+
* ⚠️ 注入文本**必经** escapePromptBraces(src/lib/prompt_safety.ts,issue #16):
|
|
26
|
+
* 宿主对 context 文本做严格 `{{variable}}` 插值,裸 `{{` 会让每轮 assemble 抛错
|
|
27
|
+
* → 会话永久不可用(记忆永久在库,非偶发)。记忆真源不动,只在**注入副本**上
|
|
28
|
+
* 打断 `{{`——新增任何 push context/section 的代码,同样必须过这道转义。
|
|
24
29
|
*/
|
|
25
30
|
|
|
26
31
|
import '@deepseek-ai/dsh-session'
|
|
@@ -29,6 +34,7 @@ import type { Context } from '@deepseek-ai/cordis'
|
|
|
29
34
|
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
|
30
35
|
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
|
|
31
36
|
import type { MdcgClient } from './lib/mdcg_client.js'
|
|
37
|
+
import { escapePromptBraces } from './lib/prompt_safety.js'
|
|
32
38
|
|
|
33
39
|
/** 自动记忆开关。 */
|
|
34
40
|
export interface MemoryHooksOptions {
|
|
@@ -207,9 +213,12 @@ export function installMemoryHooks(ctx: Context, mdcg: MdcgClient | null, opts:
|
|
|
207
213
|
if (shouldPushRecall(text, lastRecallText, skippedSincePush)) {
|
|
208
214
|
lastRecallText = text
|
|
209
215
|
skippedSincePush = 0
|
|
216
|
+
// 注入边界转义(issue #16):宿主 system-prompt 对 context 文本做严格
|
|
217
|
+
// `{{variable}}` 插值,裸 `{{` 会 throw → 该轮请求整体失败。记忆原文
|
|
218
|
+
// (含用户命令里的 `{{.X}}`)必须保真落库,故只在注入副本上打断 `{{`。
|
|
210
219
|
assembly.contexts.push({
|
|
211
220
|
name: 'lingshu:auto-recall',
|
|
212
|
-
text: `【灵枢最近记忆】\n${text.slice(0, RECALL_MAX_CHARS)}
|
|
221
|
+
text: escapePromptBraces(`【灵枢最近记忆】\n${text.slice(0, RECALL_MAX_CHARS)}`),
|
|
213
222
|
})
|
|
214
223
|
} else {
|
|
215
224
|
skippedSincePush += 1
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* prompt_safety.ts · 注入边界文本安全(宿主严格模板的转义)
|
|
3
|
+
*
|
|
4
|
+
* ── 根因(issue #16,2026-09-16)────────────────────────────────────────
|
|
5
|
+
* dsh 宿主 `@deepseek-ai/dsh-system-prompt` 的 `interpolate()` 会对**每个
|
|
6
|
+
* section / context** 文本做严格 `{{variable}}` 插值,且**四类一律 throw**:
|
|
7
|
+
* ① `{{` 之后能找到 `}}` 但不成简单组(GROUP_AT = /^\{\{([^{}]*)\}\}/ 不匹配)
|
|
8
|
+
* ② 组成立但名不匹配 /^[a-z][a-z0-9_]*$/(如 `{{.Architecture}}`)
|
|
9
|
+
* ③ 名合法但未注册(如 `{{name}}`)
|
|
10
|
+
* ④ 已注册但值为 undefined
|
|
11
|
+
* `renderContextSections()` 逐 context 调用它,throw 直接冒泡到
|
|
12
|
+
* `system-prompt/assemble` → **该轮请求整体失败**。
|
|
13
|
+
*
|
|
14
|
+
* ── 伤害路径 ───────────────────────────────────────────────────────────
|
|
15
|
+
* 自动记忆把用户命令原样沉淀(如 `docker inspect --format '{{.Architecture}}'`)
|
|
16
|
+
* → auto-recall 把含裸 `{{` 的预览推入 `assembly.contexts`
|
|
17
|
+
* → 宿主每轮 assemble 必抛 → **会话永久不可用**(记忆永久在库,非偶发故障)。
|
|
18
|
+
*
|
|
19
|
+
* ── 处置:为何只在注入边界转义,不在写入侧 ────────────────────────────
|
|
20
|
+
* ① 记忆真源必须保真:`{{.Architecture}}` 是用户命令原文,写入侧转义会
|
|
21
|
+
* 不可逆失真,且 cg(op=read) / 工具面 / 检索都依赖原文(= 污染真源);
|
|
22
|
+
* ② 伤害面就是「注入到 prompt 的文本」,在注入边界处理即**最小充分**;
|
|
23
|
+
* ③ 与写入侧 desensitize 不冲突:脱敏是**隐私**要求(不许落盘),
|
|
24
|
+
* 转义是**渲染安全**要求(落盘保真、渲染时规避)——层次不同。
|
|
25
|
+
*
|
|
26
|
+
* ── 转义策略:确保文本中不出现连续两个 `{` ────────────────────────────
|
|
27
|
+
* 依据(宿主源码 `lib/index.js:109-116`):`interpolate` 以
|
|
28
|
+
* `text.indexOf("{{")` 为**唯一扫描锚点**;破坏 `{{` 序列后循环不进入,
|
|
29
|
+
* 文本走 `text.slice(last)` 原样返回。
|
|
30
|
+
* · `}}` 无需处理——它只在 `{{` 成组时参与解析,锚点已破即无意义;
|
|
31
|
+
* · 逐对打断(而非 `split('{{').join(...)`):后者对 `{{{a}}}` 会产出
|
|
32
|
+
* `{ {{a}}}`——新锚点仍在(相邻 `{` 重新合成),不幂等也不安全;
|
|
33
|
+
* · 跨 context 拼接安全:`joinContextSections` 以 `"\n\n"` 分隔并带固定
|
|
34
|
+
* 前缀(`lib/index.js:85-87`,前缀以 `.` 结尾),边界不会合成新 `{{`;
|
|
35
|
+
* · 为何不用零宽字符:显示上「看起来一样」但复制会带入不可见字符
|
|
36
|
+
* (终端粘贴即出错)——诚实性优先,宁可让改写可见。
|
|
37
|
+
*
|
|
38
|
+
* 幂等:结果中不存在 `{{`,重复调用结果不变。
|
|
39
|
+
* 边界:本函数只服务**注入到宿主 prompt 的文本**;记忆真源与工具返回原文
|
|
40
|
+
* 一律不动(工具结果不进 assembly 插值面)。
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/** 宿主严格模板的扫描锚点。 */
|
|
44
|
+
const TEMPLATE_OPEN = '{{'
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 把文本中每一对相邻的 `{` 打断(插入半角空格),使其不再包含 `{{`。
|
|
48
|
+
*
|
|
49
|
+
* 不含 `{{` 的文本**原样返回**(零改写,保真)。
|
|
50
|
+
* 例:`{{.Architecture}}` → `{ {.Architecture}}`;`{{{a}}}` → `{ { {a}}}`。
|
|
51
|
+
*/
|
|
52
|
+
export function escapePromptBraces(text: string): string {
|
|
53
|
+
if (!text.includes(TEMPLATE_OPEN)) return text
|
|
54
|
+
let out = ''
|
|
55
|
+
for (let i = 0; i < text.length; i++) {
|
|
56
|
+
const ch = text.charAt(i)
|
|
57
|
+
out += ch
|
|
58
|
+
// 该 `{` 与下一个字符构成锚点 → 立即插入空格打断
|
|
59
|
+
if (ch === '{' && text.charAt(i + 1) === '{') out += ' '
|
|
60
|
+
}
|
|
61
|
+
return out
|
|
62
|
+
}
|