@nightpainters/dsh-ling 1.5.2 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ACCESS-DESIGN.md +4 -3
- package/CHANGELOG.md +150 -9
- package/DESIGN.md +32 -11
- package/README.md +194 -10
- package/lib/audit/access-log.js +46 -12
- package/lib/audit/index.js +2 -2
- package/lib/audit/patch-spec.js +1 -1
- package/lib/client.js +319 -69
- package/lib/host/api.js +602 -60
- package/lib/host/archive.js +15 -9
- package/lib/host/autotree.js +2 -2
- package/lib/host/backfill.js +8 -2
- package/lib/host/clock-skill.js +1 -1
- package/lib/host/deep-index-skill.js +111 -0
- package/lib/host/feedback.js +112 -2
- package/lib/host/freeze.js +52 -1
- package/lib/host/guard.js +38 -1
- package/lib/host/habit-gen.js +12 -4
- package/lib/host/import-file.js +62 -6
- package/lib/host/import-log.js +80 -2
- package/lib/host/inject.js +1 -1
- package/lib/host/l1.js +350 -23
- package/lib/host/lifecycle.js +76 -10
- package/lib/host/memory.js +483 -48
- package/lib/host/mode-service.js +85 -0
- package/lib/host/mode.js +29 -9
- package/lib/host/persona.js +2 -2
- package/lib/host/rawlog.js +317 -0
- package/lib/host/recall-search.js +210 -0
- package/lib/host/retitle.js +11 -5
- package/lib/host/session-line.js +46 -6
- package/lib/host/snapshot.js +65 -13
- package/lib/host/summarizer.js +164 -2
- package/lib/host/surgery.js +600 -0
- package/lib/host/tools.js +339 -15
- package/lib/host/tree-ops.js +13 -7
- package/lib/host/util.js +1 -1
- package/lib/index.js +58 -31
- package/package.json +6 -2
- package/tools/apply-access-log-patch.mjs +1 -1
package/lib/index.js
CHANGED
|
@@ -2,20 +2,22 @@
|
|
|
2
2
|
// Skeleton wiring: settings file, sqlite memory, freeze gate, system-prompt
|
|
3
3
|
// injection, lifecycle listeners, loopback HTTP gateway.
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
-
import { readFileSync
|
|
6
|
-
import { svc, pick, ensureDir, defaultDataDir, utcIso
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { svc, pick, ensureDir, defaultDataDir, utcIso } from './host/util.js';
|
|
7
7
|
import { SettingsFile } from './host/settings-file.js';
|
|
8
8
|
import { MemoryStore } from './host/memory.js';
|
|
9
9
|
import { FreezeGate } from './host/freeze.js';
|
|
10
10
|
import { registerMemorySection } from './host/inject.js';
|
|
11
11
|
import { wireSessionLine } from './host/session-line.js';
|
|
12
12
|
import { wireLifecycle } from './host/lifecycle.js';
|
|
13
|
+
import { registerModeService } from './host/mode-service.js';
|
|
13
14
|
import { registerApi } from './host/api.js';
|
|
14
15
|
import { summarizeDsh } from './host/summarizer.js';
|
|
15
|
-
import {
|
|
16
|
+
import { registerFeedbackListener } from './host/feedback.js';
|
|
16
17
|
import { probeLLM, runDeepPass, DEEP_CFG } from './host/deepsummary.js';
|
|
17
18
|
import { registerLingTools } from './host/tools.js';
|
|
18
19
|
import { registerClockSkill } from './host/clock-skill.js';
|
|
20
|
+
import { registerIndexSkill } from './host/deep-index-skill.js'; // 1.6 步 6:深层库索引层(一簇一行,走 skill)
|
|
19
21
|
import { installAccessLog } from './audit/index.js';
|
|
20
22
|
|
|
21
23
|
export const name = 'dsh-ling';
|
|
@@ -36,7 +38,39 @@ export function apply(ctx, config = {}) {
|
|
|
36
38
|
ensureDir(dataDir);
|
|
37
39
|
|
|
38
40
|
const settings = new SettingsFile(dataDir);
|
|
39
|
-
|
|
41
|
+
// ⚠️ F4(蓝队 2026-10-04 实测 + 主人 2026-10-04 拍板「按错误码分流」):这里此前**没有 try** ⇒
|
|
42
|
+
// 只要构造抛(典型:另一个进程持写锁 —— 升级形态实测 16,445ms 后抛 `database is locked`),
|
|
43
|
+
// 异常就一路冒到 `apply()` ⇒ **8 个工具全不在、人格注入也没了**,而且**没有任何可指认面**。
|
|
44
|
+
// 那是 **fail-dead** 不是 fail-closed(本项目对 fail-closed 的定义 = 拒绝 + 给判据,见 B-05)。
|
|
45
|
+
// 分流规则:
|
|
46
|
+
// · errcode **5 / 6**(BUSY / LOCKED)= "等一会儿就能开" ⇒ 同步有界重试 3 次 × 2 秒;
|
|
47
|
+
// · **其余**(8 READONLY / 11 CORRUPT / 打不开文件…)= "真的打不开" ⇒ **不重试**,直接降级;
|
|
48
|
+
// 仍失败 ⇒ **降级加载**:本插件不注册任何东西(别的插件不受影响),并留一条**可执行**的 error。
|
|
49
|
+
const sleepSync = (ms) => {
|
|
50
|
+
try { Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms); } catch { /* 退化:不睡 */ }
|
|
51
|
+
};
|
|
52
|
+
const isLockErr = (e) => Number(e?.errcode ?? e?.code ?? -1) === 5 || Number(e?.errcode ?? -1) === 6
|
|
53
|
+
|| /SQLITE_BUSY|SQLITE_LOCKED|database is locked/i.test(String(e?.message ?? e));
|
|
54
|
+
let memory = null;
|
|
55
|
+
let memoryErr = null;
|
|
56
|
+
for (let attempt = 0; attempt < 3 && !memory; attempt += 1) {
|
|
57
|
+
try {
|
|
58
|
+
memory = new MemoryStore(join(dataDir, 'memory.db'));
|
|
59
|
+
} catch (e) {
|
|
60
|
+
memoryErr = e;
|
|
61
|
+
if (!isLockErr(e)) break; // 非锁类:立即放弃重试(照旧"真的打不开")
|
|
62
|
+
if (attempt < 2) sleepSync(2000);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!memory) {
|
|
66
|
+
const dbFile = join(dataDir, 'memory.db');
|
|
67
|
+
console.error('[dsh-ling] 记忆库打不开,**本次以降级形态加载**(记忆 / 人格注入 / 器灵工具面全部不可用;'
|
|
68
|
+
+ '其余插件不受影响)。原因:' + String(memoryErr?.message ?? memoryErr).slice(0, 300)
|
|
69
|
+
+ '\n 怎么排查:① 多半是别的进程正持写锁 —— 等几秒后重载宿主;'
|
|
70
|
+
+ '② 确认 ' + dbFile + ' 没被只读挂载或被独占;'
|
|
71
|
+
+ '③ 若文件损坏,先从 ' + dataDir + ' 下的备份恢复。');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
40
74
|
// invalidate 延迟绑定(避免环):onFlush 在事件时点才调用,彼时必已就绪
|
|
41
75
|
let invalidateNow = (sid) => {};
|
|
42
76
|
import('./host/inject.js').then((m) => {
|
|
@@ -62,12 +96,21 @@ export function apply(ctx, config = {}) {
|
|
|
62
96
|
// 也绝不调用带 surfaceOp 的 session.append。关掉开关:settings.memory.sessionProgressLine=false。
|
|
63
97
|
disposers.push(wireSessionLine(ctx, memory, settings, cfg.sessionLine || {}));
|
|
64
98
|
disposers.push(wireLifecycle(ctx, gate, memory, settings, cfg));
|
|
99
|
+
// P3-A(2026-09-29):把「给某个会话设模式」暴露成跨插件服务 `dsh-ling/mode` ——
|
|
100
|
+
// 飞书桥建线时用它把新会话钉到生活挡位。软能力:注册失败只记日志,不影响其余功能。
|
|
101
|
+
disposers.push(registerModeService(ctx, { gate, memory, settings }));
|
|
65
102
|
disposers.push(registerApi(ctx, { gate, memory, settings }, cfg));
|
|
66
103
|
// E4 可取证访问日志:只挂两个全局钩子,core 侧补丁留的调用点见 lib/audit/patch-spec.js。
|
|
67
104
|
// 补丁未打时钩子收不到调用 —— 安装器会在启动日志里明确喊出来,不允许静默不记。
|
|
68
105
|
disposers.push(installAccessLog(ctx, cfg.accessLog || {}));
|
|
69
|
-
// clock skill(精确时间按需查询,2026-09-23
|
|
106
|
+
// clock skill(精确时间按需查询,2026-09-23 尝生定):取服务走与 tools 同款的三级兜底。
|
|
70
107
|
disposers.push(...registerClockSkill(ctx));
|
|
108
|
+
// 深层库索引层 skill(1.6 步 6,2026-10-04):主人拍过「索引层 ≤400 字 · 一簇一行」,
|
|
109
|
+
// 承载形态选定为**整个索引层一个 skill** —— 目录只常驻 name + description(11 行簇目录),
|
|
110
|
+
// 正文按需展开;而 skill 目录按 sha256 比对、**内容不变就不重发** ⇒ 前缀稳定是白拿的。
|
|
111
|
+
// ⚠️ 不要拆成"每簇一个 skill":实测边际成本 ≈205 字/条,11 簇会把常驻目录面撑爆,
|
|
112
|
+
// 而那个池子正是工具面压缩要抢的同一个。
|
|
113
|
+
disposers.push(...registerIndexSkill(ctx));
|
|
71
114
|
// 会话内工具(规矩直达 / 习惯提议):拿不到 tools 服务就退化为"只能用面板",绝不抛错。
|
|
72
115
|
// 取服务用三级兜底(与 webServer 同一套路):直取 → ctx.inject 子 ctx → 定时重试。
|
|
73
116
|
// 2026-09-16 教训:本插件 apply 可能早于 tools 服务挂载,一次性直取会静默失败 —— 工具从未注册。
|
|
@@ -187,30 +230,19 @@ export function apply(ctx, config = {}) {
|
|
|
187
230
|
}, 8000);
|
|
188
231
|
}
|
|
189
232
|
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
233
|
+
// 反馈成长回路:事件驱动(U7 正解,2026-10-01)。
|
|
234
|
+
// ⚠️ 旧实现是"每 60 秒读一次 `~/.dsh/storages/message_feedback.json`",两处都错:
|
|
235
|
+
// ① 宿主**有推送** —— `dsh-message-feedback\lib\index.js:288` 在冷会话写入 flush 后
|
|
236
|
+
// 发 `feedback/committed`(活会话走 `session/event` 的 feedback 事件);
|
|
237
|
+
// ② 那个 sidecar 在 Web 组合下自 2026/9/11 起**零写入**(该组合只挂 message-feedback,
|
|
238
|
+
// 反馈已改写在会话日志里)⇒ 轮询每 60 秒读同一个死文件,纯空转。
|
|
239
|
+
// 现在:事件到达即处理;`feedbackAuto:false` 可整体关掉。
|
|
240
|
+
if (cfg.feedbackAuto !== false) {
|
|
194
241
|
try {
|
|
195
|
-
|
|
196
|
-
const fileObj = JSON.parse(readFileSync(fbFile, 'utf8'));
|
|
197
|
-
const entries = entriesFromFeedbackFile(fileObj);
|
|
198
|
-
if (!entries.length) return;
|
|
199
|
-
const seen = loadSeen(memory);
|
|
200
|
-
const res = ingestFeedbackEntries(memory, entries, { seen });
|
|
201
|
-
if (res.queued + res.boosted + res.seenAdded > 0) {
|
|
202
|
-
memory.kvSet('feedback.last', JSON.stringify({ at: utcIso(), ...res }));
|
|
203
|
-
}
|
|
242
|
+
disposers.push(registerFeedbackListener(ctx, memory));
|
|
204
243
|
} catch (e) {
|
|
205
|
-
console.debug('[dsh-ling] feedback
|
|
244
|
+
console.debug('[dsh-ling] feedback listener wiring failed', e);
|
|
206
245
|
}
|
|
207
|
-
};
|
|
208
|
-
if (cfg.feedbackAuto !== false) {
|
|
209
|
-
const pollSec = Math.max(20, Number(cfg.feedbackPollSec ?? 60));
|
|
210
|
-
fbTimer = setTimeout(() => {
|
|
211
|
-
pollFeedback();
|
|
212
|
-
fbTimer = setInterval(pollFeedback, pollSec * 1000);
|
|
213
|
-
}, 20000);
|
|
214
246
|
}
|
|
215
247
|
|
|
216
248
|
// LLM 深摘要:启动 12s 先自检探针;通过才挂自动调度(保守上限,可配)
|
|
@@ -237,11 +269,6 @@ export function apply(ctx, config = {}) {
|
|
|
237
269
|
clearInterval(sumTimer);
|
|
238
270
|
sumTimer = null;
|
|
239
271
|
}
|
|
240
|
-
if (fbTimer) {
|
|
241
|
-
clearTimeout(fbTimer);
|
|
242
|
-
clearInterval(fbTimer);
|
|
243
|
-
fbTimer = null;
|
|
244
|
-
}
|
|
245
272
|
if (deepTimer) {
|
|
246
273
|
clearInterval(deepTimer);
|
|
247
274
|
deepTimer = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nightpainters/dsh-ling",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "器灵 · 跑在你自己电脑上的记忆与人格助手插件:三层记忆、从历史里长出来的人格档案、定型锁与自由生长",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
|
-
"test": "node tests/unit.mjs && node tests/access-log.test.mjs && node tests/archive.test.mjs && node tests/autotree.test.mjs && node tests/backfill.test.mjs && node tests/branch.test.mjs && node tests/assistant.test.mjs && node tests/capture.test.mjs && node tests/clock.test.mjs && node tests/clock-skill.test.mjs && node tests/deep.test.mjs && node tests/dswebsum.test.mjs && node tests/duty.test.mjs && node tests/export.test.mjs && node tests/feedback.test.mjs && node tests/forget-backfill.test.mjs && node tests/genesis.test.mjs && node tests/guard.test.mjs && node tests/habitgen.test.mjs && node tests/idle.test.mjs && node tests/importfile.test.mjs && node tests/importlog.test.mjs && node tests/l1lines.test.mjs && node tests/memories.test.mjs && node tests/mode.test.mjs && node tests/mustache.test.mjs && node tests/retitle.test.mjs && node tests/rules.test.mjs && node tests/sessionline.test.mjs && node tests/suggest.test.mjs && node tests/summarize.test.mjs && node tests/times.test.mjs && node tests/tone.test.mjs && node tests/tools.test.mjs && node tests/tree.test.mjs && node tests/patch-spec.test.mjs",
|
|
27
|
+
"test": "node tests/unit.mjs && node tests/access-log.test.mjs && node tests/archive.test.mjs && node tests/autotree.test.mjs && node tests/backfill.test.mjs && node tests/branch.test.mjs && node tests/assistant.test.mjs && node tests/capture.test.mjs && node tests/clock.test.mjs && node tests/clock-skill.test.mjs && node tests/deep.test.mjs && node tests/deepitem.test.mjs && node tests/dswebsum.test.mjs && node tests/duty.test.mjs && node tests/export.test.mjs && node tests/feedback.test.mjs && node tests/forget-backfill.test.mjs && node tests/genesis.test.mjs && node tests/guard.test.mjs && node tests/habitgen.test.mjs && node tests/idle.test.mjs && node tests/importfile.test.mjs && node tests/importlog.test.mjs && node tests/injectlog.test.mjs && node tests/l1lines.test.mjs && node tests/memories.test.mjs && node tests/mode.test.mjs && node tests/mode-service.test.mjs && node tests/mustache.test.mjs && node tests/retitle.test.mjs && node tests/rules.test.mjs && node tests/sessionline.test.mjs && node tests/suggest.test.mjs && node tests/summarize.test.mjs && node tests/times.test.mjs && node tests/tone.test.mjs && node tests/tools.test.mjs && node tests/recall.test.mjs && node tests/recall-search.test.mjs && node tests/tree.test.mjs && node tests/patch-spec.test.mjs && node tests/bodylimit.test.mjs && node tests/client-batch.test.mjs",
|
|
28
28
|
"check": "node tools/check-install.mjs",
|
|
29
29
|
"check:publish": "node tools/check-publish.mjs"
|
|
30
30
|
},
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"@deepseek-ai/dsh-client-ui-slots": ">=0.1.7-rc.2",
|
|
62
62
|
"@deepseek-ai/dsh-host-webserver": ">=0.1.7-rc.2",
|
|
63
63
|
"@deepseek-ai/dsh-llm": ">=0.1.7-rc.2",
|
|
64
|
+
"@deepseek-ai/dsh-session-query": ">=0.1.7-rc.2",
|
|
64
65
|
"@deepseek-ai/dsh-skill": ">=0.1.7-rc.2",
|
|
65
66
|
"@deepseek-ai/dsh-system-prompt": ">=0.1.7-rc.2",
|
|
66
67
|
"@deepseek-ai/dsh-tools": ">=0.1.7-rc.2"
|
|
@@ -90,6 +91,9 @@
|
|
|
90
91
|
"@deepseek-ai/dsh-llm": {
|
|
91
92
|
"optional": true
|
|
92
93
|
},
|
|
94
|
+
"@deepseek-ai/dsh-session-query": {
|
|
95
|
+
"optional": true
|
|
96
|
+
},
|
|
93
97
|
"@deepseek-ai/dsh-skill": {
|
|
94
98
|
"optional": true
|
|
95
99
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// E4 访问日志 · 核心补丁脚本(幂等 / 可回滚 / 可自检)
|
|
3
3
|
//
|
|
4
4
|
// 背景:`/api` 的 403/401 栅栏在 core 里(dsh-client-connection),插件层看不到 ——
|
|
5
|
-
// 而"谁在什么时候被拦下"
|
|
5
|
+
// 而"谁在什么时候被拦下"恰恰是悬案最缺的那条证据(E4 设计里定下的首选落点)。
|
|
6
6
|
// 解决办法不是把逻辑搬进 core,而是让 core 多一行**可选调用**:
|
|
7
7
|
//
|
|
8
8
|
// globalThis.__dshAccessLogObserve?.(req, res); // E4 access log (dsh-ling patch)
|