foliko 1.0.81 → 1.0.83

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 (88) hide show
  1. package/cli/bin/foliko.js +12 -12
  2. package/cli/src/commands/chat.js +143 -143
  3. package/cli/src/commands/list.js +93 -93
  4. package/cli/src/index.js +75 -75
  5. package/cli/src/ui/chat-ui.js +201 -201
  6. package/cli/src/utils/ansi.js +40 -40
  7. package/cli/src/utils/markdown.js +292 -292
  8. package/examples/ambient-example.js +194 -194
  9. package/examples/basic.js +115 -115
  10. package/examples/bootstrap.js +121 -121
  11. package/examples/mcp-example.js +56 -56
  12. package/examples/skill-example.js +49 -49
  13. package/examples/test-chat.js +137 -137
  14. package/examples/test-mcp.js +85 -85
  15. package/examples/test-reload.js +59 -59
  16. package/examples/test-telegram.js +50 -50
  17. package/examples/test-tg-bot.js +45 -45
  18. package/examples/test-tg-simple.js +47 -47
  19. package/examples/test-tg.js +62 -62
  20. package/examples/test-think.js +43 -43
  21. package/examples/test-web-plugin.js +103 -103
  22. package/examples/test-weixin-feishu.js +103 -103
  23. package/examples/workflow.js +158 -158
  24. package/package.json +83 -83
  25. package/plugins/ai-plugin.js +102 -102
  26. package/plugins/ambient-agent/EventWatcher.js +113 -113
  27. package/plugins/ambient-agent/ExplorerLoop.js +640 -640
  28. package/plugins/ambient-agent/GoalManager.js +197 -197
  29. package/plugins/ambient-agent/Reflector.js +95 -95
  30. package/plugins/ambient-agent/StateStore.js +90 -90
  31. package/plugins/ambient-agent/constants.js +101 -101
  32. package/plugins/ambient-agent/index.js +579 -579
  33. package/plugins/audit-plugin.js +187 -187
  34. package/plugins/default-plugins.js +548 -548
  35. package/plugins/email/constants.js +64 -64
  36. package/plugins/email/handlers.js +461 -461
  37. package/plugins/email/index.js +278 -278
  38. package/plugins/email/monitor.js +269 -269
  39. package/plugins/email/parser.js +138 -138
  40. package/plugins/email/reply.js +151 -151
  41. package/plugins/email/utils.js +124 -124
  42. package/plugins/extension-executor-plugin.js +326 -326
  43. package/plugins/feishu-plugin.js +481 -481
  44. package/plugins/file-system-plugin.js +920 -920
  45. package/plugins/gate-trading.js +747 -747
  46. package/plugins/install-plugin.js +199 -199
  47. package/plugins/python-executor-plugin.js +367 -367
  48. package/plugins/python-plugin-loader.js +651 -651
  49. package/plugins/rules-plugin.js +294 -294
  50. package/plugins/scheduler-plugin.js +691 -691
  51. package/plugins/session-plugin.js +494 -494
  52. package/plugins/shell-executor-plugin.js +197 -197
  53. package/plugins/storage-plugin.js +263 -263
  54. package/plugins/subagent-plugin.js +845 -845
  55. package/plugins/telegram-plugin.js +482 -482
  56. package/plugins/think-plugin.js +345 -345
  57. package/plugins/tools-plugin.js +196 -196
  58. package/plugins/web-plugin.js +637 -637
  59. package/plugins/weixin-plugin.js +545 -545
  60. package/src/capabilities/index.js +11 -11
  61. package/src/capabilities/skill-manager.js +609 -609
  62. package/src/capabilities/workflow-engine.js +1109 -1109
  63. package/src/core/agent-chat.js +1 -1
  64. package/src/core/agent.js +958 -958
  65. package/src/core/framework.js +465 -465
  66. package/src/core/index.js +19 -19
  67. package/src/core/plugin-base.js +262 -262
  68. package/src/core/plugin-manager.js +863 -863
  69. package/src/core/provider.js +114 -114
  70. package/src/core/sub-agent-config.js +264 -264
  71. package/src/core/system-prompt-builder.js +120 -120
  72. package/src/core/tool-registry.js +517 -517
  73. package/src/core/tool-router.js +297 -297
  74. package/src/executors/executor-base.js +58 -58
  75. package/src/executors/mcp-executor.js +845 -845
  76. package/src/index.js +25 -25
  77. package/src/utils/circuit-breaker.js +301 -301
  78. package/src/utils/error-boundary.js +363 -363
  79. package/src/utils/error.js +374 -374
  80. package/src/utils/event-emitter.js +97 -97
  81. package/src/utils/id.js +133 -133
  82. package/src/utils/index.js +217 -217
  83. package/src/utils/logger.js +181 -181
  84. package/src/utils/plugin-helpers.js +90 -90
  85. package/src/utils/retry.js +122 -122
  86. package/src/utils/sandbox.js +292 -292
  87. package/test/tool-registry-validation.test.js +218 -218
  88. package/website/script.js +136 -136
