@theseus-whale/dsh-continuity 0.0.0-stage → 4.12.2

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 (258) hide show
  1. package/INSTALL.md +25 -0
  2. package/PANEL.md +92 -0
  3. package/README.md +286 -2
  4. package/RELEASE-NOTES.md +732 -0
  5. package/bin/bc-acceptance-full.mjs +91 -0
  6. package/bin/bc-acceptance-tight.mjs +66 -0
  7. package/bin/bc-acceptance.mjs +22 -0
  8. package/bin/bc-add-keywords.mjs +21 -0
  9. package/bin/bc-demo.mjs +47 -0
  10. package/bin/bc-expand-hint.mjs +179 -0
  11. package/bin/bc-ingest-index.mjs +35 -0
  12. package/bin/bc-ingest.mjs +31 -0
  13. package/bin/bc-label-demo.mjs +18 -0
  14. package/bin/bc-relabel.mjs +150 -0
  15. package/bin/bc-sessionize.mjs +59 -0
  16. package/bin/coref_pipeline.py +47 -0
  17. package/bin/coref_service.py +57 -0
  18. package/bin/debug_ckip.py +16 -0
  19. package/bin/debug_ner_zh.py +18 -0
  20. package/bin/doctor.mjs +275 -0
  21. package/bin/en_ner_error_audit.py +33 -0
  22. package/bin/eval-label-rules.mjs +342 -0
  23. package/bin/export-turns.py +68 -0
  24. package/bin/extract-user-paragraph.mjs +14 -0
  25. package/bin/extraction-composite-smoke.mjs +28 -0
  26. package/bin/extraction-latency.mjs +58 -0
  27. package/bin/extraction-user-smoke.mjs +28 -0
  28. package/bin/extraction_service.py +164 -0
  29. package/bin/facet-active-experiment.mjs +110 -0
  30. package/bin/facet-covid.mjs +8 -0
  31. package/bin/facet-curve.mjs +27 -0
  32. package/bin/facet-embedding-train.mjs +20 -0
  33. package/bin/facet-embedding-validate.mjs +83 -0
  34. package/bin/facet-eval-roleplay.mjs +42 -0
  35. package/bin/facet-eval.mjs +38 -0
  36. package/bin/facet-fold.mjs +31 -0
  37. package/bin/facet-load.mjs +16 -0
  38. package/bin/facet-mlp-scan.mjs +45 -0
  39. package/bin/facet-mvp.mjs +34 -0
  40. package/bin/facet-online-experiments.mjs +78 -0
  41. package/bin/facet-online-race.mjs +99 -0
  42. package/bin/facet-online-validate.mjs +51 -0
  43. package/bin/facet-panel.mjs +22 -0
  44. package/bin/facet-passage.mjs +41 -0
  45. package/bin/facet-random-validate.mjs +48 -0
  46. package/bin/facet-roleplay.mjs +33 -0
  47. package/bin/facet-scan.mjs +56 -0
  48. package/bin/facet-segment.mjs +34 -0
  49. package/bin/facet-train.mjs +19 -0
  50. package/bin/facet-validate.mjs +103 -0
  51. package/bin/facet-zeroshot.mjs +14 -0
  52. package/bin/install-templates.mjs +72 -0
  53. package/bin/memory-bench.mjs +53 -0
  54. package/bin/memory-build-summary.mjs +43 -0
  55. package/bin/memory-build-vector.mjs +31 -0
  56. package/bin/memory-build.mjs +41 -0
  57. package/bin/memory-calibration.mjs +70 -0
  58. package/bin/memory-closed-loop.mjs +119 -0
  59. package/bin/memory-dry-run.mjs +70 -0
  60. package/bin/memory-eval.mjs +104 -0
  61. package/bin/memory-event-alias-test.mjs +89 -0
  62. package/bin/memory-extraction-bench.mjs +58 -0
  63. package/bin/memory-load-gate-replay.mjs +137 -0
  64. package/bin/memory-loop-test.mjs +90 -0
  65. package/bin/memory-pre-turn-harness.mjs +63 -0
  66. package/bin/memory-query.mjs +39 -0
  67. package/bin/memory-replay-hints.mjs +84 -0
  68. package/bin/memory-scale.mjs +58 -0
  69. package/bin/memory-scenario-test.mjs +146 -0
  70. package/bin/memory-user-loop.mjs +95 -0
  71. package/bin/ner_bench_tiny_zh.py +59 -0
  72. package/bin/ner_bench_zh.py +60 -0
  73. package/bin/ner_conll_en.py +139 -0
  74. package/bin/ner_direct_en.py +123 -0
  75. package/bin/ner_direct_zh.py +100 -0
  76. package/bin/ner_pipeline.py +208 -0
  77. package/bin/noise-selfcheck.mjs +50 -0
  78. package/bin/pack-integrity.mjs +47 -0
  79. package/bin/panel-check.mjs +57 -0
  80. package/bin/plugin-integrity.mjs +74 -0
  81. package/bin/preview-live.mjs +13 -0
  82. package/bin/public-install-check.mjs +222 -0
  83. package/bin/relabel-apply.mjs +272 -0
  84. package/bin/relabel-dry-run.mjs +381 -0
  85. package/bin/relik_pipeline.py +39 -0
  86. package/bin/relik_service.py +52 -0
  87. package/bin/srl_pipeline.py +55 -0
  88. package/bin/srl_service.py +68 -0
  89. package/bin/summary-index.mjs +151 -0
  90. package/bin/summary-query.mjs +79 -0
  91. package/bin/vec-check.mjs +27 -0
  92. package/bin/zh_ner_error_audit.py +36 -0
  93. package/bin/zh_ner_slot_eval.py +38 -0
  94. package/client.js +760 -0
  95. package/cordis.patch.yml +378 -0
  96. package/docs/API-NOTES.md +1743 -0
  97. package/docs/ARCHITECTURE.md +355 -0
  98. package/docs/CACHE.md +253 -0
  99. package/docs/CONDUCT.md +30 -0
  100. package/docs/CREATOR-HANDOFF.md +102 -0
  101. package/docs/EXPERIMENT.md +136 -0
  102. package/docs/FACET-MVP-REPORT.md +473 -0
  103. package/docs/GUIDE-0.4.66.md +195 -0
  104. package/docs/GUIDE.md +127 -0
  105. package/docs/MECHANISM.md +243 -0
  106. package/docs/NEW-INSTANCE-HANDOFF.md +219 -0
  107. package/docs/README.md +37 -0
  108. package/docs/SESSION-LAYOUT.md +151 -0
  109. package/docs/history-map.md +249 -0
  110. package/docs/relabel-migration-wiring.md +366 -0
  111. package/guides/00-READ-FIRST.md +206 -0
  112. package/guides/01-INSTALL.md +207 -0
  113. package/guides/02-TABLES.md +225 -0
  114. package/guides/03-AUDIT.md +201 -0
  115. package/guides/04-REBUILD.md +306 -0
  116. package/guides/05-FIRST-RUN.md +191 -0
  117. package/guides/06-EMBEDDING.md +146 -0
  118. package/guides/07-PTC-AND-ASSETS.md +221 -0
  119. package/icon.svg +7 -0
  120. package/locale/en.json +6 -0
  121. package/locale/zh.json +6 -0
  122. package/models/README.md +13 -0
  123. package/models/Xenova/bge-small-zh-v1.5/config.json +31 -0
  124. package/models/Xenova/bge-small-zh-v1.5/onnx/model_quantized.onnx +0 -0
  125. package/models/Xenova/bge-small-zh-v1.5/tokenizer.json +21278 -0
  126. package/models/Xenova/bge-small-zh-v1.5/tokenizer_config.json +15 -0
  127. package/package.json +139 -4
  128. package/src/asset-tool.mjs +237 -0
  129. package/src/assets.mjs +244 -0
  130. package/src/backfill.mjs +74 -0
  131. package/src/backup.mjs +80 -0
  132. package/src/deadend.mjs +95 -0
  133. package/src/deleted-store.mjs +35 -0
  134. package/src/engine.mjs +3831 -0
  135. package/src/facets/classifier.mjs +146 -0
  136. package/src/facets/embedding-service.mjs +53 -0
  137. package/src/facets/embedding-worker.mjs +27 -0
  138. package/src/facets/embedding.mjs +163 -0
  139. package/src/facets/extract.mjs +80 -0
  140. package/src/facets/fold-plan.mjs +84 -0
  141. package/src/facets/hybrid.mjs +29 -0
  142. package/src/facets/index.mjs +113 -0
  143. package/src/facets/linear-probe.mjs +43 -0
  144. package/src/facets/load.mjs +37 -0
  145. package/src/facets/mlp-probe.mjs +54 -0
  146. package/src/facets/mlp-service.mjs +53 -0
  147. package/src/facets/mlp-worker.mjs +26 -0
  148. package/src/facets/online-knn.mjs +58 -0
  149. package/src/facets/online-models.mjs +227 -0
  150. package/src/facets/panel-state.mjs +23 -0
  151. package/src/facets/panel.mjs +73 -0
  152. package/src/facets/random.mjs +26 -0
  153. package/src/facets/scanner.mjs +35 -0
  154. package/src/facets/schema.mjs +75 -0
  155. package/src/facets/seed-examples.mjs +58 -0
  156. package/src/facets/sensitivity.mjs +98 -0
  157. package/src/facets/service.mjs +53 -0
  158. package/src/facets/store.mjs +39 -0
  159. package/src/facets/worker.mjs +23 -0
  160. package/src/folds-store.mjs +109 -0
  161. package/src/groups.mjs +167 -0
  162. package/src/handoff-tool.mjs +51 -0
  163. package/src/handoff.mjs +127 -0
  164. package/src/index.mjs +649 -0
  165. package/src/inject-policy.mjs +17 -0
  166. package/src/learning.mjs +88 -0
  167. package/src/memory/alias-proposal.mjs +40 -0
  168. package/src/memory/bc-audit.mjs +245 -0
  169. package/src/memory/bc-index-updater.mjs +404 -0
  170. package/src/memory/bc-index.mjs +48 -0
  171. package/src/memory/bc-label.mjs +220 -0
  172. package/src/memory/bc-panel-state.mjs +107 -0
  173. package/src/memory/bc-pre-step.mjs +463 -0
  174. package/src/memory/bc-recall.mjs +470 -0
  175. package/src/memory/bc-shift.mjs +123 -0
  176. package/src/memory/bc-store.mjs +41 -0
  177. package/src/memory/bm25.mjs +112 -0
  178. package/src/memory/calibration.mjs +54 -0
  179. package/src/memory/chatter.mjs +29 -0
  180. package/src/memory/cmap-file.mjs +107 -0
  181. package/src/memory/concepts-file.mjs +41 -0
  182. package/src/memory/coref-slots.mjs +40 -0
  183. package/src/memory/event-alias-ledger.mjs +87 -0
  184. package/src/memory/event-log.mjs +95 -0
  185. package/src/memory/event-model.mjs +132 -0
  186. package/src/memory/event-segmenter.mjs +36 -0
  187. package/src/memory/event-summary-tree.mjs +122 -0
  188. package/src/memory/extraction-adapter.mjs +76 -0
  189. package/src/memory/extraction-composite.mjs +75 -0
  190. package/src/memory/extraction-core.mjs +76 -0
  191. package/src/memory/extraction-pre-step.mjs +161 -0
  192. package/src/memory/extraction-provider.mjs +300 -0
  193. package/src/memory/extraction-score.mjs +36 -0
  194. package/src/memory/facet-stabilizer.mjs +59 -0
  195. package/src/memory/history-map.mjs +139 -0
  196. package/src/memory/language-router.mjs +30 -0
  197. package/src/memory/load-gate.mjs +161 -0
  198. package/src/memory/mechanical-note.mjs +104 -0
  199. package/src/memory/onboarding.mjs +163 -0
  200. package/src/memory/persona.mjs +78 -0
  201. package/src/memory/planner.mjs +249 -0
  202. package/src/memory/pre-turn.mjs +80 -0
  203. package/src/memory/query-aliases.mjs +39 -0
  204. package/src/memory/recall-cue.mjs +43 -0
  205. package/src/memory/recall.mjs +65 -0
  206. package/src/memory/referent-candidates.mjs +55 -0
  207. package/src/memory/referent-ledger.mjs +86 -0
  208. package/src/memory/relabel-migrate.mjs +376 -0
  209. package/src/memory/relik-adapter.mjs +25 -0
  210. package/src/memory/special-keywords.mjs +60 -0
  211. package/src/memory/structured-adapters.mjs +75 -0
  212. package/src/memory/summary-index.mjs +335 -0
  213. package/src/memory/summary-tree.mjs +110 -0
  214. package/src/memory/term-extractor.mjs +44 -0
  215. package/src/memory/tokenize.mjs +9 -0
  216. package/src/memory/vector-index.mjs +54 -0
  217. package/src/memory/window-summary.mjs +133 -0
  218. package/src/mood.mjs +133 -0
  219. package/src/peer-tool.mjs +63 -0
  220. package/src/peer.mjs +144 -0
  221. package/src/policy.mjs +170 -0
  222. package/src/preflight.mjs +113 -0
  223. package/src/provenance.mjs +94 -0
  224. package/src/range.mjs +75 -0
  225. package/src/recall-exact-tool.mjs +267 -0
  226. package/src/recall-tool.mjs +521 -0
  227. package/src/recheck.mjs +99 -0
  228. package/src/save-start.mjs +29 -0
  229. package/src/session-guard.mjs +140 -0
  230. package/src/session-log-tool.mjs +322 -0
  231. package/src/session-log.mjs +606 -0
  232. package/src/skills.mjs +111 -0
  233. package/src/split-range.mjs +115 -0
  234. package/src/state.mjs +198 -0
  235. package/src/t0.mjs +149 -0
  236. package/src/tag-confirm.mjs +78 -0
  237. package/src/tag-detect.mjs +42 -0
  238. package/src/tag-maintenance.mjs +285 -0
  239. package/src/tag-reject.mjs +73 -0
  240. package/src/tags-store.mjs +94 -0
  241. package/src/tags.mjs +692 -0
  242. package/src/threads-backend.mjs +26 -0
  243. package/src/threads-core.mjs +1887 -0
  244. package/src/threads-store.mjs +207 -0
  245. package/src/threads-tool.mjs +522 -0
  246. package/src/threads.mjs +929 -0
  247. package/src/tool-preview.mjs +427 -0
  248. package/src/topic.mjs +63 -0
  249. package/src/training.mjs +75 -0
  250. package/src/turn-tag.mjs +62 -0
  251. package/src/user-turn.mjs +39 -0
  252. package/templates/cmap.md +11 -0
  253. package/templates/concepts.md +12 -0
  254. package/templates/habits.md +40 -0
  255. package/templates/keywords.md +6 -0
  256. package/templates/rebuild.md +134 -0
  257. package/templates/rules-safe.md +184 -0
  258. package/templates/rules.md +177 -0
