@zhin.js/core 1.0.36 → 1.0.38

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 (196) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +57 -3
  3. package/lib/adapter.d.ts +11 -0
  4. package/lib/adapter.d.ts.map +1 -1
  5. package/lib/adapter.js +61 -0
  6. package/lib/adapter.js.map +1 -1
  7. package/lib/ai/index.d.ts +3 -39
  8. package/lib/ai/index.d.ts.map +1 -1
  9. package/lib/ai/index.js +2 -44
  10. package/lib/ai/index.js.map +1 -1
  11. package/lib/ai/types.d.ts +4 -3
  12. package/lib/ai/types.d.ts.map +1 -1
  13. package/lib/built/ai-trigger.js.map +1 -1
  14. package/lib/built/common-adapter-tools.d.ts +55 -0
  15. package/lib/built/common-adapter-tools.d.ts.map +1 -0
  16. package/lib/built/common-adapter-tools.js +158 -0
  17. package/lib/built/common-adapter-tools.js.map +1 -0
  18. package/lib/built/dispatcher.d.ts.map +1 -1
  19. package/lib/built/dispatcher.js +50 -46
  20. package/lib/built/dispatcher.js.map +1 -1
  21. package/lib/built/skill.d.ts.map +1 -1
  22. package/lib/built/skill.js +0 -1
  23. package/lib/built/skill.js.map +1 -1
  24. package/lib/built/tool.d.ts +3 -3
  25. package/lib/built/tool.d.ts.map +1 -1
  26. package/lib/built/tool.js.map +1 -1
  27. package/lib/feature.d.ts +16 -1
  28. package/lib/feature.d.ts.map +1 -1
  29. package/lib/feature.js +41 -2
  30. package/lib/feature.js.map +1 -1
  31. package/lib/index.d.ts +1 -0
  32. package/lib/index.d.ts.map +1 -1
  33. package/lib/index.js +2 -0
  34. package/lib/index.js.map +1 -1
  35. package/lib/plugin.d.ts +38 -1
  36. package/lib/plugin.d.ts.map +1 -1
  37. package/lib/plugin.js +73 -22
  38. package/lib/plugin.js.map +1 -1
  39. package/lib/scheduler/scheduler.js +1 -1
  40. package/lib/scheduler/scheduler.js.map +1 -1
  41. package/lib/types.d.ts +43 -28
  42. package/lib/types.d.ts.map +1 -1
  43. package/lib/utils.d.ts +12 -3
  44. package/lib/utils.d.ts.map +1 -1
  45. package/lib/utils.js +64 -54
  46. package/lib/utils.js.map +1 -1
  47. package/package.json +5 -5
  48. package/src/adapter.ts +85 -5
  49. package/src/ai/index.ts +8 -186
  50. package/src/ai/types.ts +5 -4
  51. package/src/built/ai-trigger.ts +2 -2
  52. package/src/built/common-adapter-tools.ts +207 -0
  53. package/src/built/dispatcher.ts +51 -52
  54. package/src/built/skill.ts +3 -4
  55. package/src/built/tool.ts +3 -3
  56. package/src/feature.ts +45 -2
  57. package/src/index.ts +2 -0
  58. package/src/plugin.ts +92 -31
  59. package/src/scheduler/scheduler.ts +1 -1
  60. package/src/types.ts +39 -28
  61. package/src/utils.ts +63 -52
  62. package/tests/ai/setup.ts +2 -2
  63. package/tests/utils.test.ts +1 -3
  64. package/lib/ai/agent.d.ts +0 -130
  65. package/lib/ai/agent.d.ts.map +0 -1
  66. package/lib/ai/agent.js +0 -684
  67. package/lib/ai/agent.js.map +0 -1
  68. package/lib/ai/bootstrap.d.ts +0 -91
  69. package/lib/ai/bootstrap.d.ts.map +0 -1
  70. package/lib/ai/bootstrap.js +0 -243
  71. package/lib/ai/bootstrap.js.map +0 -1
  72. package/lib/ai/builtin-tools.d.ts +0 -59
  73. package/lib/ai/builtin-tools.d.ts.map +0 -1
  74. package/lib/ai/builtin-tools.js +0 -777
  75. package/lib/ai/builtin-tools.js.map +0 -1
  76. package/lib/ai/compaction.d.ts +0 -132
  77. package/lib/ai/compaction.d.ts.map +0 -1
  78. package/lib/ai/compaction.js +0 -370
  79. package/lib/ai/compaction.js.map +0 -1
  80. package/lib/ai/context-manager.d.ts +0 -213
  81. package/lib/ai/context-manager.d.ts.map +0 -1
  82. package/lib/ai/context-manager.js +0 -313
  83. package/lib/ai/context-manager.js.map +0 -1
  84. package/lib/ai/conversation-memory.d.ts +0 -181
  85. package/lib/ai/conversation-memory.d.ts.map +0 -1
  86. package/lib/ai/conversation-memory.js +0 -581
  87. package/lib/ai/conversation-memory.js.map +0 -1
  88. package/lib/ai/cron-engine.d.ts +0 -92
  89. package/lib/ai/cron-engine.d.ts.map +0 -1
  90. package/lib/ai/cron-engine.js +0 -278
  91. package/lib/ai/cron-engine.js.map +0 -1
  92. package/lib/ai/follow-up.d.ts +0 -131
  93. package/lib/ai/follow-up.d.ts.map +0 -1
  94. package/lib/ai/follow-up.js +0 -265
  95. package/lib/ai/follow-up.js.map +0 -1
  96. package/lib/ai/hooks.d.ts +0 -143
  97. package/lib/ai/hooks.d.ts.map +0 -1
  98. package/lib/ai/hooks.js +0 -108
  99. package/lib/ai/hooks.js.map +0 -1
  100. package/lib/ai/init.d.ts +0 -30
  101. package/lib/ai/init.d.ts.map +0 -1
  102. package/lib/ai/init.js +0 -686
  103. package/lib/ai/init.js.map +0 -1
  104. package/lib/ai/output.d.ts +0 -93
  105. package/lib/ai/output.d.ts.map +0 -1
  106. package/lib/ai/output.js +0 -176
  107. package/lib/ai/output.js.map +0 -1
  108. package/lib/ai/rate-limiter.d.ts +0 -38
  109. package/lib/ai/rate-limiter.d.ts.map +0 -1
  110. package/lib/ai/rate-limiter.js +0 -86
  111. package/lib/ai/rate-limiter.js.map +0 -1
  112. package/lib/ai/service.d.ts +0 -88
  113. package/lib/ai/service.d.ts.map +0 -1
  114. package/lib/ai/service.js +0 -285
  115. package/lib/ai/service.js.map +0 -1
  116. package/lib/ai/session.d.ts +0 -186
  117. package/lib/ai/session.d.ts.map +0 -1
  118. package/lib/ai/session.js +0 -443
  119. package/lib/ai/session.js.map +0 -1
  120. package/lib/ai/subagent.d.ts +0 -50
  121. package/lib/ai/subagent.d.ts.map +0 -1
  122. package/lib/ai/subagent.js +0 -144
  123. package/lib/ai/subagent.js.map +0 -1
  124. package/lib/ai/tone-detector.d.ts +0 -19
  125. package/lib/ai/tone-detector.d.ts.map +0 -1
  126. package/lib/ai/tone-detector.js +0 -72
  127. package/lib/ai/tone-detector.js.map +0 -1
  128. package/lib/ai/tools.d.ts +0 -45
  129. package/lib/ai/tools.d.ts.map +0 -1
  130. package/lib/ai/tools.js +0 -206
  131. package/lib/ai/tools.js.map +0 -1
  132. package/lib/ai/user-profile.d.ts +0 -56
  133. package/lib/ai/user-profile.d.ts.map +0 -1
  134. package/lib/ai/user-profile.js +0 -130
  135. package/lib/ai/user-profile.js.map +0 -1
  136. package/lib/ai/zhin-agent/builtin-tools.d.ts +0 -17
  137. package/lib/ai/zhin-agent/builtin-tools.d.ts.map +0 -1
  138. package/lib/ai/zhin-agent/builtin-tools.js +0 -220
  139. package/lib/ai/zhin-agent/builtin-tools.js.map +0 -1
  140. package/lib/ai/zhin-agent/config.d.ts +0 -54
  141. package/lib/ai/zhin-agent/config.d.ts.map +0 -1
  142. package/lib/ai/zhin-agent/config.js +0 -76
  143. package/lib/ai/zhin-agent/config.js.map +0 -1
  144. package/lib/ai/zhin-agent/exec-policy.d.ts +0 -20
  145. package/lib/ai/zhin-agent/exec-policy.d.ts.map +0 -1
  146. package/lib/ai/zhin-agent/exec-policy.js +0 -71
  147. package/lib/ai/zhin-agent/exec-policy.js.map +0 -1
  148. package/lib/ai/zhin-agent/index.d.ts +0 -70
  149. package/lib/ai/zhin-agent/index.d.ts.map +0 -1
  150. package/lib/ai/zhin-agent/index.js +0 -404
  151. package/lib/ai/zhin-agent/index.js.map +0 -1
  152. package/lib/ai/zhin-agent/prompt.d.ts +0 -21
  153. package/lib/ai/zhin-agent/prompt.d.ts.map +0 -1
  154. package/lib/ai/zhin-agent/prompt.js +0 -111
  155. package/lib/ai/zhin-agent/prompt.js.map +0 -1
  156. package/lib/ai/zhin-agent/tool-collector.d.ts +0 -22
  157. package/lib/ai/zhin-agent/tool-collector.d.ts.map +0 -1
  158. package/lib/ai/zhin-agent/tool-collector.js +0 -218
  159. package/lib/ai/zhin-agent/tool-collector.js.map +0 -1
  160. package/src/ai/agent.ts +0 -812
  161. package/src/ai/bootstrap.ts +0 -309
  162. package/src/ai/builtin-tools.ts +0 -849
  163. package/src/ai/compaction.ts +0 -529
  164. package/src/ai/context-manager.ts +0 -440
  165. package/src/ai/conversation-memory.ts +0 -774
  166. package/src/ai/cron-engine.ts +0 -337
  167. package/src/ai/follow-up.ts +0 -357
  168. package/src/ai/hooks.ts +0 -223
  169. package/src/ai/init.ts +0 -762
  170. package/src/ai/output.ts +0 -261
  171. package/src/ai/rate-limiter.ts +0 -129
  172. package/src/ai/service.ts +0 -331
  173. package/src/ai/session.ts +0 -544
  174. package/src/ai/subagent.ts +0 -209
  175. package/src/ai/tone-detector.ts +0 -89
  176. package/src/ai/tools.ts +0 -218
  177. package/src/ai/user-profile.ts +0 -181
  178. package/src/ai/zhin-agent/builtin-tools.ts +0 -247
  179. package/src/ai/zhin-agent/config.ts +0 -113
  180. package/src/ai/zhin-agent/exec-policy.ts +0 -78
  181. package/src/ai/zhin-agent/index.ts +0 -512
  182. package/src/ai/zhin-agent/prompt.ts +0 -131
  183. package/src/ai/zhin-agent/tool-collector.ts +0 -243
  184. package/tests/ai/agent.test.ts +0 -614
  185. package/tests/ai/context-manager.test.ts +0 -413
  186. package/tests/ai/conversation-memory.test.ts +0 -128
  187. package/tests/ai/follow-up.test.ts +0 -175
  188. package/tests/ai/integration.test.ts +0 -584
  189. package/tests/ai/output.test.ts +0 -128
  190. package/tests/ai/rate-limiter.test.ts +0 -108
  191. package/tests/ai/session.test.ts +0 -375
  192. package/tests/ai/subagent.test.ts +0 -270
  193. package/tests/ai/tone-detector.test.ts +0 -80
  194. package/tests/ai/tools-builtin.test.ts +0 -346
  195. package/tests/ai/user-profile.test.ts +0 -73
  196. package/tests/ai/zhin-agent.test.ts +0 -177