@@ -1,495 +1,495 @@
1
- /**
2
- * Session 会话管理插件
3
- * 管理多个用户会话,支持会话隔离和历史记录
4
- */
5
-
6
- const { Plugin } = require('../src/core/plugin-base')
7
- const { logger } = require('../src/utils/logger')
8
- const log = logger.child('Session')
9
- const { z } = require('zod')
10
- const { EventEmitter } = require('events')
11
-
12
- /**
13
- * 生成 UUID
14
- */
15
- function generateId() {
16
- if (require('crypto').randomUUID) {
17
- return require('crypto').randomUUID()
18
- }
19
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
20
- const r = Math.random() * 16 | 0
21
- const v = c === 'x' ? r : (r & 0x3 | 0x8)
22
- return v.toString(16)
23
- })
24
- }
25
-
26
- class SessionPlugin extends Plugin {
27
- constructor(config = {}) {
28
- super()
29
- this.name = 'session'
30
- this.version = '1.0.0'
31
- this.description = '会话管理插件,支持多会话隔离、历史记录、会话超时清理'
32
- this.priority = 5
33
-
34
- this.system = true
35
-
36
- this.config = {
37
- sessionTTL: config.sessionTTL || 30 * 60 * 1000, // 30分钟
38
- maxSessions: config.maxSessions || 100,
39
- maxHistoryLength: config.maxHistoryLength || 150, // 放宽到 150,Agent 已有智能压缩
40
- autoCleanup: config.autoCleanup||false, // 默认不开启自动清理,避免误删会话
41
- cleanupInterval: config.cleanupInterval || 5 * 60 * 1000, // 5分钟
42
- persistToStorage: config.persistToStorage !== false // 默认持久化到 storage
43
- }
44
- // console.log('SessionPlugin config:', this.config)
45
- this._framework = null
46
- this._sessions = new Map()
47
- this._cleanupTimer = null
48
- this._events = new EventEmitter()
49
- this._storageKeyPrefix = 'session:'
50
- }
51
-
52
- install(framework) {
53
- this._framework = framework
54
- return this
55
- }
56
-
57
- /**
58
- * 获取存储实例
59
- * @private
60
- */
61
- _getStorage() {
62
- if (!this._framework) return null;
63
- return this._framework.pluginManager.get('storage');
64
- }
65
-
66
- /**
67
- * 从 storage 加载会话数据
68
- * @private
69
- */
70
- _loadFromStorage(sessionId) {
71
- const storage = this._getStorage();
72
- if (!storage) return null;
73
-
74
- try {
75
- const store = storage.getStore();
76
- const data = store.get(this._storageKeyPrefix + sessionId);
77
- if (data && data.messages) {
78
- return data;
79
- }
80
- } catch (err) {
81
- // 忽略加载错误
82
- }
83
- return null;
84
- }
85
-
86
- /**
87
- * 保存会话数据到 storage
88
- * @private
89
- */
90
- _saveToStorage(session) {
91
- const storage = this._getStorage();
92
- if (!storage || !this.config.persistToStorage) return;
93
-
94
- try {
95
- storage.setDirect(this._storageKeyPrefix + session.id, {
96
- messages: session.messages,
97
- variables: session.variables,
98
- metadata: session.metadata,
99
- createdAt: session.createdAt,
100
- lastActive: session.lastActive
101
- });
102
- } catch (err) {
103
- // 忽略保存错误
104
- }
105
- }
106
-
107
- /**
108
- * 从 storage 删除会话数据
109
- * @private
110
- */
111
- _deleteFromStorage(sessionId) {
112
- const storage = this._getStorage();
113
- if (!storage) return;
114
-
115
- try {
116
- storage.deleteDirect(this._storageKeyPrefix + sessionId);
117
- } catch (err) {
118
- // 忽略删除错误
119
- }
120
- }
121
-
122
- /**
123
- * 从 storage 加载所有会话
124
- * @private
125
- */
126
- _loadAllFromStorage() {
127
- const storage = this._getStorage();
128
- if (!storage) return;
129
-
130
- try {
131
- const store = storage.getStore();
132
- // 遍历 storage 找出所有 session: 开头的键
133
- for (const [key, value] of store.entries()) {
134
- if (key.startsWith(this._storageKeyPrefix)) {
135
- const sessionId = key.substring(this._storageKeyPrefix.length);
136
- const sessionData = value;
137
- if (sessionId && sessionData && sessionData.messages) {
138
- // 恢复会话到内存
139
- this._sessions.set(sessionId, {
140
- id: sessionId,
141
- messages: sessionData.messages || [],
142
- variables: sessionData.variables || {},
143
- metadata: sessionData.metadata || {},
144
- createdAt: sessionData.createdAt ? new Date(sessionData.createdAt) : new Date(),
145
- lastActive: sessionData.lastActive ? new Date(sessionData.lastActive) : new Date()
146
- });
147
- }
148
- }
149
- }
150
- log.info(`Loaded ${this._sessions.size} sessions from storage`);
151
- } catch (err) {
152
- log.warn('Failed to load sessions from storage:', err.message);
153
- }
154
- }
155
-
156
- start(framework) {
157
- // 从 storage 加载所有会话
158
- this._loadAllFromStorage();
159
- // 注册会话管理工具
160
- framework.registerTool({
161
- name: 'session_create',
162
- description: '创建新会话',
163
- inputSchema: z.object({
164
- sessionId: z.string().optional().describe('可选:指定会话 ID'),
165
- metadata: z.object({}).optional().describe('会话元数据')
166
- }),
167
- execute: async (args) => {
168
- const sessionId = args.sessionId || `session_${generateId()}`
169
- const session = this.createSession(sessionId, { metadata: args.metadata })
170
- return {
171
- success: true,
172
- sessionId: session.id,
173
- createdAt: session.createdAt
174
- }
175
- }
176
- })
177
-
178
- framework.registerTool({
179
- name: 'session_get',
180
- description: '获取会话信息',
181
- inputSchema: z.object({
182
- sessionId: z.string().describe('会话 ID')
183
- }),
184
- execute: async (args) => {
185
- const session = this.getSession(args.sessionId)
186
- if (!session) {
187
- return { success: false, error: 'Session not found' }
188
- }
189
- return {
190
- success: true,
191
- sessionId: session.id,
192
- messageCount: session.messages.length,
193
- variables: Object.keys(session.variables),
194
- createdAt: session.createdAt,
195
- lastActive: session.lastActive
196
- }
197
- }
198
- })
199
-
200
- framework.registerTool({
201
- name: 'session_list',
202
- description: '列出所有会话',
203
- inputSchema: z.object({}),
204
- execute: async () => {
205
- const sessions = this.listSessions()
206
- return {
207
- success: true,
208
- sessions,
209
- total: sessions.length
210
- }
211
- }
212
- })
213
-
214
- framework.registerTool({
215
- name: 'session_delete',
216
- description: '删除会话',
217
- inputSchema: z.object({
218
- sessionId: z.string().describe('会话 ID')
219
- }),
220
- execute: async (args) => {
221
- const deleted = this.deleteSession(args.sessionId)
222
- return { success: deleted, deleted: args.sessionId }
223
- }
224
- })
225
-
226
- framework.registerTool({
227
- name: 'session_history',
228
- description: '获取会话历史消息',
229
- inputSchema: z.object({
230
- sessionId: z.string().describe('会话 ID'),
231
- limit: z.number().optional().describe('返回消息数量限制')
232
- }),
233
- execute: async (args) => {
234
- const history = this.getHistory(args.sessionId, args.limit)
235
- return {
236
- success: true,
237
- sessionId: args.sessionId,
238
- messages: history,
239
- count: history.length
240
- }
241
- }
242
- })
243
-
244
- framework.registerTool({
245
- name: 'session_stats',
246
- description: '获取会话统计信息',
247
- inputSchema: z.object({}),
248
- execute: async () => {
249
- return {
250
- success: true,
251
- stats: this.getMemoryStats()
252
- }
253
- }
254
- })
255
-
256
- // 启动自动清理
257
- if (this.config.autoCleanup) {
258
- this._startAutoCleanup()
259
- }
260
-
261
- return this
262
- }
263
-
264
- /**
265
- * 创建新会话
266
- */
267
- createSession(sessionId, options = {}) {
268
- const id = sessionId || `session_${generateId()}`
269
-
270
- if (this._sessions.has(id)) {
271
- return this._sessions.get(id)
272
- }
273
-
274
- // 如果会话数超过限制,清理最老的
275
- if (this._sessions.size >= this.config.maxSessions) {
276
- this._cleanupLRU()
277
- }
278
-
279
- const session = {
280
- id,
281
- messages: options.initialMessages || [],
282
- variables: options.variables || {},
283
- metadata: options.metadata || {},
284
- createdAt: new Date(),
285
- lastActive: new Date()
286
- }
287
-
288
- this._sessions.set(id, session)
289
- this._saveToStorage(session)
290
- return session
291
- }
292
-
293
- /**
294
- * 获取会话
295
- */
296
- getSession(sessionId) {
297
- let session = this._sessions.get(sessionId)
298
- if (session) {
299
- session.lastActive = new Date()
300
- return session
301
- }
302
-
303
- // 尝试从 storage 加载
304
- const storageData = this._loadFromStorage(sessionId)
305
- if (storageData) {
306
- session = {
307
- id: sessionId,
308
- messages: storageData.messages || [],
309
- variables: storageData.variables || {},
310
- metadata: storageData.metadata || {},
311
- createdAt: storageData.createdAt ? new Date(storageData.createdAt) : new Date(),
312
- lastActive: new Date()
313
- }
314
- this._sessions.set(sessionId, session)
315
- return session
316
- }
317
-
318
- return null
319
- }
320
-
321
- /**
322
- * 获取或创建会话
323
- */
324
- getOrCreateSession(sessionId, options = {}) {
325
- let session = this.getSession(sessionId)
326
- if (!session) {
327
- session = this.createSession(sessionId, options)
328
- }
329
- return session
330
- }
331
-
332
- /**
333
- * 添加消息到会话
334
- */
335
- addMessage(sessionId, message) {
336
- const session = this.getSession(sessionId)
337
- if (session) {
338
- session.messages.push(message)
339
- session.lastActive = new Date()
340
-
341
- // 限制历史长度
342
- if (session.messages.length > this.config.maxHistoryLength) {
343
- session.messages = session.messages.slice(-this.config.maxHistoryLength)
344
- }
345
-
346
- // 持久化到 storage
347
- this._saveToStorage(session)
348
- }
349
- return session
350
- }
351
-
352
- /**
353
- * 获取会话历史
354
- */
355
- getHistory(sessionId, limit = null) {
356
- const session = this.getSession(sessionId)
357
- if (!session) return []
358
-
359
- const messages = session.messages
360
- if (limit) {
361
- return messages.slice(-limit)
362
- }
363
- return messages
364
- }
365
-
366
- /**
367
- * 删除会话
368
- */
369
- deleteSession(sessionId) {
370
- const deleted = this._sessions.delete(sessionId)
371
- if (deleted) {
372
- this._deleteFromStorage(sessionId)
373
- this._events.emit('session:deleted', sessionId)
374
- }
375
- return deleted
376
- }
377
-
378
- /**
379
- * 列出所有会话
380
- */
381
- listSessions() {
382
- return Array.from(this._sessions.values()).map(s => ({
383
- id: s.id,
384
- messageCount: s.messages.length,
385
- createdAt: s.createdAt,
386
- lastActive: s.lastActive,
387
- metadata: s.metadata
388
- }))
389
- }
390
-
391
- /**
392
- * 清理过期会话
393
- */
394
- cleanup() {
395
- const now = Date.now()
396
- let cleanedCount = 0
397
-
398
- for (const [sessionId, session] of this._sessions) {
399
- if (now - session.lastActive.getTime() > this.config.sessionTTL) {
400
- this._sessions.delete(sessionId)
401
- cleanedCount++
402
- }
403
- }
404
-
405
- return cleanedCount
406
- }
407
-
408
- /**
409
- * LRU 清理 - 删除最老的 20% 会话
410
- */
411
- _cleanupLRU() {
412
- const sessions = Array.from(this._sessions.values())
413
- if (sessions.length === 0) return
414
-
415
- // 按最后活跃时间排序
416
- sessions.sort((a, b) => a.lastActive.getTime() - b.lastActive.getTime())
417
-
418
- // 删除最老的会话
419
- const toDelete = sessions.slice(0, Math.ceil(sessions.length * 0.2))
420
- for (const session of toDelete) {
421
- this._sessions.delete(session.id)
422
- }
423
- }
424
-
425
- /**
426
- * 获取内存统计
427
- */
428
- getMemoryStats() {
429
- const sessionCount = this._sessions.size
430
- const totalMessages = Array.from(this._sessions.values()).reduce(
431
- (sum, s) => sum + s.messages.length, 0
432
- )
433
-
434
- return {
435
- sessionCount,
436
- maxSessions: this.config.maxSessions,
437
- totalMessages,
438
- ttl: this.config.sessionTTL
439
- }
440
- }
441
-
442
- /**
443
- * 清理所有会话
444
- */
445
- clear() {
446
- const sessionIds = Array.from(this._sessions.keys())
447
- this._sessions.clear()
448
- sessionIds.forEach(id => this._events.emit('session:deleted', id))
449
- }
450
-
451
- /**
452
- * 监听会话事件
453
- */
454
- on(event, listener) {
455
- this._events.on(event, listener)
456
- }
457
-
458
- /**
459
- * 移除会话事件监听
460
- */
461
- off(event, listener) {
462
- this._events.off(event, listener)
463
- }
464
-
465
- /**
466
- * 启动自动清理定时器
467
- */
468
- _startAutoCleanup() {
469
- if (this._cleanupTimer) {
470
- clearInterval(this._cleanupTimer)
471
- }
472
- this._cleanupTimer = setInterval(() => {
473
- const cleaned = this.cleanup()
474
- if (cleaned > 0) {
475
- log.info(` Cleaned up ${cleaned} expired sessions`)
476
- }
477
- }, this.config.cleanupInterval)
478
- }
479
-
480
- reload(framework) {
481
- this._framework = framework
482
- }
483
-
484
- uninstall(framework) {
485
- if (this._cleanupTimer) {
486
- clearInterval(this._cleanupTimer)
487
- this._cleanupTimer = null
488
- }
489
- this._events.removeAllListeners()
490
- this._sessions.clear()
491
- this._framework = null
492
- }
493
- }
494
-
1
+ /**
2
+ * Session 会话管理插件
3
+ * 管理多个用户会话,支持会话隔离和历史记录
4
+ */
5
+
6
+ const { Plugin } = require('../src/core/plugin-base')
7
+ const { logger } = require('../src/utils/logger')
8
+ const log = logger.child('Session')
9
+ const { z } = require('zod')
10
+ const { EventEmitter } = require('events')
11
+
12
+ /**
13
+ * 生成 UUID
14
+ */
15
+ function generateId() {
16
+ if (require('crypto').randomUUID) {
17
+ return require('crypto').randomUUID()
18
+ }
19
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
20
+ const r = Math.random() * 16 | 0
21
+ const v = c === 'x' ? r : (r & 0x3 | 0x8)
22
+ return v.toString(16)
23
+ })
24
+ }
25
+
26
+ class SessionPlugin extends Plugin {
27
+ constructor(config = {}) {
28
+ super()
29
+ this.name = 'session'
30
+ this.version = '1.0.0'
31
+ this.description = '会话管理插件,支持多会话隔离、历史记录、会话超时清理'
32
+ this.priority = 5
33
+
34
+ this.system = true
35
+
36
+ this.config = {
37
+ sessionTTL: config.sessionTTL || 30 * 60 * 1000, // 30分钟
38
+ maxSessions: config.maxSessions || 100,
39
+ maxHistoryLength: config.maxHistoryLength || 150, // 放宽到 150,Agent 已有智能压缩
40
+ autoCleanup: config.autoCleanup||false, // 默认不开启自动清理,避免误删会话
41
+ cleanupInterval: config.cleanupInterval || 5 * 60 * 1000, // 5分钟
42
+ persistToStorage: config.persistToStorage !== false // 默认持久化到 storage
43
+ }
44
+ // console.log('SessionPlugin config:', this.config)
45
+ this._framework = null
46
+ this._sessions = new Map()
47
+ this._cleanupTimer = null
48
+ this._events = new EventEmitter()
49
+ this._storageKeyPrefix = 'session:'
50
+ }
51
+
52
+ install(framework) {
53
+ this._framework = framework
54
+ return this
55
+ }
56
+
57
+ /**
58
+ * 获取存储实例
59
+ * @private
60
+ */
61
+ _getStorage() {
62
+ if (!this._framework) return null;
63
+ return this._framework.pluginManager.get('storage');
64
+ }
65
+
66
+ /**
67
+ * 从 storage 加载会话数据
68
+ * @private
69
+ */
70
+ _loadFromStorage(sessionId) {
71
+ const storage = this._getStorage();
72
+ if (!storage) return null;
73
+
74
+ try {
75
+ const store = storage.getStore();
76
+ const data = store.get(this._storageKeyPrefix + sessionId);
77
+ if (data && data.messages) {
78
+ return data;
79
+ }
80
+ } catch (err) {
81
+ // 忽略加载错误
82
+ }
83
+ return null;
84
+ }
85
+
86
+ /**
87
+ * 保存会话数据到 storage
88
+ * @private
89
+ */
90
+ _saveToStorage(session) {
91
+ const storage = this._getStorage();
92
+ if (!storage || !this.config.persistToStorage) return;
93
+
94
+ try {
95
+ storage.setDirect(this._storageKeyPrefix + session.id, {
96
+ messages: session.messages,
97
+ variables: session.variables,
98
+ metadata: session.metadata,
99
+ createdAt: session.createdAt,
100
+ lastActive: session.lastActive
101
+ });
102
+ } catch (err) {
103
+ // 忽略保存错误
104
+ }
105
+ }
106
+
107
+ /**
108
+ * 从 storage 删除会话数据
109
+ * @private
110
+ */
111
+ _deleteFromStorage(sessionId) {
112
+ const storage = this._getStorage();
113
+ if (!storage) return;
114
+
115
+ try {
116
+ storage.deleteDirect(this._storageKeyPrefix + sessionId);
117
+ } catch (err) {
118
+ // 忽略删除错误
119
+ }
120
+ }
121
+
122
+ /**
123
+ * 从 storage 加载所有会话
124
+ * @private
125
+ */
126
+ _loadAllFromStorage() {
127
+ const storage = this._getStorage();
128
+ if (!storage) return;
129
+
130
+ try {
131
+ const store = storage.getStore();
132
+ // 遍历 storage 找出所有 session: 开头的键
133
+ for (const [key, value] of store.entries()) {
134
+ if (key.startsWith(this._storageKeyPrefix)) {
135
+ const sessionId = key.substring(this._storageKeyPrefix.length);
136
+ const sessionData = value;
137
+ if (sessionId && sessionData && sessionData.messages) {
138
+ // 恢复会话到内存
139
+ this._sessions.set(sessionId, {
140
+ id: sessionId,
141
+ messages: sessionData.messages || [],
142
+ variables: sessionData.variables || {},
143
+ metadata: sessionData.metadata || {},
144
+ createdAt: sessionData.createdAt ? new Date(sessionData.createdAt) : new Date(),
145
+ lastActive: sessionData.lastActive ? new Date(sessionData.lastActive) : new Date()
146
+ });
147
+ }
148
+ }
149
+ }
150
+ log.info(`Loaded ${this._sessions.size} sessions from storage`);
151
+ } catch (err) {
152
+ log.warn('Failed to load sessions from storage:', err.message);
153
+ }
154
+ }
155
+
156
+ start(framework) {
157
+ // 从 storage 加载所有会话
158
+ this._loadAllFromStorage();
159
+ // 注册会话管理工具
160
+ framework.registerTool({
161
+ name: 'session_create',
162
+ description: '创建新会话',
163
+ inputSchema: z.object({
164
+ sessionId: z.string().optional().describe('可选:指定会话 ID'),
165
+ metadata: z.object({}).optional().describe('会话元数据')
166
+ }),
167
+ execute: async (args) => {
168
+ const sessionId = args.sessionId || `session_${generateId()}`
169
+ const session = this.createSession(sessionId, { metadata: args.metadata })
170
+ return {
171
+ success: true,
172
+ sessionId: session.id,
173
+ createdAt: session.createdAt
174
+ }
175
+ }
176
+ })
177
+
178
+ framework.registerTool({
179
+ name: 'session_get',
180
+ description: '获取会话信息',
181
+ inputSchema: z.object({
182
+ sessionId: z.string().describe('会话 ID')
183
+ }),
184
+ execute: async (args) => {
185
+ const session = this.getSession(args.sessionId)
186
+ if (!session) {
187
+ return { success: false, error: 'Session not found' }
188
+ }
189
+ return {
190
+ success: true,
191
+ sessionId: session.id,
192
+ messageCount: session.messages.length,
193
+ variables: Object.keys(session.variables),
194
+ createdAt: session.createdAt,
195
+ lastActive: session.lastActive
196
+ }
197
+ }
198
+ })
199
+
200
+ framework.registerTool({
201
+ name: 'session_list',
202
+ description: '列出所有会话',
203
+ inputSchema: z.object({}),
204
+ execute: async () => {
205
+ const sessions = this.listSessions()
206
+ return {
207
+ success: true,
208
+ sessions,
209
+ total: sessions.length
210
+ }
211
+ }
212
+ })
213
+
214
+ framework.registerTool({
215
+ name: 'session_delete',
216
+ description: '删除会话',
217
+ inputSchema: z.object({
218
+ sessionId: z.string().describe('会话 ID')
219
+ }),
220
+ execute: async (args) => {
221
+ const deleted = this.deleteSession(args.sessionId)
222
+ return { success: deleted, deleted: args.sessionId }
223
+ }
224
+ })
225
+
226
+ framework.registerTool({
227
+ name: 'session_history',
228
+ description: '获取会话历史消息',
229
+ inputSchema: z.object({
230
+ sessionId: z.string().describe('会话 ID'),
231
+ limit: z.number().optional().describe('返回消息数量限制')
232
+ }),
233
+ execute: async (args) => {
234
+ const history = this.getHistory(args.sessionId, args.limit)
235
+ return {
236
+ success: true,
237
+ sessionId: args.sessionId,
238
+ messages: history,
239
+ count: history.length
240
+ }
241
+ }
242
+ })
243
+
244
+ framework.registerTool({
245
+ name: 'session_stats',
246
+ description: '获取会话统计信息',
247
+ inputSchema: z.object({}),
248
+ execute: async () => {
249
+ return {
250
+ success: true,
251
+ stats: this.getMemoryStats()
252
+ }
253
+ }
254
+ })
255
+
256
+ // 启动自动清理
257
+ if (this.config.autoCleanup) {
258
+ this._startAutoCleanup()
259
+ }
260
+
261
+ return this
262
+ }
263
+
264
+ /**
265
+ * 创建新会话
266
+ */
267
+ createSession(sessionId, options = {}) {
268
+ const id = sessionId || `session_${generateId()}`
269
+
270
+ if (this._sessions.has(id)) {
271
+ return this._sessions.get(id)
272
+ }
273
+
274
+ // 如果会话数超过限制,清理最老的
275
+ if (this._sessions.size >= this.config.maxSessions) {
276
+ this._cleanupLRU()
277
+ }
278
+
279
+ const session = {
280
+ id,
281
+ messages: options.initialMessages || [],
282
+ variables: options.variables || {},
283
+ metadata: options.metadata || {},
284
+ createdAt: new Date(),
285
+ lastActive: new Date()
286
+ }
287
+
288
+ this._sessions.set(id, session)
289
+ this._saveToStorage(session)
290
+ return session
291
+ }
292
+
293
+ /**
294
+ * 获取会话
295
+ */
296
+ getSession(sessionId) {
297
+ let session = this._sessions.get(sessionId)
298
+ if (session) {
299
+ session.lastActive = new Date()
300
+ return session
301
+ }
302
+
303
+ // 尝试从 storage 加载
304
+ const storageData = this._loadFromStorage(sessionId)
305
+ if (storageData) {
306
+ session = {
307
+ id: sessionId,
308
+ messages: storageData.messages || [],
309
+ variables: storageData.variables || {},
310
+ metadata: storageData.metadata || {},
311
+ createdAt: storageData.createdAt ? new Date(storageData.createdAt) : new Date(),
312
+ lastActive: new Date()
313
+ }
314
+ this._sessions.set(sessionId, session)
315
+ return session
316
+ }
317
+
318
+ return null
319
+ }
320
+
321
+ /**
322
+ * 获取或创建会话
323
+ */
324
+ getOrCreateSession(sessionId, options = {}) {
325
+ let session = this.getSession(sessionId)
326
+ if (!session) {
327
+ session = this.createSession(sessionId, options)
328
+ }
329
+ return session
330
+ }
331
+
332
+ /**
333
+ * 添加消息到会话
334
+ */
335
+ addMessage(sessionId, message) {
336
+ const session = this.getSession(sessionId)
337
+ if (session) {
338
+ session.messages.push(message)
339
+ session.lastActive = new Date()
340
+
341
+ // 限制历史长度
342
+ if (session.messages.length > this.config.maxHistoryLength) {
343
+ session.messages = session.messages.slice(-this.config.maxHistoryLength)
344
+ }
345
+
346
+ // 持久化到 storage
347
+ this._saveToStorage(session)
348
+ }
349
+ return session
350
+ }
351
+
352
+ /**
353
+ * 获取会话历史
354
+ */
355
+ getHistory(sessionId, limit = null) {
356
+ const session = this.getSession(sessionId)
357
+ if (!session) return []
358
+
359
+ const messages = session.messages
360
+ if (limit) {
361
+ return messages.slice(-limit)
362
+ }
363
+ return messages
364
+ }
365
+
366
+ /**
367
+ * 删除会话
368
+ */
369
+ deleteSession(sessionId) {
370
+ const deleted = this._sessions.delete(sessionId)
371
+ if (deleted) {
372
+ this._deleteFromStorage(sessionId)
373
+ this._events.emit('session:deleted', sessionId)
374
+ }
375
+ return deleted
376
+ }
377
+
378
+ /**
379
+ * 列出所有会话
380
+ */
381
+ listSessions() {
382
+ return Array.from(this._sessions.values()).map(s => ({
383
+ id: s.id,
384
+ messageCount: s.messages.length,
385
+ createdAt: s.createdAt,
386
+ lastActive: s.lastActive,
387
+ metadata: s.metadata
388
+ }))
389
+ }
390
+
391
+ /**
392
+ * 清理过期会话
393
+ */
394
+ cleanup() {
395
+ const now = Date.now()
396
+ let cleanedCount = 0
397
+
398
+ for (const [sessionId, session] of this._sessions) {
399
+ if (now - session.lastActive.getTime() > this.config.sessionTTL) {
400
+ this._sessions.delete(sessionId)
401
+ cleanedCount++
402
+ }
403
+ }
404
+
405
+ return cleanedCount
406
+ }
407
+
408
+ /**
409
+ * LRU 清理 - 删除最老的 20% 会话
410
+ */
411
+ _cleanupLRU() {
412
+ const sessions = Array.from(this._sessions.values())
413
+ if (sessions.length === 0) return
414
+
415
+ // 按最后活跃时间排序
416
+ sessions.sort((a, b) => a.lastActive.getTime() - b.lastActive.getTime())
417
+
418
+ // 删除最老的会话
419
+ const toDelete = sessions.slice(0, Math.ceil(sessions.length * 0.2))
420
+ for (const session of toDelete) {
421
+ this._sessions.delete(session.id)
422
+ }
423
+ }
424
+
425
+ /**
426
+ * 获取内存统计
427
+ */
428
+ getMemoryStats() {
429
+ const sessionCount = this._sessions.size
430
+ const totalMessages = Array.from(this._sessions.values()).reduce(
431
+ (sum, s) => sum + s.messages.length, 0
432
+ )
433
+
434
+ return {
435
+ sessionCount,
436
+ maxSessions: this.config.maxSessions,
437
+ totalMessages,
438
+ ttl: this.config.sessionTTL
439
+ }
440
+ }
441
+
442
+ /**
443
+ * 清理所有会话
444
+ */
445
+ clear() {
446
+ const sessionIds = Array.from(this._sessions.keys())
447
+ this._sessions.clear()
448
+ sessionIds.forEach(id => this._events.emit('session:deleted', id))
449
+ }
450
+
451
+ /**
452
+ * 监听会话事件
453
+ */
454
+ on(event, listener) {
455
+ this._events.on(event, listener)
456
+ }
457
+
458
+ /**
459
+ * 移除会话事件监听
460
+ */
461
+ off(event, listener) {
462
+ this._events.off(event, listener)
463
+ }
464
+
465
+ /**
466
+ * 启动自动清理定时器
467
+ */
468
+ _startAutoCleanup() {
469
+ if (this._cleanupTimer) {
470
+ clearInterval(this._cleanupTimer)
471
+ }
472
+ this._cleanupTimer = setInterval(() => {
473
+ const cleaned = this.cleanup()
474
+ if (cleaned > 0) {
475
+ log.info(` Cleaned up ${cleaned} expired sessions`)
476
+ }
477
+ }, this.config.cleanupInterval)
478
+ }
479
+
480
+ reload(framework) {
481
+ this._framework = framework
482
+ }
483
+
484
+ uninstall(framework) {
485
+ if (this._cleanupTimer) {
486
+ clearInterval(this._cleanupTimer)
487
+ this._cleanupTimer = null
488
+ }
489
+ this._events.removeAllListeners()
490
+ this._sessions.clear()
491
+ this._framework = null
492
+ }
493
+ }
494
+
495
495
  module.exports = SessionPlugin