@@ -0,0 +1,355 @@
1
+ # Continuity architecture — 4.11.36
2
+
3
+ ## 1. Shape
4
+
5
+ ```text
6
+ DSH Host
7
+ └ dsh-continuity host row
8
+ ├ /dsh-continuity/state.json
9
+ ├ /dsh-continuity/bc.json
10
+ ├ /dsh-continuity/peer
11
+ ├ per-session bc pre-step
12
+ └ best-effort tool path guard
13
+
14
+ Continuity preset
15
+ ├ compaction engine
16
+ ├ session_log
17
+ └ threads-tool (safe mode: recall + peer + session_log)
18
+
19
+ Client
20
+ ├ left sidebar control panel
21
+ └ per-session A/B/C/K right tab
22
+ ```
23
+
24
+ Safe mode does not add a separate prompt-classification, tagging, or workline layer.
25
+
26
+ ## 2. Session owns everything
27
+
28
+ ```text
29
+ ~/.dsh/continuity/sessions/<sessionId>/
30
+ bc-index.jsonl
31
+ bc-index-state.json
32
+ audit-state.json
33
+ expand-hint.jsonl
34
+ concepts.md
35
+ cmap.md
36
+ keywords.md
37
+ aliases.md
38
+ rules.md
39
+ habits.md
40
+ rebuild.md
41
+ ```
42
+
43
+ No A/C/K/rules/habits inheritance between sessions. A new session starts with:
44
+
45
+ ```text
46
+ empty A/C/K
47
+ rules.md <- templates/rules.md
48
+ habits.md <- templates/habits.md
49
+ ```
50
+
51
+ Personas are identity/enable labels only:
52
+
53
+ ```text
54
+ session-persona.json:
55
+ <sessionId> -> continuity | <pid>
56
+ default -> continuity
57
+
58
+ personas/<pid>/persona.json:
59
+ {"enabled": true}
60
+ ```
61
+
62
+ `continuity` is enabled for old installs without a file. A disabled persona is
63
+ skipped by the pre-step and its panel returns `persona-disabled`.
64
+
65
+ ## 3. Write path
66
+
67
+ ```text
68
+ completed human turn
69
+ -> bc-pre-step sees agent.session.id
70
+ -> resolve persona (enable check only)
71
+ -> ensure session scaffolds
72
+ -> index file sessions/<id>/bc-index.jsonl
73
+ -> read session tail after lastSeq
74
+ -> label with that session's concepts
75
+ -> append one pointer record
76
+ -> save JSONL + bc-index-state.json
77
+ ```
78
+
79
+ The current turn is indexed on the next pre-step. Up to 8 missing turns per
80
+ pass; background repair continues. Failure writes `gate-errors.jsonl`.
81
+
82
+ New records carry:
83
+
84
+ ```json
85
+ {
86
+ "turn": 40,
87
+ "seqStart": 2186,
88
+ "seqEnd": 2186,
89
+ "generic": ["跑团叙事", "原文与上下文"],
90
+ "genericTopic": ["跑团叙事"],
91
+ "genericWorkflow": ["原文与上下文"],
92
+ "topicScore": 0.71,
93
+ "topicConfidence": "normal",
94
+ "labelVersion": 2,
95
+ "terms": ["跑团"],
96
+ "keywords": ["coo"],
97
+ "conceptScores": {"跑团叙事": 0.71, "原文与上下文": 0.66},
98
+ "vec": "...",
99
+ "chatter": 0.42,
100
+ "sessionId": "session-..."
101
+ }
102
+ ```
103
+
104
+ No raw text is stored in the index. Old records read as `labelVersion: 1` and
105
+ have no `topicScore`; they are not backfilled.
106
+
107
+ ## 4. A / B / C / K
108
+
109
+ ### A — concepts
110
+
111
+ ```text
112
+ name | description words | coverage estimate | kind
113
+ kind = topic | workflow (default: topic)
114
+ ```
115
+
116
+ - `recall("A标签")` lists topic only.
117
+ - `recall("A标签:workflow")` lists workflow only.
118
+ - `recall("A标签:<name>")` browses any concept.
119
+ - `kind` is a human decision, not derived from coverage.
120
+
121
+ ### B — per-turn labels
122
+
123
+ `generic` is the topic+workflow union for compatibility. `genericTopic` and
124
+ `genericWorkflow` carry the split. B is display/feature material, not A browse.
125
+
126
+ ### C / K
127
+
128
+ Both are session-local files:
129
+
130
+ ```text
131
+ sessions/<id>/cmap.md
132
+ sessions/<id>/keywords.md
133
+ ```
134
+
135
+ No fallback to another session, a persona, or the old global table.
136
+
137
+ ## 5. Recall
138
+
139
+ ```text
140
+ recall({ query?, recent?, sinceTurn? })
141
+ ```
142
+
143
+ | Query | Result |
144
+ |---|---|
145
+ | none | interpret current user message |
146
+ | `A标签` | topic inventory |
147
+ | `A标签:workflow` | workflow inventory |
148
+ | `A标签:<name>` | area hits for that concept |
149
+ | `B标签` / `C标签` / `K标签` | namespace inventory |
150
+ | `B标签 <v>` / `C标签 <v>` / `K标签 <v>` | namespace search |
151
+ | plain text | literal + alias + K/C/A |
152
+
153
+ - Index and vocabulary come from the current session only.
154
+ - `via` is `A | B | C | K | live`.
155
+ - `intervals` keeps every match; the injected note is bounded.
156
+ - A result is candidates, not facts; open the cited turn/seq.
157
+
158
+ ## 6. Compaction and receipts
159
+
160
+ ```text
161
+ measure
162
+ -> planFold(policy)
163
+ -> choose oldest balanced range
164
+ -> summarize range to [gist]
165
+ -> keep protected tail
166
+ -> record fold + pass
167
+ ```
168
+
169
+ Band:
170
+
171
+ ```text
172
+ high = 0.48 W start a pass
173
+ low = 0.32 W target request
174
+ emergency = 0.65 W
175
+ emergencyTo = 0.42 W
176
+ protect = 0.14 W
177
+ maxPerBatch = 190k
178
+ minAction = 24k
179
+ ```
180
+
181
+ The continuity preset uses this balanced band from 4.9.122. On the measured
182
+ 0b window (baseline 133,272) the floor is ~244.2k, so the 253.6k low target is
183
+ reachable with ~9.4k headroom; .14 is a conservative default, not a guarantee. One pass removes
184
+ ~127k, below the 190k cap; emergency 0.65→0.42 is 182k, so the 190k cap is
185
+ kept for emergency drainage.
186
+
187
+ One pass folds one oldest contiguous range. `budget` caps the folded **surface**
188
+ tokens only; the associated non-surface prefix can leave with it, so actual
189
+ request reduction can exceed the budget. Measured: pre 375,803 → next request
190
+ 201,756 (174,047 gone) while budget was 122,179 and folded surface was 114,373.
191
+ The selected end must also be tool-pair balanced, so `afterTokens` can be above
192
+ the floor. This is expected, not a measurement error: the measured 0b pass was
193
+ +3,639 above its floor. Reachable floor:
194
+
195
+ ```text
196
+ floor(T) = (protect * W + baseline(T)) / W
197
+ ```
198
+
199
+ `baseline` is not constant. For the pass decision, use the baseline at the
200
+ triggering request (`preBaselineTokens`); `afterBaselineTokens` describes the
201
+ post-fold state and must not be used as the pass floor. The measured 0b pass
202
+ closed at preBaseline 133,272 + .16W = 260,084, and afterTokens was 263,723
203
+ (+3,639 from the tool-pair cut).
204
+
205
+ `protect` is calibrated once, at the first band-high trigger, from that
206
+ request's preBaseline:
207
+
208
+ ```text
209
+ protect_eff = clamp((low*W - preBaseline - margin)/W, 0.10, 0.20)
210
+ margin = 0.01*W
211
+ ```
212
+
213
+ The same fold uses the calibrated value; it is then frozen for the session
214
+ (protect_source=calibration, B0=preBaseline, margin recorded). If calibration
215
+ is unavailable, the default .14 is used (`protect_source=default`). Later folds
216
+ compare the suggested protect against protect_eff; a gap >= 0.02 produces one
217
+ suggestion, never an automatic policy change. If protect_eff clamps at .10 and
218
+ floor >= low, both "low unreachable" and `low_min = floor + margin` are
219
+ reported.
220
+
221
+ Long-session data (5,498 measurements, baseline/surface median 0.519) shows
222
+ that .14 is not a guarantee: floor = baseline + .14W is below low only while
223
+ baseline < 142,663, and three of six sampled pre-fold baselines were above
224
+ that. The drift criterion therefore watches protect distance, not just a
225
+ baseline percentage.
226
+
227
+ Receipt:
228
+
229
+ ```text
230
+ [continuity:fold] seq <from>–<to> · N 条 · tokens · reason=...
231
+ 压前 ... → 压后 ... · window ...
232
+ ```
233
+
234
+ Files:
235
+
236
+ ```text
237
+ ~/.dsh/continuity/passes.jsonl append-only, no 24-row cap
238
+ ~/.dsh/continuity/folds.json from/to/gistSeq/at;
239
+ new folds also count/compactionId
240
+ ~/.dsh/continuity/sessions/<id>/dropped-ranges.json
241
+ per-session merged ranges + coverageFrom
242
+ ```
243
+
244
+ `sessions/<id>/dropped-ranges.json` merges only that session's folds (history
245
+ before pass receipts) and passes. The global `dropped-ranges.json` is no longer
246
+ written; merging different seq spaces was invalid.
247
+
248
+ ## 7. Audit
249
+
250
+ Every 200 turns, the per-session audit reads the session index and the session's
251
+ concepts:
252
+
253
+ ```text
254
+ vec + chatter + keywords + conceptScores
255
+ k = clamp(round(N/50), 20, 60)
256
+ size >= 10
257
+ topicMax < 0.5
258
+ ```
259
+
260
+ The result is a pending hint in the session's `expand-hint.jsonl`. It does not
261
+ edit concepts automatically. An empty A table skips audit rather than failing.
262
+
263
+ ## 8. Panel
264
+
265
+ `/dsh-continuity/bc.json?sessionId=<id>`:
266
+
267
+ ```text
268
+ enabled persona -> available with that session's A/B/C/K
269
+ disabled -> persona-disabled
270
+ unknown -> bc-index not found
271
+ ```
272
+
273
+ The client reads `sessionId` from slot props. On `persona-disabled`, it closes
274
+ the tab. The panel is read-only and never compacts.
275
+
276
+ ## 9. Peer
277
+
278
+ ```text
279
+ peer_message({ to, text, from? })
280
+ POST /dsh-continuity/peer
281
+ ```
282
+
283
+ `to` must be an exact live session id. No newest/active fallback.
284
+
285
+ ## 10. Isolation guard
286
+
287
+ The host registers a best-effort `tools.guard`:
288
+
289
+ ```text
290
+ each session may touch only its own sessions/<id>/ directory
291
+ another session's continuity files are denied
292
+ ```
293
+
294
+ This is not an OS boundary. Arbitrary code with full access can bypass it;
295
+ strong isolation needs ACL/container. The guard exists to stop normal
296
+ edit/write/shell tools from crossing sessions.
297
+
298
+ ## 11. Injection prefixes
299
+
300
+ ```text
301
+ [continuity:boot] onboarding
302
+ [continuity:audit] audit suggestion
303
+ [continuity:fold] pass receipt
304
+ ```
305
+
306
+ Plugin-injected `role=user` messages start with `[continuity:*]`.
307
+ `[追忆]` is a recall tool result.
308
+
309
+ ## 12. Code map
310
+
311
+ ```text
312
+ src/index.mjs host routes, panel, guard install
313
+ src/engine.mjs compaction, pass receipt, fold record
314
+ src/state.mjs passes, dropped-ranges
315
+ src/policy.mjs band math
316
+ src/recall-tool.mjs A/B/C/K retrieval
317
+ src/memory/bc-pre-step.mjs per-session dispatcher
318
+ src/memory/bc-index.mjs JSONL codec
319
+ src/memory/bc-index-updater.mjs turn labelling
320
+ src/memory/bc-panel-state.mjs panel projection
321
+ src/memory/concepts-file.mjs A parser (kind)
322
+ src/memory/persona.mjs identity/enable/session paths
323
+ src/memory/onboarding.mjs boot marker/scaffolds
324
+ src/memory/bc-audit.mjs audit + hint
325
+ src/threads-store.mjs per-session rules/habits
326
+ src/folds-store.mjs folds.json
327
+ src/session-guard.mjs best-effort tool path guard
328
+ client.js right tab
329
+ cordis.patch.yml bundle patch
330
+ ```
331
+
332
+ ## 13. Authoritative layout
333
+
334
+ `docs/SESSION-LAYOUT.md` is the authoritative path/permission matrix:
335
+
336
+ ```text
337
+ sessions/<id>/ one complete set per session
338
+ personas/ deprecated archive; persona.json enable only
339
+ global folds/passes engine-owned
340
+ model tools own session dir + workspace only
341
+ ```
342
+
343
+ ## 14. Invariants
344
+
345
+ ```text
346
+ Every session owns A/B/C/K/rules/habits/index.
347
+ No cross-session vocabulary or rules inheritance.
348
+ A topic browse excludes workflow by default.
349
+ A candidate is not a fact.
350
+ [gist] is reconstruction, not verbatim.
351
+ A pass receipt is append-only.
352
+ floor(T) depends on the current baseline.
353
+ No extra prompt-classification/tagging/workline layer in safe mode.
354
+ Tool guard is not an OS security boundary.
355
+ ```
package/docs/CACHE.md ADDED
@@ -0,0 +1,253 @@
1
+ # Cache-first context maintenance
2
+
3
+ > **Historical.** 当前 band/floor 行为以 `docs/ARCHITECTURE.md` 和 `src/policy.mjs` 为准;本文的旧数字/策略不作当前契约。
4
+
5
+ The goal is not “small drops”; it is **few prefix rewrites**. This note explains why, measures the
6
+ current Continuity session, and records the policy that follows from it.
7
+
8
+ ## 1. Why every compaction invalidates the suffix
9
+
10
+ The provider's prompt cache is prefix-addressed: a request is a hit only up to the first token that
11
+ differs from a cached request. A compaction replaces an old span with one summary user message at
12
+ the **start** of that span. Everything before the span (system prompt, tool schemas) is unchanged;
13
+ everything after the replacement now has a different prefix, so the entire suffix misses on the
14
+ next request and must be re-read/re-cached.
15
+
16
+ Therefore:
17
+
18
+ - **Pass count is the cost**, not pass size. Twenty 4k passes invalidate twenty times; one 190k
19
+ pass invalidates once.
20
+ - A small pass is the worst case: it removes little but still pays a full suffix re-read.
21
+ - Compaction cannot be made cache-transparent while it removes surface nodes. The only lever is to
22
+ do it rarely and do enough at once.
23
+
24
+ ## 2. Measured evidence from the Continuity session
25
+
26
+ Session `session-0b094f6e-8a4b-4c61-9ce8-0aec316c57b3` (preset `continuity`, log decoded read-only):
27
+
28
+ | Metric | Value |
29
+ |---|---:|
30
+ | summary compactions | **53** |
31
+ | total shadowed tokens | **698,108** |
32
+ | average pass | 13,172 |
33
+ | largest pass | 100,725 |
34
+ | one-node passes | 34 |
35
+ | passes under 10k | 47 |
36
+ | passes 50k–150k | 6 |
37
+
38
+ The old policy (`setpoint 0.65 / high 0.80 / low 0.55 / maxPerBatch 8k / minAction 4k`) capped every
39
+ high pass at 8k. Draining from 80% to 55% is ~198k, so it took ~24 passes — and because the selector
40
+ stops before a node that would exceed the budget, many passes removed a single ~3.4k node. That is
41
+ the 21 “band-high single message” passes plus emergencies.
42
+
43
+ The cache cost is visible in the provider usage of two adjacent requests:
44
+
45
+ ```
46
+ request A: inputTokens 659 cacheReadTokens 733,184 (99.91% cached)
47
+ ... compaction (one 4k node + summary) ...
48
+ request B: inputTokens 733,414 cacheReadTokens 8,704 (1.2% cached)
49
+ ```
50
+
51
+ A 4k-token reduction forced a **733k-token prefix re-read**. This is why lifetime cache hit rate is
52
+ the wrong metric: 53 such events dilute it. Look at the per-request `cacheReadTokens` between two
53
+ compactions instead.
54
+
55
+ ## 3. The policy that follows
56
+
57
+ `src/policy.mjs` now exports `CACHE_FIRST_POLICY` (the default), with `W = messageBudget`:
58
+
59
+ | Field | Value | Tokens at W = 792,576 |
60
+ |---|---:|---:|
61
+ | `setpoint` | 0.60 | 475,546 |
62
+ | `high` | 0.60 | 475,546 |
63
+ | `low` | 0.40 | 317,030 |
64
+ | `emergency` | 0.65 | 515,174 |
65
+ | `emergencyTo` | 0.42 | 332,882 |
66
+ | `maxPerBatch` | 190,000 | — |
67
+ | `minAction` | 24,000 | — |
68
+ | `protect` | 0.30 | 237,773 |
69
+
70
+ `setpoint` **equals** `high` on purpose. Above high, one pass computes
71
+ `est − low × W` (≈158k at the crossing) and the 190k cap lets it land at low. The context then
72
+ swings between 40% and 60%; there are no 24k proportional passes in between. The optional
73
+ `CACHE_GRADUAL_POLICY` keeps `setpoint 0.50` for a smoother average context, but every 24k pass it
74
+ adds is another prefix invalidation.
75
+
76
+ `emergencyTo` is **below high** on purpose. With the old 0.88, a 97% overshoot emergency-landed at
77
+ 88% (697,467), still above high, so the next batch immediately ran a second band-high pass: two
78
+ prefix invalidations for one overshoot. 0.2.7 lands at 42% (332,882), leaving 18 percentage points
79
+ below high 60% (475,546); exactly 65% still takes one band-high, and >65% takes one emergency.
80
+
81
+ Expected pass count for the same 698,108 tokens removed:
82
+
83
+ ```
84
+ 698,108 / 158,515 ≈ 4.4 → 5 passes
85
+ ```
86
+
87
+ That is the 53 → 5 reduction, without shrinking the protected tail or touching the raw archive.
88
+
89
+ ### What stays fixed
90
+
91
+ - **system + tools prefix**: never compacted; the engine starts at the oldest non-system node.
92
+ - **protected tail**: 30% of W (237,773) is never folded; the low water mark (62%) is above it.
93
+ - **gist pointer / raw archive**: the summary still carries
94
+ `[gist · log:<session>#seq=<start>-<end> · N messages · original retrievable]`; the original log
95
+ events remain at those seqs.
96
+
97
+ ### What it costs
98
+
99
+ - One large summarization call per pass (~190k input, a few k output).
100
+ - The context oscillates over a 24% band instead of hovering near a setpoint.
101
+ - The first request after a pass is a cache miss from the replacement onward; later requests hit
102
+ again. The win is fewer such first requests.
103
+
104
+ ## 4. How to verify
105
+
106
+ 1. In the Web UI's performance usage, read per-request `cacheReadTokens` / `inputTokens`; do not
107
+ compare lifetime percentages.
108
+ 2. A pass is followed by one low-hit request; subsequent requests should return to high hits.
109
+ 3. The Continuity panel's “Recent passes” list records each pass (range, tokens, reason, time).
110
+ Compare the interval between passes before/after the policy change.
111
+ 4. The expected signal: pass count drops from ~53 to ~4 for the same session growth; the total
112
+ tokens removed stays similar.
113
+
114
+ ## 5. Applying it to a live session
115
+
116
+ The policy is read when the engine mounts. An already-running session keeps the revision it started
117
+ with, so either:
118
+
119
+ - restart DSH and resume the Continuity session with the updated preset (`preset-continuity` pins
120
+ `setpoint 0.86` and inherits the rest of `CACHE_FIRST_POLICY`), or
121
+ - fork the session; the child mounts the current preset and inherits the history, or
122
+ - remount the bundle. In this run a plugin-manager `set_plugin` toggle remounted the preset and
123
+ applied the pinned cache-first policy live at 18:27:37 (`engine.policy` 0.86/0.86/0.62,
124
+ 190000/24000/0.30). The JS-module cache still means a full restart is needed for code-only
125
+ changes such as the cold-start route fix.
126
+
127
+ The parent session's 53 old summaries remain as gists; the next high crossing under the new policy
128
+ is the first large drain. Its pass record (reason `band-high`, budget 190000) is the first
129
+ cache-first observation.
130
+
131
+ ## A4/B in 0.2.4
132
+
133
+ A4-safe previews only old, non-active `tool/result` text as the first stage of a compression whose
134
+ target is **low 62%** (`emergencyTo` 80% for an emergency), not merely `high` 86%. If the preview
135
+ cannot cover the whole gap it is still applied, and the remaining pass continues to the target even
136
+ when the estimate is already below high. This is deliberately different from 0.2.2, which stopped
137
+ after previewing to high: that left the context hovering at 86% and caused the repeated prefix
138
+ invalidations the 190k band is meant to avoid. One high crossing now produces one 24%-W band; the
139
+ oldest user/reasoning is folded only when the tool previews cannot reach 62%, and active pins stay
140
+ full. The summary-view (B) preview changes only the summarization input, never the surface or cache.
141
+
142
+ 0.2.4 adds a 3-turn active window (old unmatched calls no longer pin), non-text handles that keep
143
+ the payload out of the main context (one optional auxiliary semantic call; deterministic fallback),
144
+ and an overflow-only active pause. The pause is the only path that can detach an active result; it
145
+ does so only when that result is large enough to explain the measured overage. Raw stats may
146
+ double-count summary + replacement; that overlap is archive-only, not context.
147
+
148
+ 0.2.5 adds no policy change. It makes A4 skip `[tool result handle]` / `[continuity pause]` /
149
+ existing previews (no second preview of a handle), measures the surface after each pass and records
150
+ `afterTokens`/`afterSurfaceTokens`/`rebloatTokens`, and labels a manual `/compact` with a gist header
151
+ plus a `manual` pass record. The manual cut remains the base command's depth.
152
+
153
+ 0.2.6 introduced the 515k hard cap. 0.2.7 tunes the ratio to high 0.60 (475,546; 45.4% full),
154
+ low 0.40 (317,030; 30.2%), emergency 0.65 (515,174; 49.1% full) → emergencyTo 0.42 (332,882; 31.8%).
155
+ The high→emergency gap is 5% W (39,628), above `minAction` 24k; 0.62/0.65 was only 3% (23,777), below
156
+ it. `emergencyTo` is below high, so it cannot chain into band-high. `protect`, `maxPerBatch`, and
157
+ `minAction` are unchanged.
158
+
159
+ 0.3.0 adds threads without changing the band policy. The active/rules line is a `user/message`
160
+ appended after the surface tail once per turn; it is never part of the system/tool prefix, so the
161
+ static cache prefix is unchanged. The active thread's `loadSeq` is a hard pin: `oldestRange()` uses
162
+ it as the tail floor, so automatic compaction cannot fold the loaded quote. A save clears
163
+ `activeId`; the old quote becomes foldable. Save/load commits are deferred to the next pre-step,
164
+ outside the tool call/result pair.
165
+
166
+ 0.3.1 changes injected messages to `source: { kind: "user" }`. The v4 format rejects an
167
+ unregistered `plugin` source kind; the line/load/manual/semantic messages are context, not
168
+ compaction checkpoints, so `user` is the correct producer-owned kind. The gist replacement keeps the
169
+ base `compact` checkpoint source.
170
+
171
+ 0.3.2 moves that to `{ kind: "runtime-context", form: "snapshot", sections: [...] }`, which v4
172
+ accepts and the UI renders as a context block rather than a human message. It also removes the
173
+ post-gist `compaction/prune`; the summary's own `shadowedTokenCount` is the price, and a prune after
174
+ the replacement names a shadowed span. No post-replacement prune means no v4 surface corruption.
175
+
176
+ 0.3.3 keeps one injection per turn (not per step) and prefixes the rules with an attribution:
177
+ they are the instance's own `rules.md`, not a new user instruction or system requirement, and may
178
+ be ignored or rewritten. The block is appended after the tail; the static prefix is unchanged.
179
+
180
+ 0.3.4 adds `thread.segments[]` and optional `save.threadId`. Append is explicit: the engine never
181
+ guesses affinity. `list`/`active` display `segments`/`lastAt`; load quotes the latest segment with
182
+ `第N/M段`; old one-range state normalizes on read and is rewritten with segments on the next write.
183
+ Thread-level totals/`next` follow the latest segment, so the active line and load header stay small.
184
+
185
+ 0.3.5 adds `scan` (read-only, non-injecting, reasoning-free heads, synthetic context skipped, <900
186
+ token payload) and range/select load (`fromSeq`/`toSeq`, `select`, `segs`). The static prefix is
187
+ untouched; scan writes nothing and load still queues its quote for the next pre-step.
188
+
189
+ 0.3.6 adds the `tags.json` sidecar (overrides → last run → T0), find/tag/retag/tags, scan-by-seqs,
190
+ and tag/seqs load (earliest 3 + latest 5, `segs`). No `[TX]` enters the raw log; the auto-tagger
191
+ writes one run per completed turn into the sidecar only.
192
+
193
+ 0.3.7 keeps active only for thread loads; tag/seqs loads append a quote without moving `[线]`.
194
+ find(T0) returns complement ranges; find bounds are strict; tags() carries display/count/active;
195
+ tag accepts a label; receipts dedupe the display name; updatedAt advances.
196
+
197
+ 0.3.8 tag ranges append one run instead of hundreds of overrides; find exposes coverage
198
+ ranges/covered; untag writes a T0 run and clears overrides in the interval. No auto-merge, no raw
199
+ change, no save change.
200
+
201
+ 0.3.9 tags() rows split entries (runs+overrides) from events (actual resolved coverage); count is
202
+ an alias of events. The coverage pass uses max(session seq, runs, overrides); no other behavior changes.
203
+
204
+ 0.4.0 adds T= to gist headers, folds.json visible/folded plus load annotation, thread aliases,
205
+ generated tags.md with keywords, tag load first/last, and a slim [标签]/[习惯] per-turn block. No
206
+ save/load segment semantics changed.
207
+
208
+ 0.4.1 allows tag metadata-only (label/keywords/alias), tag alias sets the matching thread alias,
209
+ tags() display uses alias first. find(T0) remains the full complement.
210
+
211
+ 0.4.2 unifies T0: tags().T0.events is the complement and equals find(T0).count; other tags match
212
+ find().covered; tags.md uses the same. Gist T= uses display, omits T0 unless all T0, max 3 plus +N.
213
+
214
+ 0.4.3 treats alias as display only: auto/find/load/tag writes use canonical thread id, alias resolves,
215
+ and alias-named tags migrate into the canonical tag. find count = total coverage (incl folded), seqs =
216
+ page, covered = count.
217
+
218
+ 0.4.4 default save clamps its start to the current T run start (explicit from/to untouched); gist T
219
+ is primary display + secondary non-T0 event count, e.g. T3 +2; no multi-tag list.
220
+
221
+ 0.4.5 replaces the run start with a dominance block (own runs join; other-T islands <=5 cross; larger
222
+ other stops). Default save appends to the thread bound to the current T; a new non-T0 thread inherits
223
+ the current T alias and is active.
224
+
225
+ 0.4.6 load withReasoning: chronological body + paired reasoning, one 16k budget, 800-token reasoning
226
+ truncated to 150 head + 650 tail, receipt reasoningInjected/reasoningTokens/skipped. Default 8k load,
227
+ gist, and scan unchanged.
228
+
229
+ 0.4.7 adds explicit delete with deleted/ backup, header segment/token/activity counts, save relabel
230
+ protection, tag/retag alias id resolution with unknown-tag-id, and drops zero-event tags.md rows.
231
+
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+
247
+
248
+
249
+
250
+
251
+
252
+
253
+
@@ -0,0 +1,30 @@
1
+ # 基本守则(CONDUCT)
2
+
3
+ > 这是给**新实例**的最小守则,不是完整规则。完整规则见 `rules.md`,那份是长出来的,不必照抄。
4
+ > 引擎会在头里给提示;**提示里带命令的,照命令做**。守则只写“为什么”和“哪条不能省”。
5
+
6
+ ## 不可省的五条
7
+
8
+ 1. **每条内容必须被判过**:贴标签,或显式判 `T0`。没有“还没轮到”——`save` 会因未决拒绝。
9
+ 2. **每轮一个标**:`turnmap.mjs set --turn N --tag Tx`。轮是检索单位,标签是 turn ↔ seq ↔ T 的桥。
10
+ 3. **不猜**:不确定属于哪个话题,先 `match-tags.mjs "<原话>"` 查表;查不到再判 T0 或建新标签(要带证据)。
11
+ 4. **改代码:先判据,后改,再验**。同一处改到第三版就停下,先搜文献、先写“怎么算改好”。
12
+ 5. **不可逆动作先问**(删除、覆盖、重置、重启);可逆的(切线、收段)自己决定,做完报一句。
13
+
14
+ ## 别做的三件
15
+
16
+ - **别把痕迹当状态**:记“当时注意力朝哪”,不写“我当时很难过”,也不许事后补写。
17
+ - **别让结论只靠用户的话**:只有陈述支撑的结论标「待验证」,重要结论留 `basis`。
18
+ - **别为了省钱跳过检查**:验一次比返工便宜;闸拒绝时先看它列出的清单,别硬折。
19
+
20
+ ## 引擎会替你做的(不用背)
21
+
22
+ ```
23
+ 头里注入:当前线 / 标签 / 上次段 / 下一步
24
+ 每轮:自动归位(唯一命中就贴)
25
+ 漏标:下一轮出现 [打标?]
26
+ 换话题:出现 [话题?](先 kw-audit explain 再决定)
27
+ save 时:T0 闸 / 标签闸 / tags-gap 闸 / C7 闸
28
+ ```
29
+
30
+ **闸不是敌人**:它拒绝时给出的清单,就是“该补的账”。