package/src/ai/session.ts DELETED
@@ -1,544 +0,0 @@
1
- /**
2
- * @zhin.js/ai - Session Manager
3
- * 会话管理器,支持上下文记忆和数据库持久化
4
- *
5
- * 特性:
6
- * - 数据库持久化存储(使用 Zhin 的数据库服务)
7
- * - 内存缓存加速读取
8
- * - 自动过期清理
9
- * - 更长的上下文记忆能力
10
- */
11
-
12
- import { Logger } from '@zhin.js/logger';
13
- import type { ChatMessage, SessionConfig, Session } from './types.js';
14
-
15
- const logger = new Logger(null, 'AI-Session');
16
-
17
- /**
18
- * 数据库模型定义
19
- */
20
- export const AI_SESSION_MODEL = {
21
- session_id: { type: 'text' as const, nullable: false },
22
- messages: { type: 'json' as const, default: [] },
23
- config: { type: 'json' as const, default: {} },
24
- created_at: { type: 'integer' as const, default: 0 },
25
- updated_at: { type: 'integer' as const, default: 0 },
26
- };
27
-
28
- /**
29
- * 数据库会话记录
30
- */
31
- interface SessionRecord {
32
- id?: number;
33
- session_id: string;
34
- messages: ChatMessage[];
35
- config: SessionConfig;
36
- created_at: number;
37
- updated_at: number;
38
- }
39
-
40
- /**
41
- * 会话管理器接口
42
- */
43
- export interface ISessionManager {
44
- get(sessionId: string, config?: SessionConfig): Session | Promise<Session>;
45
- has(sessionId: string): boolean | Promise<boolean>;
46
- addMessage(sessionId: string, message: ChatMessage): void | Promise<void>;
47
- getMessages(sessionId: string): ChatMessage[] | Promise<ChatMessage[]>;
48
- setSystemPrompt(sessionId: string, prompt: string): void | Promise<void>;
49
- clear(sessionId: string): boolean | Promise<boolean>;
50
- reset(sessionId: string): void | Promise<void>;
51
- listSessions(): string[] | Promise<string[]>;
52
- getStats(): { total: number; active: number; expired: number } | Promise<{ total: number; active: number; expired: number }>;
53
- cleanup(): number | Promise<number>;
54
- dispose(): void | Promise<void>;
55
- }
56
-
57
- /**
58
- * 内存会话管理器(回退方案)
59
- */
60
- export class MemorySessionManager implements ISessionManager {
61
- private sessions: Map<string, Session> = new Map();
62
- private config: Required<Pick<SessionConfig, 'maxHistory' | 'expireMs'>>;
63
- private cleanupTimer?: ReturnType<typeof setInterval>;
64
-
65
- constructor(config: { maxHistory?: number; expireMs?: number } = {}) {
66
- this.config = {
67
- maxHistory: config.maxHistory ?? 100,
68
- expireMs: config.expireMs ?? 24 * 60 * 60 * 1000, // 24 小时
69
- };
70
-
71
- // 定期清理过期会话
72
- this.cleanupTimer = setInterval(() => this.cleanup(), 5 * 60 * 1000);
73
- }
74
-
75
- get(sessionId: string, config?: SessionConfig): Session {
76
- let session = this.sessions.get(sessionId);
77
-
78
- if (!session) {
79
- session = {
80
- id: sessionId,
81
- config: config || { provider: 'openai' },
82
- messages: [],
83
- createdAt: Date.now(),
84
- updatedAt: Date.now(),
85
- };
86
- this.sessions.set(sessionId, session);
87
- } else {
88
- session.updatedAt = Date.now();
89
- }
90
-
91
- return session;
92
- }
93
-
94
- has(sessionId: string): boolean {
95
- return this.sessions.has(sessionId);
96
- }
97
-
98
- addMessage(sessionId: string, message: ChatMessage): void {
99
- const session = this.get(sessionId);
100
- session.messages.push(message);
101
- session.updatedAt = Date.now();
102
- this.trimMessages(session);
103
- }
104
-
105
- private trimMessages(session: Session): void {
106
- const maxHistory = session.config.maxHistory ?? this.config.maxHistory;
107
- if (session.messages.length > maxHistory) {
108
- const systemMessages = session.messages.filter(m => m.role === 'system');
109
- const otherMessages = session.messages.filter(m => m.role !== 'system');
110
- const keepCount = maxHistory - systemMessages.length;
111
- session.messages = [...systemMessages, ...otherMessages.slice(-keepCount)];
112
- }
113
- }
114
-
115
- getMessages(sessionId: string): ChatMessage[] {
116
- return this.sessions.get(sessionId)?.messages || [];
117
- }
118
-
119
- setSystemPrompt(sessionId: string, prompt: string): void {
120
- const session = this.get(sessionId);
121
- session.messages = session.messages.filter(m => m.role !== 'system');
122
- session.messages.unshift({ role: 'system', content: prompt });
123
- session.updatedAt = Date.now();
124
- }
125
-
126
- clear(sessionId: string): boolean {
127
- return this.sessions.delete(sessionId);
128
- }
129
-
130
- reset(sessionId: string): void {
131
- const session = this.sessions.get(sessionId);
132
- if (session) {
133
- const systemMessages = session.messages.filter(m => m.role === 'system');
134
- session.messages = systemMessages;
135
- session.updatedAt = Date.now();
136
- }
137
- }
138
-
139
- listSessions(): string[] {
140
- return Array.from(this.sessions.keys());
141
- }
142
-
143
- getStats(): { total: number; active: number; expired: number } {
144
- const now = Date.now();
145
- let active = 0;
146
- let expired = 0;
147
-
148
- for (const session of this.sessions.values()) {
149
- if (now - session.updatedAt > this.config.expireMs) {
150
- expired++;
151
- } else {
152
- active++;
153
- }
154
- }
155
-
156
- return { total: this.sessions.size, active, expired };
157
- }
158
-
159
- cleanup(): number {
160
- const now = Date.now();
161
- let cleaned = 0;
162
-
163
- for (const [id, session] of this.sessions) {
164
- const expireMs = session.config.expireMs ?? this.config.expireMs;
165
- if (now - session.updatedAt > expireMs) {
166
- this.sessions.delete(id);
167
- cleaned++;
168
- }
169
- }
170
-
171
- return cleaned;
172
- }
173
-
174
- dispose(): void {
175
- if (this.cleanupTimer) {
176
- clearInterval(this.cleanupTimer);
177
- this.cleanupTimer = undefined;
178
- }
179
- this.sessions.clear();
180
- }
181
- }
182
-
183
- /**
184
- * 数据库会话管理器
185
- * 使用 Zhin 的数据库服务进行持久化存储
186
- */
187
- export class DatabaseSessionManager implements ISessionManager {
188
- private cache: Map<string, Session> = new Map();
189
- private config: Required<Pick<SessionConfig, 'maxHistory' | 'expireMs'>>;
190
- private cleanupTimer?: ReturnType<typeof setInterval>;
191
- private saveQueue: Map<string, Session> = new Map();
192
- private saveTimer?: ReturnType<typeof setTimeout>;
193
- private model: any; // 数据库模型
194
-
195
- constructor(
196
- model: any,
197
- config: { maxHistory?: number; expireMs?: number } = {}
198
- ) {
199
- this.model = model;
200
- this.config = {
201
- maxHistory: config.maxHistory ?? 200, // 数据库支持更长的历史
202
- expireMs: config.expireMs ?? 7 * 24 * 60 * 60 * 1000, // 7 天过期
203
- };
204
-
205
- // 定期清理过期会话(每小时)
206
- this.cleanupTimer = setInterval(() => this.cleanup(), 60 * 60 * 1000);
207
- }
208
-
209
- /**
210
- * 从数据库加载会话
211
- */
212
- private async loadSession(sessionId: string): Promise<Session | null> {
213
- try {
214
- const records = await this.model.select().where({ session_id: sessionId });
215
- if (records && records.length > 0) {
216
- const record = records[0] as SessionRecord;
217
- // SQLite 中 json 类型存储为 TEXT,读回时需要解析
218
- const messages = typeof record.messages === 'string'
219
- ? JSON.parse(record.messages)
220
- : (record.messages || []);
221
- const config = typeof record.config === 'string'
222
- ? JSON.parse(record.config)
223
- : (record.config || { provider: 'openai' });
224
- return {
225
- id: record.session_id,
226
- config: Array.isArray(config) ? { provider: 'openai' } : config,
227
- messages: Array.isArray(messages) ? messages : [],
228
- createdAt: record.created_at,
229
- updatedAt: record.updated_at,
230
- };
231
- }
232
- } catch (error) {
233
- logger.debug('加载会话失败:', error);
234
- }
235
- return null;
236
- }
237
-
238
- /**
239
- * 保存会话到数据库(防抖)
240
- */
241
- private schedulesSave(session: Session): void {
242
- this.saveQueue.set(session.id, session);
243
-
244
- if (!this.saveTimer) {
245
- this.saveTimer = setTimeout(() => this.flushSaveQueue(), 1000);
246
- }
247
- }
248
-
249
- /**
250
- * 批量保存队列中的会话
251
- */
252
- private async flushSaveQueue(): Promise<void> {
253
- this.saveTimer = undefined;
254
-
255
- const sessions = Array.from(this.saveQueue.values());
256
- this.saveQueue.clear();
257
-
258
- for (const session of sessions) {
259
- try {
260
- const existing = await this.model.select().where({ session_id: session.id });
261
- const record: Partial<SessionRecord> = {
262
- session_id: session.id,
263
- messages: session.messages,
264
- config: session.config,
265
- updated_at: session.updatedAt,
266
- };
267
-
268
- if (existing && existing.length > 0) {
269
- await this.model.update(record).where({ session_id: session.id });
270
- } else {
271
- record.created_at = session.createdAt;
272
- await this.model.create(record);
273
- }
274
- } catch (error) {
275
- logger.debug(`保存会话 ${session.id} 失败:`, error);
276
- }
277
- }
278
- }
279
-
280
- async get(sessionId: string, config?: SessionConfig): Promise<Session> {
281
- // 先检查缓存
282
- let session = this.cache.get(sessionId);
283
-
284
- if (!session) {
285
- // 从数据库加载
286
- session = await this.loadSession(sessionId) ?? undefined;
287
-
288
- if (!session) {
289
- // 创建新会话
290
- session = {
291
- id: sessionId,
292
- config: config || { provider: 'openai' },
293
- messages: [],
294
- createdAt: Date.now(),
295
- updatedAt: Date.now(),
296
- };
297
- }
298
-
299
- this.cache.set(sessionId, session);
300
- }
301
-
302
- session.updatedAt = Date.now();
303
- this.schedulesSave(session);
304
-
305
- return session;
306
- }
307
-
308
- async has(sessionId: string): Promise<boolean> {
309
- if (this.cache.has(sessionId)) {
310
- return true;
311
- }
312
-
313
- try {
314
- const records = await this.model.select().where({ session_id: sessionId });
315
- return records && records.length > 0;
316
- } catch {
317
- return false;
318
- }
319
- }
320
-
321
- async addMessage(sessionId: string, message: ChatMessage): Promise<void> {
322
- const session = await this.get(sessionId);
323
- session.messages.push(message);
324
- session.updatedAt = Date.now();
325
- this.trimMessages(session);
326
- this.schedulesSave(session);
327
- }
328
-
329
- private trimMessages(session: Session): void {
330
- const maxHistory = session.config.maxHistory ?? this.config.maxHistory;
331
- if (session.messages.length > maxHistory) {
332
- const systemMessages = session.messages.filter(m => m.role === 'system');
333
- const otherMessages = session.messages.filter(m => m.role !== 'system');
334
- const keepCount = maxHistory - systemMessages.length;
335
- session.messages = [...systemMessages, ...otherMessages.slice(-keepCount)];
336
- }
337
- }
338
-
339
- async getMessages(sessionId: string): Promise<ChatMessage[]> {
340
- const session = await this.get(sessionId);
341
- return session.messages;
342
- }
343
-
344
- async setSystemPrompt(sessionId: string, prompt: string): Promise<void> {
345
- const session = await this.get(sessionId);
346
- session.messages = session.messages.filter(m => m.role !== 'system');
347
- session.messages.unshift({ role: 'system', content: prompt });
348
- session.updatedAt = Date.now();
349
- this.schedulesSave(session);
350
- }
351
-
352
- async clear(sessionId: string): Promise<boolean> {
353
- this.cache.delete(sessionId);
354
- this.saveQueue.delete(sessionId);
355
-
356
- try {
357
- await this.model.delete({ session_id: sessionId });
358
- return true;
359
- } catch (error) {
360
- logger.debug(`删除会话 ${sessionId} 失败:`, error);
361
- return false;
362
- }
363
- }
364
-
365
- async reset(sessionId: string): Promise<void> {
366
- const session = await this.get(sessionId);
367
- const systemMessages = session.messages.filter(m => m.role === 'system');
368
- session.messages = systemMessages;
369
- session.updatedAt = Date.now();
370
- this.schedulesSave(session);
371
- }
372
-
373
- async listSessions(): Promise<string[]> {
374
- try {
375
- const records = await this.model.select();
376
- return (records as SessionRecord[]).map(r => r.session_id);
377
- } catch (error) {
378
- logger.debug('列出会话失败:', error);
379
- return Array.from(this.cache.keys());
380
- }
381
- }
382
-
383
- async getStats(): Promise<{ total: number; active: number; expired: number }> {
384
- const now = Date.now();
385
- let total = 0;
386
- let active = 0;
387
- let expired = 0;
388
-
389
- try {
390
- const records = await this.model.select() as SessionRecord[];
391
- total = records.length;
392
-
393
- for (const record of records) {
394
- const expireMs = record.config?.expireMs ?? this.config.expireMs;
395
- if (now - record.updated_at > expireMs) {
396
- expired++;
397
- } else {
398
- active++;
399
- }
400
- }
401
- } catch (error) {
402
- logger.debug('获取统计失败:', error);
403
- // 回退到缓存统计
404
- total = this.cache.size;
405
- for (const session of this.cache.values()) {
406
- if (now - session.updatedAt > this.config.expireMs) {
407
- expired++;
408
- } else {
409
- active++;
410
- }
411
- }
412
- }
413
-
414
- return { total, active, expired };
415
- }
416
-
417
- async cleanup(): Promise<number> {
418
- const now = Date.now();
419
- let cleaned = 0;
420
-
421
- try {
422
- const records = await this.model.select() as SessionRecord[];
423
-
424
- for (const record of records) {
425
- const expireMs = record.config?.expireMs ?? this.config.expireMs;
426
- if (now - record.updated_at > expireMs) {
427
- await this.model.delete({ session_id: record.session_id });
428
- this.cache.delete(record.session_id);
429
- cleaned++;
430
- }
431
- }
432
- } catch (error) {
433
- logger.debug('清理会话失败:', error);
434
- }
435
-
436
- return cleaned;
437
- }
438
-
439
- async dispose(): Promise<void> {
440
- // 保存所有待保存的会话
441
- if (this.saveTimer) {
442
- clearTimeout(this.saveTimer);
443
- this.saveTimer = undefined;
444
- }
445
- await this.flushSaveQueue();
446
-
447
- if (this.cleanupTimer) {
448
- clearInterval(this.cleanupTimer);
449
- this.cleanupTimer = undefined;
450
- }
451
-
452
- this.cache.clear();
453
- }
454
- }
455
-
456
- /**
457
- * 会话管理器包装器
458
- * 支持同步和异步接口的统一使用
459
- */
460
- export class SessionManager implements ISessionManager {
461
- private manager: ISessionManager;
462
-
463
- constructor(manager: ISessionManager) {
464
- this.manager = manager;
465
- }
466
-
467
- /**
468
- * 生成会话 ID
469
- */
470
- static generateId(platform: string, userId: string, channelId?: string): string {
471
- return channelId
472
- ? `${platform}:${channelId}:${userId}`
473
- : `${platform}:${userId}`;
474
- }
475
-
476
- get(sessionId: string, config?: SessionConfig): Session | Promise<Session> {
477
- return this.manager.get(sessionId, config);
478
- }
479
-
480
- has(sessionId: string): boolean | Promise<boolean> {
481
- return this.manager.has(sessionId);
482
- }
483
-
484
- addMessage(sessionId: string, message: ChatMessage): void | Promise<void> {
485
- return this.manager.addMessage(sessionId, message);
486
- }
487
-
488
- getMessages(sessionId: string): ChatMessage[] | Promise<ChatMessage[]> {
489
- return this.manager.getMessages(sessionId);
490
- }
491
-
492
- setSystemPrompt(sessionId: string, prompt: string): void | Promise<void> {
493
- return this.manager.setSystemPrompt(sessionId, prompt);
494
- }
495
-
496
- clear(sessionId: string): boolean | Promise<boolean> {
497
- return this.manager.clear(sessionId);
498
- }
499
-
500
- reset(sessionId: string): void | Promise<void> {
501
- return this.manager.reset(sessionId);
502
- }
503
-
504
- listSessions(): string[] | Promise<string[]> {
505
- return this.manager.listSessions();
506
- }
507
-
508
- getStats(): { total: number; active: number; expired: number } | Promise<{ total: number; active: number; expired: number }> {
509
- return this.manager.getStats();
510
- }
511
-
512
- cleanup(): number | Promise<number> {
513
- return this.manager.cleanup();
514
- }
515
-
516
- dispose(): void | Promise<void> {
517
- return this.manager.dispose();
518
- }
519
- }
520
-
521
- /**
522
- * 创建内存会话管理器(回退方案)
523
- */
524
- export function createMemorySessionManager(config?: { maxHistory?: number; expireMs?: number }): SessionManager {
525
- return new SessionManager(new MemorySessionManager(config));
526
- }
527
-
528
- /**
529
- * 创建数据库会话管理器
530
- */
531
- export function createDatabaseSessionManager(
532
- model: any,
533
- config?: { maxHistory?: number; expireMs?: number }
534
- ): SessionManager {
535
- return new SessionManager(new DatabaseSessionManager(model, config));
536
- }
537
-
538
- /**
539
- * 创建会话管理器(向后兼容)
540
- * @deprecated 使用 createMemorySessionManager 或 createDatabaseSessionManager
541
- */
542
- export function createSessionManager(config?: { maxHistory?: number; expireMs?: number }): SessionManager {
543
- return createMemorySessionManager(config);
544
- }