@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.
- package/INSTALL.md +25 -0
- package/PANEL.md +92 -0
- package/README.md +286 -2
- package/RELEASE-NOTES.md +732 -0
- package/bin/bc-acceptance-full.mjs +91 -0
- package/bin/bc-acceptance-tight.mjs +66 -0
- package/bin/bc-acceptance.mjs +22 -0
- package/bin/bc-add-keywords.mjs +21 -0
- package/bin/bc-demo.mjs +47 -0
- package/bin/bc-expand-hint.mjs +179 -0
- package/bin/bc-ingest-index.mjs +35 -0
- package/bin/bc-ingest.mjs +31 -0
- package/bin/bc-label-demo.mjs +18 -0
- package/bin/bc-relabel.mjs +150 -0
- package/bin/bc-sessionize.mjs +59 -0
- package/bin/coref_pipeline.py +47 -0
- package/bin/coref_service.py +57 -0
- package/bin/debug_ckip.py +16 -0
- package/bin/debug_ner_zh.py +18 -0
- package/bin/doctor.mjs +275 -0
- package/bin/en_ner_error_audit.py +33 -0
- package/bin/eval-label-rules.mjs +342 -0
- package/bin/export-turns.py +68 -0
- package/bin/extract-user-paragraph.mjs +14 -0
- package/bin/extraction-composite-smoke.mjs +28 -0
- package/bin/extraction-latency.mjs +58 -0
- package/bin/extraction-user-smoke.mjs +28 -0
- package/bin/extraction_service.py +164 -0
- package/bin/facet-active-experiment.mjs +110 -0
- package/bin/facet-covid.mjs +8 -0
- package/bin/facet-curve.mjs +27 -0
- package/bin/facet-embedding-train.mjs +20 -0
- package/bin/facet-embedding-validate.mjs +83 -0
- package/bin/facet-eval-roleplay.mjs +42 -0
- package/bin/facet-eval.mjs +38 -0
- package/bin/facet-fold.mjs +31 -0
- package/bin/facet-load.mjs +16 -0
- package/bin/facet-mlp-scan.mjs +45 -0
- package/bin/facet-mvp.mjs +34 -0
- package/bin/facet-online-experiments.mjs +78 -0
- package/bin/facet-online-race.mjs +99 -0
- package/bin/facet-online-validate.mjs +51 -0
- package/bin/facet-panel.mjs +22 -0
- package/bin/facet-passage.mjs +41 -0
- package/bin/facet-random-validate.mjs +48 -0
- package/bin/facet-roleplay.mjs +33 -0
- package/bin/facet-scan.mjs +56 -0
- package/bin/facet-segment.mjs +34 -0
- package/bin/facet-train.mjs +19 -0
- package/bin/facet-validate.mjs +103 -0
- package/bin/facet-zeroshot.mjs +14 -0
- package/bin/install-templates.mjs +72 -0
- package/bin/memory-bench.mjs +53 -0
- package/bin/memory-build-summary.mjs +43 -0
- package/bin/memory-build-vector.mjs +31 -0
- package/bin/memory-build.mjs +41 -0
- package/bin/memory-calibration.mjs +70 -0
- package/bin/memory-closed-loop.mjs +119 -0
- package/bin/memory-dry-run.mjs +70 -0
- package/bin/memory-eval.mjs +104 -0
- package/bin/memory-event-alias-test.mjs +89 -0
- package/bin/memory-extraction-bench.mjs +58 -0
- package/bin/memory-load-gate-replay.mjs +137 -0
- package/bin/memory-loop-test.mjs +90 -0
- package/bin/memory-pre-turn-harness.mjs +63 -0
- package/bin/memory-query.mjs +39 -0
- package/bin/memory-replay-hints.mjs +84 -0
- package/bin/memory-scale.mjs +58 -0
- package/bin/memory-scenario-test.mjs +146 -0
- package/bin/memory-user-loop.mjs +95 -0
- package/bin/ner_bench_tiny_zh.py +59 -0
- package/bin/ner_bench_zh.py +60 -0
- package/bin/ner_conll_en.py +139 -0
- package/bin/ner_direct_en.py +123 -0
- package/bin/ner_direct_zh.py +100 -0
- package/bin/ner_pipeline.py +208 -0
- package/bin/noise-selfcheck.mjs +50 -0
- package/bin/pack-integrity.mjs +47 -0
- package/bin/panel-check.mjs +57 -0
- package/bin/plugin-integrity.mjs +74 -0
- package/bin/preview-live.mjs +13 -0
- package/bin/public-install-check.mjs +222 -0
- package/bin/relabel-apply.mjs +272 -0
- package/bin/relabel-dry-run.mjs +381 -0
- package/bin/relik_pipeline.py +39 -0
- package/bin/relik_service.py +52 -0
- package/bin/srl_pipeline.py +55 -0
- package/bin/srl_service.py +68 -0
- package/bin/summary-index.mjs +151 -0
- package/bin/summary-query.mjs +79 -0
- package/bin/vec-check.mjs +27 -0
- package/bin/zh_ner_error_audit.py +36 -0
- package/bin/zh_ner_slot_eval.py +38 -0
- package/client.js +760 -0
- package/cordis.patch.yml +378 -0
- package/docs/API-NOTES.md +1743 -0
- package/docs/ARCHITECTURE.md +355 -0
- package/docs/CACHE.md +253 -0
- package/docs/CONDUCT.md +30 -0
- package/docs/CREATOR-HANDOFF.md +102 -0
- package/docs/EXPERIMENT.md +136 -0
- package/docs/FACET-MVP-REPORT.md +473 -0
- package/docs/GUIDE-0.4.66.md +195 -0
- package/docs/GUIDE.md +127 -0
- package/docs/MECHANISM.md +243 -0
- package/docs/NEW-INSTANCE-HANDOFF.md +219 -0
- package/docs/README.md +37 -0
- package/docs/SESSION-LAYOUT.md +151 -0
- package/docs/history-map.md +249 -0
- package/docs/relabel-migration-wiring.md +366 -0
- package/guides/00-READ-FIRST.md +206 -0
- package/guides/01-INSTALL.md +207 -0
- package/guides/02-TABLES.md +225 -0
- package/guides/03-AUDIT.md +201 -0
- package/guides/04-REBUILD.md +306 -0
- package/guides/05-FIRST-RUN.md +191 -0
- package/guides/06-EMBEDDING.md +146 -0
- package/guides/07-PTC-AND-ASSETS.md +221 -0
- package/icon.svg +7 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/models/README.md +13 -0
- package/models/Xenova/bge-small-zh-v1.5/config.json +31 -0
- package/models/Xenova/bge-small-zh-v1.5/onnx/model_quantized.onnx +0 -0
- package/models/Xenova/bge-small-zh-v1.5/tokenizer.json +21278 -0
- package/models/Xenova/bge-small-zh-v1.5/tokenizer_config.json +15 -0
- package/package.json +139 -4
- package/src/asset-tool.mjs +237 -0
- package/src/assets.mjs +244 -0
- package/src/backfill.mjs +74 -0
- package/src/backup.mjs +80 -0
- package/src/deadend.mjs +95 -0
- package/src/deleted-store.mjs +35 -0
- package/src/engine.mjs +3831 -0
- package/src/facets/classifier.mjs +146 -0
- package/src/facets/embedding-service.mjs +53 -0
- package/src/facets/embedding-worker.mjs +27 -0
- package/src/facets/embedding.mjs +163 -0
- package/src/facets/extract.mjs +80 -0
- package/src/facets/fold-plan.mjs +84 -0
- package/src/facets/hybrid.mjs +29 -0
- package/src/facets/index.mjs +113 -0
- package/src/facets/linear-probe.mjs +43 -0
- package/src/facets/load.mjs +37 -0
- package/src/facets/mlp-probe.mjs +54 -0
- package/src/facets/mlp-service.mjs +53 -0
- package/src/facets/mlp-worker.mjs +26 -0
- package/src/facets/online-knn.mjs +58 -0
- package/src/facets/online-models.mjs +227 -0
- package/src/facets/panel-state.mjs +23 -0
- package/src/facets/panel.mjs +73 -0
- package/src/facets/random.mjs +26 -0
- package/src/facets/scanner.mjs +35 -0
- package/src/facets/schema.mjs +75 -0
- package/src/facets/seed-examples.mjs +58 -0
- package/src/facets/sensitivity.mjs +98 -0
- package/src/facets/service.mjs +53 -0
- package/src/facets/store.mjs +39 -0
- package/src/facets/worker.mjs +23 -0
- package/src/folds-store.mjs +109 -0
- package/src/groups.mjs +167 -0
- package/src/handoff-tool.mjs +51 -0
- package/src/handoff.mjs +127 -0
- package/src/index.mjs +649 -0
- package/src/inject-policy.mjs +17 -0
- package/src/learning.mjs +88 -0
- package/src/memory/alias-proposal.mjs +40 -0
- package/src/memory/bc-audit.mjs +245 -0
- package/src/memory/bc-index-updater.mjs +404 -0
- package/src/memory/bc-index.mjs +48 -0
- package/src/memory/bc-label.mjs +220 -0
- package/src/memory/bc-panel-state.mjs +107 -0
- package/src/memory/bc-pre-step.mjs +463 -0
- package/src/memory/bc-recall.mjs +470 -0
- package/src/memory/bc-shift.mjs +123 -0
- package/src/memory/bc-store.mjs +41 -0
- package/src/memory/bm25.mjs +112 -0
- package/src/memory/calibration.mjs +54 -0
- package/src/memory/chatter.mjs +29 -0
- package/src/memory/cmap-file.mjs +107 -0
- package/src/memory/concepts-file.mjs +41 -0
- package/src/memory/coref-slots.mjs +40 -0
- package/src/memory/event-alias-ledger.mjs +87 -0
- package/src/memory/event-log.mjs +95 -0
- package/src/memory/event-model.mjs +132 -0
- package/src/memory/event-segmenter.mjs +36 -0
- package/src/memory/event-summary-tree.mjs +122 -0
- package/src/memory/extraction-adapter.mjs +76 -0
- package/src/memory/extraction-composite.mjs +75 -0
- package/src/memory/extraction-core.mjs +76 -0
- package/src/memory/extraction-pre-step.mjs +161 -0
- package/src/memory/extraction-provider.mjs +300 -0
- package/src/memory/extraction-score.mjs +36 -0
- package/src/memory/facet-stabilizer.mjs +59 -0
- package/src/memory/history-map.mjs +139 -0
- package/src/memory/language-router.mjs +30 -0
- package/src/memory/load-gate.mjs +161 -0
- package/src/memory/mechanical-note.mjs +104 -0
- package/src/memory/onboarding.mjs +163 -0
- package/src/memory/persona.mjs +78 -0
- package/src/memory/planner.mjs +249 -0
- package/src/memory/pre-turn.mjs +80 -0
- package/src/memory/query-aliases.mjs +39 -0
- package/src/memory/recall-cue.mjs +43 -0
- package/src/memory/recall.mjs +65 -0
- package/src/memory/referent-candidates.mjs +55 -0
- package/src/memory/referent-ledger.mjs +86 -0
- package/src/memory/relabel-migrate.mjs +376 -0
- package/src/memory/relik-adapter.mjs +25 -0
- package/src/memory/special-keywords.mjs +60 -0
- package/src/memory/structured-adapters.mjs +75 -0
- package/src/memory/summary-index.mjs +335 -0
- package/src/memory/summary-tree.mjs +110 -0
- package/src/memory/term-extractor.mjs +44 -0
- package/src/memory/tokenize.mjs +9 -0
- package/src/memory/vector-index.mjs +54 -0
- package/src/memory/window-summary.mjs +133 -0
- package/src/mood.mjs +133 -0
- package/src/peer-tool.mjs +63 -0
- package/src/peer.mjs +144 -0
- package/src/policy.mjs +170 -0
- package/src/preflight.mjs +113 -0
- package/src/provenance.mjs +94 -0
- package/src/range.mjs +75 -0
- package/src/recall-exact-tool.mjs +267 -0
- package/src/recall-tool.mjs +521 -0
- package/src/recheck.mjs +99 -0
- package/src/save-start.mjs +29 -0
- package/src/session-guard.mjs +140 -0
- package/src/session-log-tool.mjs +322 -0
- package/src/session-log.mjs +606 -0
- package/src/skills.mjs +111 -0
- package/src/split-range.mjs +115 -0
- package/src/state.mjs +198 -0
- package/src/t0.mjs +149 -0
- package/src/tag-confirm.mjs +78 -0
- package/src/tag-detect.mjs +42 -0
- package/src/tag-maintenance.mjs +285 -0
- package/src/tag-reject.mjs +73 -0
- package/src/tags-store.mjs +94 -0
- package/src/tags.mjs +692 -0
- package/src/threads-backend.mjs +26 -0
- package/src/threads-core.mjs +1887 -0
- package/src/threads-store.mjs +207 -0
- package/src/threads-tool.mjs +522 -0
- package/src/threads.mjs +929 -0
- package/src/tool-preview.mjs +427 -0
- package/src/topic.mjs +63 -0
- package/src/training.mjs +75 -0
- package/src/turn-tag.mjs +62 -0
- package/src/user-turn.mjs +39 -0
- package/templates/cmap.md +11 -0
- package/templates/concepts.md +12 -0
- package/templates/habits.md +40 -0
- package/templates/keywords.md +6 -0
- package/templates/rebuild.md +134 -0
- package/templates/rules-safe.md +184 -0
- package/templates/rules.md +177 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Public install read gate.
|
|
4
|
+
*
|
|
5
|
+
* The package manager cannot force a human to read a file. This script is the
|
|
6
|
+
* supported path's gate: it asks five comprehension questions, and only writes
|
|
7
|
+
* an install acknowledgement when all answers are correct. `--verify` checks
|
|
8
|
+
* that the ack still matches the docs and package version.
|
|
9
|
+
*/
|
|
10
|
+
import { createHash } from 'node:crypto';
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
import { dirname, join } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import { createInterface } from 'node:readline';
|
|
16
|
+
|
|
17
|
+
const root = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
18
|
+
const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
|
|
19
|
+
const docs = {
|
|
20
|
+
baseRules: join(root, 'BASE-RULES.md'),
|
|
21
|
+
install: join(root, 'INSTALL.md'),
|
|
22
|
+
agentsTemplate: join(root, 'templates', 'AGENTS-continuity.md'),
|
|
23
|
+
rulesTemplate: join(root, 'templates', 'rules.md'),
|
|
24
|
+
habitsTemplate: join(root, 'templates', 'habits.md'),
|
|
25
|
+
};
|
|
26
|
+
const home = process.env.DSH_CONTINUITY_HOME
|
|
27
|
+
? process.env.DSH_CONTINUITY_HOME
|
|
28
|
+
: join(homedir(), '.dsh', 'continuity');
|
|
29
|
+
const agentsFile = join(homedir(), '.dsh', 'AGENTS.md');
|
|
30
|
+
const ackFile = join(home, 'public-install-ack.json');
|
|
31
|
+
const sha256 = (file) => createHash('sha256').update(readFileSync(file)).digest('hex');
|
|
32
|
+
|
|
33
|
+
function readDocs() {
|
|
34
|
+
for (const file of Object.values(docs)) {
|
|
35
|
+
if (!existsSync(file)) {
|
|
36
|
+
console.error(`[read-gate] 缺少必读/模板文件:${file}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
baseRules: sha256(docs.baseRules),
|
|
42
|
+
install: sha256(docs.install),
|
|
43
|
+
agentsTemplate: sha256(docs.agentsTemplate),
|
|
44
|
+
rulesTemplate: sha256(docs.rulesTemplate),
|
|
45
|
+
habitsTemplate: sha256(docs.habitsTemplate),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function verify() {
|
|
50
|
+
const expected = readDocs();
|
|
51
|
+
if (!existsSync(ackFile)) {
|
|
52
|
+
console.error(`[read-gate] 没有 ack:${ackFile}`);
|
|
53
|
+
console.error('[read-gate] 先运行 node bin/public-install-check.mjs,读完并通过 9 题。');
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
let ack;
|
|
57
|
+
try { ack = JSON.parse(readFileSync(ackFile, 'utf8')); } catch (error) {
|
|
58
|
+
console.error(`[read-gate] ack 无法解析:${error.message}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
const problems = [];
|
|
62
|
+
if (ack.version !== pkg.version) problems.push(`version ${ack.version} != ${pkg.version}`);
|
|
63
|
+
for (const key of ['baseRules', 'install', 'agentsTemplate', 'rulesTemplate', 'habitsTemplate']) {
|
|
64
|
+
if (ack.docs?.[key] !== expected[key]) problems.push(`${key} 已变化`);
|
|
65
|
+
}
|
|
66
|
+
if (!existsSync(agentsFile)) problems.push('AGENTS.md 不存在');
|
|
67
|
+
else {
|
|
68
|
+
try {
|
|
69
|
+
const agentsText = readFileSync(agentsFile, 'utf8');
|
|
70
|
+
if (!agentsText.includes('## Continuity 的 rules.md')
|
|
71
|
+
&& !agentsText.includes('## Continuity 的规则层')) {
|
|
72
|
+
problems.push('AGENTS.md 缺少 Continuity 指针');
|
|
73
|
+
}
|
|
74
|
+
} catch { problems.push('AGENTS.md 不可读'); }
|
|
75
|
+
}
|
|
76
|
+
for (const file of ['rules.md', 'habits.md']) {
|
|
77
|
+
if (!existsSync(join(home, file))) problems.push(`${file} 不存在(先跑 install-templates)`);
|
|
78
|
+
}
|
|
79
|
+
if (problems.length > 0) {
|
|
80
|
+
console.error('[read-gate] ack/安装失效:' + problems.join(';'));
|
|
81
|
+
console.error('[read-gate] 先跑 node bin/install-templates.mjs --apply,再重新运行本脚本。');
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
console.log(`[read-gate] ack 有效:${ackFile}`);
|
|
85
|
+
console.log('[read-gate] AGENTS 指针、rules/habits 模板均存在。');
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (process.argv.includes('--verify')) verify();
|
|
90
|
+
|
|
91
|
+
const hashes = readDocs();
|
|
92
|
+
console.log('');
|
|
93
|
+
console.log('Continuity public read gate');
|
|
94
|
+
console.log(`version: ${pkg.version}`);
|
|
95
|
+
console.log('');
|
|
96
|
+
console.log('安装前必须完整阅读:');
|
|
97
|
+
console.log(` 1. ${docs.baseRules}`);
|
|
98
|
+
console.log(` 2. ${docs.install}`);
|
|
99
|
+
console.log('');
|
|
100
|
+
console.log('这不是形式:下面 9 题全对才会写 ack。答错不会写任何东西。');
|
|
101
|
+
console.log('');
|
|
102
|
+
|
|
103
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
104
|
+
let inputClosed = false;
|
|
105
|
+
const lineQueue = [];
|
|
106
|
+
const waiters = [];
|
|
107
|
+
rl.on('line', (line) => {
|
|
108
|
+
const waiter = waiters.shift();
|
|
109
|
+
if (waiter !== undefined) waiter(line);
|
|
110
|
+
else lineQueue.push(line);
|
|
111
|
+
});
|
|
112
|
+
rl.on('close', () => {
|
|
113
|
+
inputClosed = true;
|
|
114
|
+
while (waiters.length > 0) waiters.shift()('');
|
|
115
|
+
});
|
|
116
|
+
// A line queue (not rl.question) so piped input and TTY input behave the same.
|
|
117
|
+
const ask = (prompt) => {
|
|
118
|
+
process.stdout.write(prompt);
|
|
119
|
+
if (lineQueue.length > 0) return Promise.resolve(lineQueue.shift());
|
|
120
|
+
if (inputClosed) return Promise.resolve('');
|
|
121
|
+
return new Promise((resolve) => waiters.push(resolve));
|
|
122
|
+
};
|
|
123
|
+
let exitCode = 0;
|
|
124
|
+
try {
|
|
125
|
+
await ask('读完后按 Enter 开始:');
|
|
126
|
+
const questions = [
|
|
127
|
+
{
|
|
128
|
+
text: '折叠/压缩之后,原文在哪里?',
|
|
129
|
+
options: ['A. 被删除', 'B. 存档里,可用取回读取', 'C. 只在摘要里'],
|
|
130
|
+
correct: 'B',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
text: '这个 public 包含跨会话投递 / peer_message 吗?',
|
|
134
|
+
options: ['A. 包含', 'B. 不包含'],
|
|
135
|
+
correct: 'B',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
text: '安装前第一件事是什么?',
|
|
139
|
+
options: ['A. 直接装', 'B. 备份、读完、跑 read gate', 'C. 清空 ~/.dsh'],
|
|
140
|
+
correct: 'B',
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
text: '线程/标签默认跨会话共享吗?',
|
|
144
|
+
options: ['A. 共享', 'B. 按会话本地存储'],
|
|
145
|
+
correct: 'B',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
text: '装坏之后正确做法是什么?',
|
|
149
|
+
options: ['A. 删掉 ~/.dsh/continuity', 'B. 移除插件并恢复备份', 'C. 继续装直到好'],
|
|
150
|
+
correct: 'B',
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
text: '子代理能看到完整记忆/线头吗?',
|
|
154
|
+
options: ['A. 能', 'B. 不能,只看到被给的一小块'],
|
|
155
|
+
correct: 'B',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
text: '看到 [候选]/[话题?] 里当前 T 以外的标签,最符合规则的做法是?',
|
|
159
|
+
options: [
|
|
160
|
+
'A. 忽略,自动归位已经完成',
|
|
161
|
+
'B. 当场判;更像另一条线就 load 过去',
|
|
162
|
+
'C. 只把末行 TAG 改成它,不切',
|
|
163
|
+
],
|
|
164
|
+
correct: 'B',
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
text: '想开一个新话题,正确的 TAG 写法是?',
|
|
168
|
+
options: ['A. TAG: 新词', 'B. TAG: NEW:新词', 'C. TAG: "新词"'],
|
|
169
|
+
correct: 'B',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
text: '安装后还必须做什么,否则规则层是断的?',
|
|
173
|
+
options: [
|
|
174
|
+
'A. 直接开始聊天',
|
|
175
|
+
'B. 合并 AGENTS 指针,并复制 rules/habits 模板',
|
|
176
|
+
'C. 删除 AGENTS.md',
|
|
177
|
+
],
|
|
178
|
+
correct: 'B',
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
const answers = [];
|
|
182
|
+
for (let index = 0; index < questions.length; index += 1) {
|
|
183
|
+
const item = questions[index];
|
|
184
|
+
console.log(`Q${index + 1}. ${item.text}`);
|
|
185
|
+
for (const option of item.options) console.log(` ${option}`);
|
|
186
|
+
const answer = (await ask('你的答案(A/B/C):')).trim().toUpperCase();
|
|
187
|
+
if (!['A', 'B', 'C'].includes(answer)) {
|
|
188
|
+
console.error(`Q${index + 1}: 只接受 A/B/C。`);
|
|
189
|
+
exitCode = 1;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
answers.push(answer);
|
|
193
|
+
if (answer !== item.correct) {
|
|
194
|
+
console.error('');
|
|
195
|
+
console.error(`Q${index + 1} 不对。没有写 ack。`);
|
|
196
|
+
console.error('回去读 BASE-RULES.md 的“机制事实”和 INSTALL.md 的“安装前/回滚”部分,再运行。');
|
|
197
|
+
exitCode = 1;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
console.log('');
|
|
201
|
+
}
|
|
202
|
+
if (exitCode === 0) {
|
|
203
|
+
mkdirSync(home, { recursive: true });
|
|
204
|
+
const ack = {
|
|
205
|
+
version: pkg.version,
|
|
206
|
+
docs: hashes,
|
|
207
|
+
answers,
|
|
208
|
+
at: new Date().toISOString(),
|
|
209
|
+
};
|
|
210
|
+
writeFileSync(ackFile, `${JSON.stringify(ack, null, 2)}\n`, 'utf8');
|
|
211
|
+
console.log(`[read-gate] 通过,已写:${ackFile}`);
|
|
212
|
+
console.log('');
|
|
213
|
+
console.log('下一步(确认路径后执行):');
|
|
214
|
+
console.log(` node "${join(root, 'bin', 'install-templates.mjs')}" --apply`);
|
|
215
|
+
console.log(` dsh plugin --profile <profile> add link:${root}`);
|
|
216
|
+
console.log('');
|
|
217
|
+
console.log(`安装后复核:node "${join(root, 'bin', 'public-install-check.mjs')}" --verify`);
|
|
218
|
+
}
|
|
219
|
+
} finally {
|
|
220
|
+
rl.close();
|
|
221
|
+
}
|
|
222
|
+
process.exitCode = exitCode;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* relabel-apply.mjs — 历史重打(**只改 topic 字段**)的写盘版。
|
|
4
|
+
*
|
|
5
|
+
* 默认是演练:不带 --apply 时只算 diff、不写任何文件。
|
|
6
|
+
* 带 --apply 时必须先自动备份 bc-index.jsonl(--no-backup 才能关,**不建议**)。
|
|
7
|
+
*
|
|
8
|
+
* 只改这四个字段:
|
|
9
|
+
* genericTopic / topicScore / topicConfidence / topicMinScore
|
|
10
|
+
* 其余字段(generic、genericWorkflow、terms、keywords、conceptScores、vec、seq…)**原样保留**。
|
|
11
|
+
* workflow 侧不动(等 baseline 够 100+ 再说)。
|
|
12
|
+
*
|
|
13
|
+
* 用法:
|
|
14
|
+
* node bin/relabel-apply.mjs --index <bc-index.jsonl> --raw <session.jsonl[.zstd]>
|
|
15
|
+
* [--concepts <concepts.md>] [--baseline-file <file>] [--apply] [--no-backup]
|
|
16
|
+
* [--center-min 0.02] [--lead 0.02] [--topic-min-score 0.33] [--json]
|
|
17
|
+
*/
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import zlib from 'node:zlib';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
|
|
23
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
const pkgRoot = path.resolve(here, '..');
|
|
25
|
+
|
|
26
|
+
const argv = process.argv.slice(2);
|
|
27
|
+
const opt = (name, fallback = null) => {
|
|
28
|
+
const i = argv.indexOf(`--${name}`);
|
|
29
|
+
if (i < 0) return fallback;
|
|
30
|
+
const next = argv[i + 1];
|
|
31
|
+
return next === undefined || next.startsWith('--') ? true : next;
|
|
32
|
+
};
|
|
33
|
+
const num = (name, fallback) => {
|
|
34
|
+
const v = opt(name);
|
|
35
|
+
if (v === null || v === true) return fallback;
|
|
36
|
+
const n = Number(v);
|
|
37
|
+
return Number.isFinite(n) ? n : fallback;
|
|
38
|
+
};
|
|
39
|
+
const flag = (name) => argv.includes(`--${name}`);
|
|
40
|
+
|
|
41
|
+
const indexFile = opt('index');
|
|
42
|
+
const rawFile = opt('raw');
|
|
43
|
+
if (!indexFile || indexFile === true || !rawFile || rawFile === true) {
|
|
44
|
+
console.error('用法:node bin/relabel-apply.mjs --index <bc-index.jsonl> --raw <session.jsonl[.zstd]> [--apply]');
|
|
45
|
+
process.exit(2);
|
|
46
|
+
}
|
|
47
|
+
const apply = flag('apply');
|
|
48
|
+
const noBackup = flag('no-backup');
|
|
49
|
+
const asJson = flag('json');
|
|
50
|
+
|
|
51
|
+
const conceptsPath = (() => {
|
|
52
|
+
const explicit = opt('concepts');
|
|
53
|
+
if (explicit && explicit !== true) return explicit;
|
|
54
|
+
const candidate = path.join(pkgRoot, 'templates', 'concepts.md');
|
|
55
|
+
return fs.existsSync(candidate) ? candidate : null;
|
|
56
|
+
})();
|
|
57
|
+
if (!conceptsPath || !fs.existsSync(conceptsPath)) {
|
|
58
|
+
console.error('找不到 A 表:请用 --concepts 指定');
|
|
59
|
+
process.exit(2);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const centerMin = num('center-min', 0.02);
|
|
63
|
+
const lead = num('lead', 0.02);
|
|
64
|
+
const topicMinScore = num('topic-min-score', 0.33);
|
|
65
|
+
const minScore = num('min-score', 0.25);
|
|
66
|
+
const baselineFile = opt('baseline-file');
|
|
67
|
+
|
|
68
|
+
const { createBLabeler } = await import('file://' + path.join(pkgRoot, 'src/memory/bc-label.mjs'));
|
|
69
|
+
const { loadConcepts } = await import('file://' + path.join(pkgRoot, 'src/memory/concepts-file.mjs'));
|
|
70
|
+
const { embedTexts } = await import('file://' + path.join(pkgRoot, 'src/facets/embedding.mjs'));
|
|
71
|
+
const { eventText } = await import('file://' + path.join(pkgRoot, 'src/session-log.mjs'));
|
|
72
|
+
|
|
73
|
+
const INJECTED = /^\[(?:抽取|工作线|标签|规则|习惯|技能|归位|候选|存\?|闸|回正|学习|追忆|记忆|continuity:[a-z-]+)\]/;
|
|
74
|
+
const MACHINE = /^(?:Current runtime context|Current working directory|Current DSH file policy|Approval prompts|Time sampled)/;
|
|
75
|
+
|
|
76
|
+
const conceptRows = loadConcepts(conceptsPath);
|
|
77
|
+
const labels = Object.fromEntries(conceptRows.map((r) => [r.label, r.description]));
|
|
78
|
+
const kinds = Object.fromEntries(conceptRows.map((r) => [r.label, r.kind]));
|
|
79
|
+
const concepts = conceptRows.map((r) => r.label);
|
|
80
|
+
const topics = conceptRows.filter((r) => r.kind === 'topic').map((r) => r.label);
|
|
81
|
+
|
|
82
|
+
// ---------- raw ----------
|
|
83
|
+
function readRawLines(file) {
|
|
84
|
+
if (!/\.zstd$/i.test(file)) return fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, '').split(/\r?\n/).filter(Boolean);
|
|
85
|
+
const buf = fs.readFileSync(file);
|
|
86
|
+
const MAGIC = Buffer.from([0x28, 0xb5, 0x2f, 0xfd]);
|
|
87
|
+
const starts = [];
|
|
88
|
+
for (let i = 0; ; ) {
|
|
89
|
+
const k = buf.indexOf(MAGIC, i);
|
|
90
|
+
if (k < 0) break;
|
|
91
|
+
starts.push(k);
|
|
92
|
+
i = k + 4;
|
|
93
|
+
}
|
|
94
|
+
const lines = [];
|
|
95
|
+
for (const start of starts) {
|
|
96
|
+
let out;
|
|
97
|
+
try { out = zlib.zstdDecompressSync(buf.subarray(start)); } catch { continue; }
|
|
98
|
+
for (const line of out.toString('utf8').split(/\r?\n/)) if (line) lines.push(line);
|
|
99
|
+
}
|
|
100
|
+
return lines;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const events = [];
|
|
104
|
+
const seenSeq = new Set();
|
|
105
|
+
for (const line of readRawLines(rawFile)) {
|
|
106
|
+
let event;
|
|
107
|
+
try { event = JSON.parse(line.replace(/^\uFEFF/, '')); } catch { continue; }
|
|
108
|
+
const seq = Number(event?.seq);
|
|
109
|
+
if (Number.isInteger(seq)) { if (seenSeq.has(seq)) continue; seenSeq.add(seq); }
|
|
110
|
+
events.push(event);
|
|
111
|
+
}
|
|
112
|
+
events.sort((a, b) => Number(a.seq) - Number(b.seq));
|
|
113
|
+
|
|
114
|
+
const markers = events
|
|
115
|
+
.map((event) => ({ seq: Number(event?.seq), turn: Number(event?.data?.turn) }))
|
|
116
|
+
.filter((m) => Number.isInteger(m.seq) && Number.isInteger(m.turn))
|
|
117
|
+
.sort((a, b) => a.seq - b.seq);
|
|
118
|
+
const turnAt = (seq) => {
|
|
119
|
+
let found = null;
|
|
120
|
+
for (const marker of markers) { if (marker.seq > seq) break; found = marker.turn; }
|
|
121
|
+
return found ?? markers.find((m) => m.seq >= seq)?.turn ?? null;
|
|
122
|
+
};
|
|
123
|
+
const isHuman = (event) => {
|
|
124
|
+
const source = event?.data?.source ?? event?.source;
|
|
125
|
+
if (source && typeof source.kind === 'string' && source.kind !== 'user') return false;
|
|
126
|
+
if (source && source.peer === true) return false;
|
|
127
|
+
const text = eventText(event, { includeReasoning: false }).trim();
|
|
128
|
+
return Boolean(text) && !INJECTED.test(text) && !MACHINE.test(text);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const turns = new Map();
|
|
132
|
+
for (const event of events) {
|
|
133
|
+
const type = event?.type;
|
|
134
|
+
if (type !== 'user/message' && type !== 'assistant/message') continue;
|
|
135
|
+
if (type === 'user/message' && !isHuman(event)) continue;
|
|
136
|
+
const text = eventText(event, { includeReasoning: false }).trim();
|
|
137
|
+
if (!text || INJECTED.test(text) || MACHINE.test(text)) continue;
|
|
138
|
+
const seq = Number(event?.seq);
|
|
139
|
+
const turn = Number.isInteger(Number(event?.data?.turn)) ? Number(event.data.turn) : turnAt(seq);
|
|
140
|
+
if (!Number.isInteger(turn)) continue;
|
|
141
|
+
const item = turns.get(turn) ?? { turn, userTexts: [], assistantTexts: [] };
|
|
142
|
+
if (type === 'user/message') item.userTexts.push(text);
|
|
143
|
+
else item.assistantTexts.push(text);
|
|
144
|
+
turns.set(turn, item);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ---------- baseline(v2 / v1 / flat) ----------
|
|
148
|
+
let baseline = null;
|
|
149
|
+
let baselineSource = 'none(raw-bootstrap)';
|
|
150
|
+
if (baselineFile && baselineFile !== true) {
|
|
151
|
+
const raw = JSON.parse(fs.readFileSync(baselineFile, 'utf8').replace(/^\uFEFF/, ''));
|
|
152
|
+
const source = raw?.topics && typeof raw.topics === 'object'
|
|
153
|
+
? raw.topics
|
|
154
|
+
: (raw?.samples && typeof raw.samples === 'object' ? raw.samples : raw);
|
|
155
|
+
const map = Object.fromEntries(topics.map((t) => {
|
|
156
|
+
const value = source?.[t];
|
|
157
|
+
if (Array.isArray(value)) {
|
|
158
|
+
const nums = value.map((x) => Number(x)).filter((x) => Number.isFinite(x));
|
|
159
|
+
return [t, nums.length > 0 ? nums.reduce((a, b) => a + b, 0) / nums.length : Number.NaN];
|
|
160
|
+
}
|
|
161
|
+
if (value === undefined || value === null || value === '') return [t, Number.NaN];
|
|
162
|
+
const n = Number(value);
|
|
163
|
+
return [t, Number.isFinite(n) ? n : Number.NaN];
|
|
164
|
+
}));
|
|
165
|
+
const known = Object.values(map).filter((v) => Number.isFinite(v));
|
|
166
|
+
if (known.length > 0) {
|
|
167
|
+
const fallback = known.reduce((a, b) => a + b, 0) / known.length;
|
|
168
|
+
for (const t of topics) if (!Number.isFinite(map[t])) map[t] = fallback;
|
|
169
|
+
baseline = map;
|
|
170
|
+
baselineSource = `file:${baselineFile}`;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ---------- 逐行重打 topic ----------
|
|
175
|
+
const rows = fs.readFileSync(indexFile, 'utf8').replace(/^\uFEFF/, '').split(/\r?\n/).filter((l) => l.trim() !== '');
|
|
176
|
+
const labeler = createBLabeler({ embed: embedTexts, labels, concepts, kinds });
|
|
177
|
+
const updated = [];
|
|
178
|
+
const stats = { rows: rows.length, relabelled: 0, unresolved: 0, topicChanged: 0, toEmpty: 0, fromEmpty: 0, same: 0 };
|
|
179
|
+
const samples = [];
|
|
180
|
+
|
|
181
|
+
for (const line of rows) {
|
|
182
|
+
const record = JSON.parse(line);
|
|
183
|
+
const turn = Number(record.turn);
|
|
184
|
+
const entry = Number.isInteger(turn) ? turns.get(turn) : null;
|
|
185
|
+
if (!entry) {
|
|
186
|
+
stats.unresolved += 1;
|
|
187
|
+
updated.push(line); // 原样保留
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const text = [...entry.userTexts, ...entry.assistantTexts].join('\n');
|
|
191
|
+
const res = await labeler.labelChunked(text, {
|
|
192
|
+
topK: 3,
|
|
193
|
+
minScore,
|
|
194
|
+
topicMinScore,
|
|
195
|
+
topicCenterMin: centerMin,
|
|
196
|
+
topicLead: lead,
|
|
197
|
+
topicTopK: 1,
|
|
198
|
+
topicBaseline: baseline ?? undefined,
|
|
199
|
+
});
|
|
200
|
+
const newTopic = (res.topic ?? [])[0]?.concept ?? null;
|
|
201
|
+
const newScore = newTopic ? Number(res.topic?.[0]?.score ?? res.topicScore ?? 0) : null;
|
|
202
|
+
const newConfidence = newTopic ? (res.topicConfidence ?? null) : null;
|
|
203
|
+
const oldTopic = Array.isArray(record.genericTopic) && record.genericTopic.length > 0 ? record.genericTopic[0] : null;
|
|
204
|
+
|
|
205
|
+
// 按旧字段顺序重建,只替换四个打标字段
|
|
206
|
+
const next = {};
|
|
207
|
+
for (const [key, value] of Object.entries(record)) {
|
|
208
|
+
if (key === 'genericTopic' || key === 'topicScore' || key === 'topicConfidence' || key === 'topicMinScore') continue;
|
|
209
|
+
next[key] = value;
|
|
210
|
+
}
|
|
211
|
+
next.genericTopic = newTopic ? [newTopic] : [];
|
|
212
|
+
if (newTopic) {
|
|
213
|
+
next.topicScore = Number(newScore.toFixed(6));
|
|
214
|
+
if (newConfidence) next.topicConfidence = newConfidence;
|
|
215
|
+
}
|
|
216
|
+
next.topicMinScore = topicMinScore;
|
|
217
|
+
|
|
218
|
+
if (oldTopic !== newTopic) {
|
|
219
|
+
stats.topicChanged += 1;
|
|
220
|
+
if (oldTopic && !newTopic) stats.toEmpty += 1;
|
|
221
|
+
if (!oldTopic && newTopic) stats.fromEmpty += 1;
|
|
222
|
+
samples.push({ turn, oldTopic, newTopic });
|
|
223
|
+
} else {
|
|
224
|
+
stats.same += 1;
|
|
225
|
+
}
|
|
226
|
+
stats.relabelled += 1;
|
|
227
|
+
updated.push(JSON.stringify(next));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const result = { apply, baselineSource, stats, samples: samples.slice(0, 40) };
|
|
231
|
+
|
|
232
|
+
if (!apply) {
|
|
233
|
+
if (asJson) console.log(JSON.stringify(result, null, 2));
|
|
234
|
+
else {
|
|
235
|
+
console.log(` 演练(不写盘)· index ${stats.rows} 行 · 可重打 ${stats.relabelled} · unresolved ${stats.unresolved}`);
|
|
236
|
+
console.log(` 换 topic ${stats.topicChanged}(其中 topic→空 ${stats.toEmpty} · 空→topic ${stats.fromEmpty})· 不变 ${stats.same}`);
|
|
237
|
+
console.log(` baseline ${baselineSource}`);
|
|
238
|
+
for (const s of result.samples.slice(0, 12)) console.log(` turn ${s.turn}: ${s.oldTopic ?? '(空)'} → ${s.newTopic ?? '(空)'}`);
|
|
239
|
+
}
|
|
240
|
+
process.exit(0);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ---------- 写盘 ----------
|
|
244
|
+
const backup = `${indexFile}.bak-${new Date().toISOString().replace(/[-:.TZ]/g, '').slice(0, 14)}`;
|
|
245
|
+
if (!noBackup) {
|
|
246
|
+
fs.copyFileSync(indexFile, backup);
|
|
247
|
+
console.log(` 已备份 → ${backup}(${fs.statSync(backup).size} B)`);
|
|
248
|
+
} else {
|
|
249
|
+
console.log(' ⚠ --no-backup:未备份');
|
|
250
|
+
}
|
|
251
|
+
const tmp = `${indexFile}.tmp-${process.pid}`;
|
|
252
|
+
fs.writeFileSync(tmp, updated.join('\n') + '\n', 'utf8');
|
|
253
|
+
|
|
254
|
+
// 校验:行数一致、每行可解析、字段完整、非目标字段未变
|
|
255
|
+
const before = rows.map((l) => JSON.parse(l));
|
|
256
|
+
const after = fs.readFileSync(tmp, 'utf8').split(/\r?\n/).filter(Boolean).map((l) => JSON.parse(l));
|
|
257
|
+
if (after.length !== before.length) throw new Error(`行数不一致 ${before.length} → ${after.length}`);
|
|
258
|
+
const target = new Set(['genericTopic', 'topicScore', 'topicConfidence', 'topicMinScore']);
|
|
259
|
+
let touchedOther = 0;
|
|
260
|
+
for (let i = 0; i < before.length; i += 1) {
|
|
261
|
+
const a = before[i];
|
|
262
|
+
const b = after[i];
|
|
263
|
+
for (const key of Object.keys(a)) {
|
|
264
|
+
if (target.has(key)) continue;
|
|
265
|
+
if (JSON.stringify(a[key]) !== JSON.stringify(b[key])) touchedOther += 1;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (touchedOther > 0) throw new Error(`非目标字段被改动 ${touchedOther} 处,已中止(原文件未动)`);
|
|
269
|
+
fs.renameSync(tmp, indexFile);
|
|
270
|
+
console.log(` 已写盘:${indexFile}`);
|
|
271
|
+
console.log(` 重打 ${stats.relabelled} · unresolved ${stats.unresolved} · 换 topic ${stats.topicChanged}(→空 ${stats.toEmpty} · 空→topic ${stats.fromEmpty})· 不变 ${stats.same}`);
|
|
272
|
+
console.log(' 校验通过:行数一致、非目标字段零改动');
|