@zaofan/dsh-qqbot 1.11.8 → 1.11.9

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/package.json CHANGED
@@ -1,145 +1,145 @@
1
- {
2
- "name": "@zaofan/dsh-qqbot",
3
- "version": "1.11.8",
4
- "description": "QQ Bot IM channel plugin for deepseek-harness (dsh)",
5
- "type": "module",
6
- "main": "./entry.js",
7
- "types": "./dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./entry.js"
12
- },
13
- "./channel-tools": {
14
- "types": "./dist/channel-tools.d.ts",
15
- "import": "./dist/channel-tools.js"
16
- },
17
- "./sticker-store": {
18
- "types": "./dist/features/sticker-store.d.ts",
19
- "import": "./dist/features/sticker-store.js"
20
- },
21
- "./schedule-store": {
22
- "types": "./dist/features/schedule-store.d.ts",
23
- "import": "./dist/features/schedule-store.js"
24
- },
25
- "./client": "./client/qqbot-settings.js",
26
- "./session-registry": "./dist/features/session-registry.js"
27
- },
28
- "scripts": {
29
- "build": "tsc",
30
- "dev": "tsc --watch",
31
- "prepublishOnly": "npm run build && node scripts/check-package.mjs",
32
- "check:package": "node scripts/check-package.mjs",
33
- "lint": "eslint src/",
34
- "test": "vitest run",
35
- "clean": "rm -rf dist",
36
- "ci:prepare-test-pkg": "node scripts/prepare-test-pkg.js"
37
- },
38
- "repository": {
39
- "type": "git",
40
- "url": "git+https://github.com/zaofan-make/dsh-qqbot.git"
41
- },
42
- "homepage": "https://github.com/zaofan-make/dsh-qqbot#readme",
43
- "bugs": {
44
- "url": "https://github.com/zaofan-make/dsh-qqbot/issues"
45
- },
46
- "keywords": [
47
- "deepseek-harness",
48
- "dsh",
49
- "dsh-plugin",
50
- "qqbot",
51
- "qq-bot",
52
- "chatbot",
53
- "ai-agent",
54
- "cordis",
55
- "plugin",
56
- "llm"
57
- ],
58
- "license": "MIT",
59
- "files": [
60
- "dist",
61
- "docs/extension-contract.md",
62
- "docs/USER-GUIDE.md",
63
- "docs/botplay-contract.md",
64
- "docs/host-services.md",
65
- "examples",
66
- "client",
67
- "cordis.patch.yml",
68
- "settings-host.js",
69
- "README.md",
70
- "README_EN.md",
71
- "LICENSE",
72
- "voice-convert.mjs",
73
- "scripts/download-model.mjs",
74
- "scripts/fix-dsh-016-presets.ps1",
75
- "entry.js"
76
- ],
77
- "dsh": {
78
- "bundle": {
79
- "patch": "./cordis.patch.yml"
80
- },
81
- "client": {
82
- "platform": "web",
83
- "inject": [
84
- "@deepseek-ai/dsh-client-ui-settings",
85
- "@deepseek-ai/dsh-client-ui-slots"
86
- ],
87
- "immediately": true
88
- },
89
- "engines": {
90
- "dsh": ">=0.1.5-rc.2"
91
- },
92
- "manifestVersion": 1
93
- },
94
- "dependencies": {
95
- "@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.1",
96
- "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
97
- "@deepseek-ai/schemastery": "3.18.4",
98
- "@tencent-connect/qqbot-connector": "1.2.0",
99
- "@tencent-connect/qqbot-nodejs": "^1.0.4",
100
- "js-yaml": "^4.1.0",
101
- "lamejs-fixed": "^1.2.2",
102
- "silk-wasm": "^3.7.1"
103
- },
104
- "optionalDependencies": {
105
- "@huggingface/transformers": "^4.3.0"
106
- },
107
- "peerDependencies": {
108
- "@deepseek-ai/cordis": ">=4.0.1",
109
- "@deepseek-ai/dsh-agent": ">=0.1.0-rc.6",
110
- "@deepseek-ai/dsh-llm": ">=0.1.0-rc.6",
111
- "@deepseek-ai/dsh-session": ">=0.1.0-rc.6",
112
- "@deepseek-ai/dsh-settings": "*",
113
- "@deepseek-ai/dsh-tools": ">=0.1.1-rc.2",
114
- "@tencent-connect/qqbot-connector": "1.2.0"
115
- },
116
- "peerDependenciesMeta": {
117
- "@deepseek-ai/cordis": {
118
- "optional": true
119
- },
120
- "@deepseek-ai/schemastery": {
121
- "optional": true
122
- },
123
- "@deepseek-ai/dsh-agent": {
124
- "optional": true
125
- },
126
- "@deepseek-ai/dsh-session": {
127
- "optional": true
128
- },
129
- "@deepseek-ai/dsh-llm": {
130
- "optional": true
131
- }
132
- },
133
- "devDependencies": {
134
- "@deepseek-ai/cordis": "4.0.1",
135
- "@deepseek-ai/dsh-agent": "0.1.0-rc.6",
136
- "@deepseek-ai/dsh-llm": "0.1.0-rc.6",
137
- "@deepseek-ai/dsh-session": "0.1.0-rc.6",
138
- "@deepseek-ai/dsh-tools": ">=0.1.1-rc.2",
139
- "@deepseek-ai/schemastery": "3.18.1",
140
- "@types/js-yaml": "^4.0.9",
141
- "@types/node": "^26.2.0",
142
- "typescript": "^5.5.0",
143
- "vitest": "^4.1.8"
144
- }
145
- }
1
+ {
2
+ "name": "@zaofan/dsh-qqbot",
3
+ "version": "1.11.9",
4
+ "description": "QQ Bot IM channel plugin for deepseek-harness (dsh)",
5
+ "type": "module",
6
+ "main": "./entry.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./entry.js"
12
+ },
13
+ "./channel-tools": {
14
+ "types": "./dist/channel-tools.d.ts",
15
+ "import": "./dist/channel-tools.js"
16
+ },
17
+ "./sticker-store": {
18
+ "types": "./dist/features/sticker-store.d.ts",
19
+ "import": "./dist/features/sticker-store.js"
20
+ },
21
+ "./schedule-store": {
22
+ "types": "./dist/features/schedule-store.d.ts",
23
+ "import": "./dist/features/schedule-store.js"
24
+ },
25
+ "./client": "./client/qqbot-settings.js",
26
+ "./session-registry": "./dist/features/session-registry.js"
27
+ },
28
+ "scripts": {
29
+ "build": "tsc",
30
+ "dev": "tsc --watch",
31
+ "prepublishOnly": "npm run build && node scripts/check-package.mjs",
32
+ "check:package": "node scripts/check-package.mjs",
33
+ "lint": "eslint src/",
34
+ "test": "vitest run",
35
+ "clean": "rm -rf dist",
36
+ "ci:prepare-test-pkg": "node scripts/prepare-test-pkg.js"
37
+ },
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/zaofan-make/dsh-qqbot.git"
41
+ },
42
+ "homepage": "https://github.com/zaofan-make/dsh-qqbot#readme",
43
+ "bugs": {
44
+ "url": "https://github.com/zaofan-make/dsh-qqbot/issues"
45
+ },
46
+ "keywords": [
47
+ "deepseek-harness",
48
+ "dsh",
49
+ "dsh-plugin",
50
+ "qqbot",
51
+ "qq-bot",
52
+ "chatbot",
53
+ "ai-agent",
54
+ "cordis",
55
+ "plugin",
56
+ "llm"
57
+ ],
58
+ "license": "MIT",
59
+ "files": [
60
+ "dist",
61
+ "docs/extension-contract.md",
62
+ "docs/USER-GUIDE.md",
63
+ "docs/botplay-contract.md",
64
+ "docs/host-services.md",
65
+ "examples",
66
+ "client",
67
+ "cordis.patch.yml",
68
+ "settings-host.js",
69
+ "README.md",
70
+ "README_EN.md",
71
+ "LICENSE",
72
+ "voice-convert.mjs",
73
+ "scripts/download-model.mjs",
74
+ "scripts/fix-dsh-016-presets.ps1",
75
+ "entry.js"
76
+ ],
77
+ "dsh": {
78
+ "bundle": {
79
+ "patch": "./cordis.patch.yml"
80
+ },
81
+ "client": {
82
+ "platform": "web",
83
+ "inject": [
84
+ "@deepseek-ai/dsh-client-ui-settings",
85
+ "@deepseek-ai/dsh-client-ui-slots"
86
+ ],
87
+ "immediately": true
88
+ },
89
+ "engines": {
90
+ "dsh": ">=0.1.5-rc.2"
91
+ },
92
+ "manifestVersion": 1
93
+ },
94
+ "dependencies": {
95
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.1",
96
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
97
+ "@deepseek-ai/schemastery": "3.18.4",
98
+ "@tencent-connect/qqbot-connector": "1.2.0",
99
+ "@tencent-connect/qqbot-nodejs": "^1.0.4",
100
+ "js-yaml": "^4.1.0",
101
+ "lamejs-fixed": "^1.2.2",
102
+ "silk-wasm": "^3.7.1"
103
+ },
104
+ "optionalDependencies": {
105
+ "@huggingface/transformers": "^4.3.0"
106
+ },
107
+ "peerDependencies": {
108
+ "@deepseek-ai/cordis": ">=4.0.1",
109
+ "@deepseek-ai/dsh-agent": ">=0.1.0-rc.6",
110
+ "@deepseek-ai/dsh-llm": ">=0.1.0-rc.6",
111
+ "@deepseek-ai/dsh-session": ">=0.1.0-rc.6",
112
+ "@deepseek-ai/dsh-settings": "*",
113
+ "@deepseek-ai/dsh-tools": ">=0.1.1-rc.2",
114
+ "@tencent-connect/qqbot-connector": "1.2.0"
115
+ },
116
+ "peerDependenciesMeta": {
117
+ "@deepseek-ai/cordis": {
118
+ "optional": true
119
+ },
120
+ "@deepseek-ai/schemastery": {
121
+ "optional": true
122
+ },
123
+ "@deepseek-ai/dsh-agent": {
124
+ "optional": true
125
+ },
126
+ "@deepseek-ai/dsh-session": {
127
+ "optional": true
128
+ },
129
+ "@deepseek-ai/dsh-llm": {
130
+ "optional": true
131
+ }
132
+ },
133
+ "devDependencies": {
134
+ "@deepseek-ai/cordis": "4.0.1",
135
+ "@deepseek-ai/dsh-agent": "0.1.0-rc.6",
136
+ "@deepseek-ai/dsh-llm": "0.1.0-rc.6",
137
+ "@deepseek-ai/dsh-session": "0.1.0-rc.6",
138
+ "@deepseek-ai/dsh-tools": ">=0.1.1-rc.2",
139
+ "@deepseek-ai/schemastery": "3.18.1",
140
+ "@types/js-yaml": "^4.0.9",
141
+ "@types/node": "^26.2.0",
142
+ "typescript": "^5.5.0",
143
+ "vitest": "^4.1.8"
144
+ }
145
+ }
package/settings-host.js CHANGED
@@ -2620,13 +2620,20 @@ export function apply(ctx) {
2620
2620
  const hits = [];
2621
2621
  const seen = new Set();
2622
2622
  // 按实例台账补昵称(dataRoot/表情包/known-chats.jsonl)
2623
+ // ⚠️ 2026-10-28:加 30s 缓存 —— 每次 lookup 都重新扫台账是"判断命中慢"的另一半原因。
2623
2624
  const nameOf = (ns, scope, peerId) => {
2624
2625
  const bot = botByNs.get(ns);
2625
2626
  if (!bot) return '';
2627
+ const nc = (globalThis.__qqsLedgerNameCache = globalThis.__qqsLedgerNameCache || new Map());
2628
+ const ck = ns + '|' + scope + '|' + peerId;
2629
+ const hitN = nc.get(ck);
2630
+ if (hitN && Date.now() - hitN.at < 30000) return hitN.name;
2626
2631
  try {
2627
2632
  const cwd = bot.cfg.dataRoot || bot.cfg.cwd;
2628
2633
  const ld = chatLedgerNames(join(cwd, '表情包')).get(scope + ':' + peerId);
2629
- return (ld && ld.name) || '';
2634
+ const name = (ld && ld.name) || '';
2635
+ nc.set(ck, { at: Date.now(), name });
2636
+ return name;
2630
2637
  } catch { return '' }
2631
2638
  };
2632
2639
  const pushHit = (ns, scope, peerId, name, via) => {
@@ -2635,35 +2642,77 @@ export function apply(ctx) {
2635
2642
  seen.add(key);
2636
2643
  hits.push({ ns, scope, peerId, name, via });
2637
2644
  };
2638
- // ① 权威: 真实会话记录(唯一可靠源 —— fork 后 sessionId 为 randomUUID, derive 推导会 miss)
2639
- const sessReg = await import('./dist/features/session-registry.js');
2640
- for (const r of sessReg.findSessionBySessionIdWeb(sessionId)) {
2641
- pushHit(r.ns, r.scope, r.peerId, nameOf(r.ns, r.scope, r.peerId), 'session');
2642
- }
2643
- // ② 兜底: 确定性推导(会话记录未建立/被回收时, 从注册表+台账推导; 与权威命中自动去重)
2645
+ // ★ 2026-10-28(主人反馈"判断命中要两三秒、悬浮球显隐太慢"):
2646
+ // ① 结果缓存 15s(front 端 5s 轮一次,命中缓存基本零延迟);
2647
+ // ② 快路优先:prefs(单文件 JSON) → derive(台账/群注册表) → **最后**才 session-registry
2648
+ // (那个 `await import('./dist/...')` 冷启动要几百 ms 到数秒,是主要慢因);
2649
+ // 快路命中就直接返回,不再等它。
2650
+ const cache = (globalThis.__qqsLookupCache = globalThis.__qqsLookupCache || new Map());
2651
+ const cached = cache.get(sessionId);
2652
+ if (cached && Date.now() - cached.at < 15000) return writeJson(res, 200, cached.data);
2653
+
2654
+ // ① 快路 A:反查持久偏好({dataRoot}/.qqbot/model-prefs.json 的 sessionKey → 最新 sessionId)
2655
+ // 覆盖"宿主刚启动、会话记录还没恢复"以及"fork/新建后 sessionId 是 randomUUID、derive 推不出"。
2644
2656
  for (const bot of bots) {
2645
2657
  if (bot.disabled || !bot.cfg?.appId || !bot.cfg?.cwd) continue;
2646
- const appId = bot.cfg.appId;
2647
2658
  const cwd = bot.cfg.dataRoot || bot.cfg.cwd;
2648
2659
  const ns = bot.id;
2649
- const ledger = chatLedgerNames(join(cwd, '表情包'));
2650
- const groups = readGroupsJson(cwd);
2651
- const gids = new Set([...Object.keys(groups), ...[...ledger.keys()].filter((k) => k.startsWith('group:')).map((k) => k.slice(6))]);
2652
- for (const gid of gids) {
2653
- if (!gid) continue;
2654
- if (deriveSessionIdOf(appId, 'group', gid) !== sessionId) continue;
2655
- const regMeta = groups[gid];
2656
- const ld = ledger.get('group:' + gid);
2657
- pushHit(ns, 'group', gid, (regMeta && regMeta.name) || (ld && ld.name) || '', 'derive');
2658
- }
2659
- for (const [key2, ld] of ledger) {
2660
- if (!key2.startsWith('c2c:')) continue;
2661
- const openid = key2.slice(4);
2662
- if (!openid || deriveSessionIdOf(appId, 'c2c', openid) !== sessionId) continue;
2663
- pushHit(ns, 'c2c', openid, ld.name || '', 'derive');
2660
+ try {
2661
+ const raw = readFileSync(join(cwd, '.qqbot', 'model-prefs.json'), 'utf8');
2662
+ const prefs = JSON.parse(raw);
2663
+ const ids = prefs && prefs.sessionIds;
2664
+ if (!ids || typeof ids !== 'object') continue;
2665
+ for (const [sk0, sid] of Object.entries(ids)) {
2666
+ if (!sid || String(sid) !== sessionId) continue;
2667
+ // 兼容 "sessionKey@sessionId" 形态(overrides 的键是那个形状,sessionIds 一般不带)
2668
+ const sk = String(sk0).split('@')[0];
2669
+ const i = sk.indexOf(':');
2670
+ if (i <= 0) continue;
2671
+ const scope = sk.slice(0, i);
2672
+ const peerId = sk.slice(i + 1);
2673
+ if (!peerId || (scope !== 'group' && scope !== 'c2c')) continue;
2674
+ pushHit(ns, scope, peerId, nameOf(ns, scope, peerId), 'prefs');
2675
+ }
2676
+ } catch { /* 没有偏好文件 / JSON 坏了:跳过 */ }
2677
+ }
2678
+ // ② 快路 B:确定性推导(会话记录未建立/被回收时,从群注册表 + 台账推导)
2679
+ if (hits.length === 0) {
2680
+ for (const bot of bots) {
2681
+ if (bot.disabled || !bot.cfg?.appId || !bot.cfg?.cwd) continue;
2682
+ const appId = bot.cfg.appId;
2683
+ const cwd = bot.cfg.dataRoot || bot.cfg.cwd;
2684
+ const ns = bot.id;
2685
+ const ledger = chatLedgerNames(join(cwd, '表情包'));
2686
+ const groups = readGroupsJson(cwd);
2687
+ const gids = new Set([...Object.keys(groups), ...[...ledger.keys()].filter((k) => k.startsWith('group:')).map((k) => k.slice(6))]);
2688
+ for (const gid of gids) {
2689
+ if (!gid) continue;
2690
+ if (deriveSessionIdOf(appId, 'group', gid) !== sessionId) continue;
2691
+ const regMeta = groups[gid];
2692
+ const ld = ledger.get('group:' + gid);
2693
+ pushHit(ns, 'group', gid, (regMeta && regMeta.name) || (ld && ld.name) || '', 'derive');
2694
+ }
2695
+ for (const [key2, ld] of ledger) {
2696
+ if (!key2.startsWith('c2c:')) continue;
2697
+ const openid = key2.slice(4);
2698
+ if (!openid || deriveSessionIdOf(appId, 'c2c', openid) !== sessionId) continue;
2699
+ pushHit(ns, 'c2c', openid, ld.name || '', 'derive');
2700
+ }
2664
2701
  }
2665
2702
  }
2666
- writeJson(res, 200, { ok: true, sessionId, hits });
2703
+ // ③ 慢路(只在快路全落空时才走):真实会话记录,权威但需要动态 import
2704
+ if (hits.length === 0) {
2705
+ try {
2706
+ const sessReg = await import('./dist/features/session-registry.js');
2707
+ for (const r of sessReg.findSessionBySessionIdWeb(sessionId)) {
2708
+ pushHit(r.ns, r.scope, r.peerId, nameOf(r.ns, r.scope, r.peerId), 'session');
2709
+ }
2710
+ } catch { /* 模块不可用:按未命中处理 */ }
2711
+ }
2712
+ const data = { ok: true, sessionId, hits };
2713
+ cache.set(sessionId, { at: Date.now(), data });
2714
+ if (cache.size > 300) { let n = 0; for (const k of cache.keys()) { cache.delete(k); if (++n >= 150) break } }
2715
+ writeJson(res, 200, data);
2667
2716
  } catch (e) { writeJson(res, 500, { error: String((e && e.message) || e) }); }
2668
2717
  });
2669
2718