foliko 1.1.67 → 1.1.69

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 (157) hide show
  1. package/.claude/settings.local.json +19 -10
  2. package/.dockerignore +45 -45
  3. package/.env.example +56 -56
  4. package/CLAUDE.md +2 -2
  5. package/README.md +13 -13
  6. package/SPEC.md +3 -3
  7. package/cli/src/commands/chat.js +2 -20
  8. package/cli/src/commands/list.js +7 -6
  9. package/cli/src/commands/plugin.js +3 -2
  10. package/cli/src/daemon.js +2 -2
  11. package/cli/src/ui/chat-ui-old.js +15 -4
  12. package/cli/src/ui/chat-ui.js +236 -203
  13. package/cli/src/ui/footer-bar.js +20 -46
  14. package/cli/src/ui/message-bubble.js +24 -2
  15. package/cli/src/ui/status-bar.js +177 -0
  16. package/cli/src/utils/config.js +29 -0
  17. package/cli/src/utils/plugin-config.js +1 -1
  18. package/docker-compose.yml +33 -33
  19. package/docs/features.md +120 -120
  20. package/docs/quick-reference.md +160 -160
  21. package/docs/user-manual.md +1391 -1391
  22. package/examples/ambient-example.js +2 -2
  23. package/examples/bootstrap.js +3 -3
  24. package/examples/test-chat.js +1 -1
  25. package/examples/test-reload.js +1 -1
  26. package/examples/test-telegram.js +1 -1
  27. package/examples/test-tg-bot.js +1 -1
  28. package/examples/test-tg-simple.js +2 -2
  29. package/examples/test-tg.js +1 -1
  30. package/examples/test-think.js +1 -1
  31. package/examples/test-weixin-feishu.js +3 -3
  32. package/package.json +3 -1
  33. package/plugins/ambient-agent/index.js +1 -1
  34. package/plugins/audit-plugin.js +84 -29
  35. package/plugins/coordinator-plugin.js +14 -12
  36. package/plugins/data-splitter-plugin.js +323 -0
  37. package/plugins/default-plugins.js +23 -12
  38. package/plugins/email/index.js +1 -1
  39. package/plugins/extension-executor-plugin.js +87 -9
  40. package/plugins/feishu-plugin.js +118 -16
  41. package/plugins/file-system-plugin.js +68 -50
  42. package/plugins/gate-trading.js +10 -10
  43. package/plugins/install-plugin.js +7 -7
  44. package/plugins/memory-plugin.js +9 -12
  45. package/plugins/plugin-manager-plugin.js +12 -14
  46. package/plugins/python-executor-plugin.js +1 -1
  47. package/plugins/python-plugin-loader.js +1 -1
  48. package/plugins/qq-plugin.js +151 -24
  49. package/plugins/rules-plugin.js +8 -8
  50. package/plugins/scheduler-plugin.js +24 -20
  51. package/plugins/session-plugin.js +313 -397
  52. package/plugins/storage-plugin.js +235 -175
  53. package/plugins/subagent-plugin.js +17 -13
  54. package/plugins/telegram-plugin.js +116 -17
  55. package/plugins/think-plugin.js +64 -60
  56. package/plugins/tools-plugin.js +8 -8
  57. package/plugins/web-plugin.js +2 -2
  58. package/plugins/weixin-plugin.js +107 -24
  59. package/skills/find-skills/AGENTS.md +2 -2
  60. package/skills/find-skills/SKILL.md +133 -133
  61. package/skills/foliko-dev/AGENTS.md +236 -236
  62. package/skills/foliko-dev/SKILL.md +19 -19
  63. package/skills/mcp-usage/SKILL.md +200 -200
  64. package/skills/plugin-guide/SKILL.md +4 -4
  65. package/skills/python-plugin-dev/SKILL.md +5 -5
  66. package/skills/skill-guide/SKILL.md +104 -6
  67. package/skills/subagent-guide/SKILL.md +237 -237
  68. package/skills/workflow-guide/SKILL.md +646 -646
  69. package/src/capabilities/skill-manager.js +124 -17
  70. package/src/capabilities/workflow-engine.js +3 -3
  71. package/src/core/agent-chat.js +72 -26
  72. package/src/core/agent.js +17 -27
  73. package/src/core/branch-summary-auto.js +206 -0
  74. package/src/core/chat-session.js +45 -169
  75. package/src/core/command-registry.js +200 -0
  76. package/src/core/constants.js +198 -0
  77. package/src/core/context-compressor.js +702 -326
  78. package/src/core/context-manager.js +0 -1
  79. package/src/core/enhanced-context-compressor.js +210 -0
  80. package/src/core/framework.js +260 -84
  81. package/src/core/jsonl-storage.js +253 -0
  82. package/src/core/plugin-base.js +7 -5
  83. package/src/core/plugin-manager.js +15 -10
  84. package/src/core/provider-registry.js +159 -0
  85. package/src/core/provider.js +2 -0
  86. package/src/core/session-entry.js +225 -0
  87. package/src/core/session-manager.js +701 -0
  88. package/src/core/storage-manager.js +494 -0
  89. package/src/core/sub-agent-config.js +1 -1
  90. package/src/core/subagent.js +16 -135
  91. package/src/core/token-counter.js +177 -58
  92. package/src/core/tool-executor.js +2 -70
  93. package/src/core/ui-extension-context.js +174 -0
  94. package/src/executors/mcp-executor.js +27 -16
  95. package/src/utils/chat-queue.js +11 -22
  96. package/src/utils/data-splitter.js +345 -0
  97. package/src/utils/logger.js +152 -180
  98. package/src/utils/message-validator.js +283 -0
  99. package/src/utils/plugin-helpers.js +2 -2
  100. package/src/utils/retry.js +168 -22
  101. package/website_v2/docs/api.html +1 -1
  102. package/website_v2/docs/configuration.html +2 -2
  103. package/website_v2/docs/plugin-development.html +4 -4
  104. package/website_v2/docs/project-structure.html +2 -2
  105. package/website_v2/docs/skill-development.html +2 -2
  106. package/website_v2/index.html +1 -1
  107. package/website_v2/styles/animations.css +7 -7
  108. package/.agent/agents/backend-dev.md +0 -102
  109. package/.agent/agents/data-analyst.md +0 -117
  110. package/.agent/agents/devops.md +0 -115
  111. package/.agent/agents/frontend-dev.md +0 -94
  112. package/.agent/agents/network-requester.md +0 -44
  113. package/.agent/agents/poster-designer.md +0 -52
  114. package/.agent/agents/product-manager.md +0 -85
  115. package/.agent/agents/qa-engineer.md +0 -100
  116. package/.agent/agents/security-engineer.md +0 -99
  117. package/.agent/agents/team-lead.md +0 -137
  118. package/.agent/agents/ui-designer.md +0 -116
  119. package/.agent/data/default.json +0 -58
  120. package/.agent/data/email/processed-emails.json +0 -1
  121. package/.agent/data/plugins-state.json +0 -199
  122. package/.agent/data/scheduler/tasks.json +0 -1
  123. package/.agent/data/web/web-config.json +0 -5
  124. package/.agent/data/weixin/images/file_1776188148383jpg +0 -0
  125. package/.agent/data/weixin/images/file_1776188458326.jpg +0 -0
  126. package/.agent/data/weixin/images/file_1776188689423.jpg +0 -0
  127. package/.agent/data/weixin/images/file_1776188813604.jpg +0 -0
  128. package/.agent/data/weixin/images/file_1776189097450.jpg +0 -0
  129. package/.agent/data/weixin/videos/file_1776188318431.mp4 +0 -0
  130. package/.agent/data/weixin.json +0 -6
  131. package/.agent/mcp_config.json +0 -14
  132. package/.agent/memory/user/mof6gk94-kneeuh.md +0 -9
  133. package/.agent/package.json +0 -8
  134. package/.agent/plugins/marknative/README.md +0 -134
  135. package/.agent/plugins/marknative/fonts/SegoeUI Emoji.ttf +0 -0
  136. package/.agent/plugins/marknative/fonts.zip +0 -0
  137. package/.agent/plugins/marknative/index.js +0 -256
  138. package/.agent/plugins/marknative/package.json +0 -12
  139. package/.agent/plugins/test-plugin.py +0 -99
  140. package/.agent/plugins.json +0 -14
  141. package/.agent/python-scripts/test_sample.py +0 -24
  142. package/.agent/sessions/cli_default.json +0 -247
  143. package/.agent/skills/agent-browser/SKILL.md +0 -311
  144. package/.agent/skills/agent-browser/TEST_PLAN.md +0 -200
  145. package/.agent/skills/sysinfo/SKILL.md +0 -38
  146. package/.agent/skills/sysinfo/system-info.sh +0 -130
  147. package/.agent/skills/workflow/SKILL.md +0 -324
  148. package/.agent/test-agent.js +0 -35
  149. package/.agent/weixin.json +0 -6
  150. package/.agent/workflows/email-digest.json +0 -50
  151. package/.agent/workflows/file-backup.json +0 -21
  152. package/.agent/workflows/get-ip-notify.json +0 -32
  153. package/.agent/workflows/news-aggregator.json +0 -93
  154. package/.agent/workflows/news-dashboard-v2.json +0 -94
  155. package/.agent/workflows/notification-batch.json +0 -32
  156. package/src/core/session-context.js +0 -346
  157. package/src/core/session-storage.js +0 -295
@@ -12,10 +12,9 @@ const { Agent } = require('./agent');
12
12
  const { Subagent } = require('./subagent');
13
13
  const { SubAgentConfigManager } = require('./sub-agent-config');
14
14
  const { ContextManager } = require('./context-manager');
15
- const { SessionContext } = require('./session-context');
16
- const { SessionStorageAdapter, getDefaultAdapter } = require('./session-storage');
15
+ const { SessionManager } = require('./session-manager');
17
16
  const { CoordinatorManager } = require('./coordinator-manager');
18
- const { logEmitter, Logger, LOG_LEVELS } = require('../utils/logger');
17
+ const { Logger, LOG_LEVELS } = require('../utils/logger');
19
18
  // 创建一个连接到 Framework 的 logger
20
19
  function createFrameworkLogger(framework) {
21
20
  const logger = new Logger({ namespace: 'foliko' });
@@ -124,13 +123,11 @@ class Framework extends EventEmitter {
124
123
  this._contextManager = new ContextManager(this);
125
124
 
126
125
  // SessionContext 缓存(轻量管理,不依赖 ContextManager)
127
- this._sessionContexts = new Map(); // sessionId → SessionContext
126
+ this._sessionContexts = new Map(); // sessionId → SessionManager
128
127
 
129
- // Session 存储适配器(默认使用文件存储)
130
- this._sessionStorage = null;
131
- this._sessionStorageConfig = config.sessionStorage || {
132
- type: 'file',
133
- baseDir: '.agent/sessions',
128
+ // Session 存储配置(JSONL 格式)
129
+ this._sessionStorageConfig = {
130
+ baseDir: '.foliko/sessions',
134
131
  };
135
132
 
136
133
  // 注册 logger 到 framework
@@ -245,6 +242,70 @@ class Framework extends EventEmitter {
245
242
  return this;
246
243
  }
247
244
 
245
+ /**
246
+ * 注册 LLM Provider
247
+ * @param {string} name - Provider 名称
248
+ * @param {Object} config - Provider 配置
249
+ */
250
+ registerProvider(name, config) {
251
+ const { registerProvider } = require('./provider');
252
+ registerProvider(name, config);
253
+ return this;
254
+ }
255
+
256
+ /**
257
+ * 获取 Provider Registry
258
+ */
259
+ getProviderRegistry() {
260
+ const { getProviderRegistry } = require('./provider');
261
+ return getProviderRegistry();
262
+ }
263
+
264
+ /**
265
+ * 注册 CLI 命令
266
+ * @param {string} name - 命令名称
267
+ * @param {Function} handler - 命令处理函数
268
+ * @param {string} description - 命令描述
269
+ * @param {Function} [getArgumentCompletions] - 自动补全函数
270
+ */
271
+ registerCommand(name, handler, description, getArgumentCompletions) {
272
+ const { getCommandRegistry } = require('./command-registry');
273
+ const registry = getCommandRegistry();
274
+ registry.registerCommand(name, handler, description, getArgumentCompletions);
275
+ return this;
276
+ }
277
+
278
+ /**
279
+ * 获取 CommandRegistry
280
+ */
281
+ getCommandRegistry() {
282
+ const { getCommandRegistry } = require('./command-registry');
283
+ return getCommandRegistry();
284
+ }
285
+
286
+ /**
287
+ * 创建 UI 扩展上下文
288
+ * @param {string} pluginName - 插件名称
289
+ * @param {Object} chatUI - ChatUI 实例
290
+ * @returns {ExtensionUIContext}
291
+ */
292
+ createUIContext(pluginName, chatUI) {
293
+ const { ExtensionUIContext } = require('./ui-extension-context');
294
+ if (!this._uiExtensions) {
295
+ this._uiExtensions = new Map();
296
+ }
297
+ const ctx = new ExtensionUIContext(chatUI, pluginName);
298
+ this._uiExtensions.set(pluginName, ctx);
299
+ return ctx;
300
+ }
301
+
302
+ /**
303
+ * 获取 UI 扩展上下文
304
+ */
305
+ getUIContext(pluginName) {
306
+ return this._uiExtensions?.get(pluginName) || null;
307
+ }
308
+
248
309
  /**
249
310
  * 加载并启动插件)
250
311
  * @param {Plugin|Object} plugin - 插件
@@ -324,6 +385,55 @@ class Framework extends EventEmitter {
324
385
  return this.toolRegistry.getAll();
325
386
  }
326
387
 
388
+ // ============================================================================
389
+ // 公开 API — 减少外部直接访问私有属性
390
+ // ============================================================================
391
+
392
+ /**
393
+ * 获取主 Agent
394
+ * @returns {Agent|null}
395
+ */
396
+ getMainAgent() {
397
+ return this._mainAgent;
398
+ }
399
+
400
+ /**
401
+ * 获取主 Agent 的 System Prompt
402
+ * @returns {string}
403
+ */
404
+ getSystemPrompt() {
405
+ if (this._mainAgent) {
406
+ return this._mainAgent._buildSystemPrompt();
407
+ }
408
+ return '';
409
+ }
410
+
411
+ /**
412
+ * 获取插件实例(安全访问)
413
+ * @param {string} name - 插件名称
414
+ * @returns {Object|null}
415
+ */
416
+ getPluginInstance(name) {
417
+ const entry = this.pluginManager.get(name);
418
+ return entry || null;
419
+ }
420
+
421
+ /**
422
+ * 获取所有 Agent 列表
423
+ * @returns {Array<Agent>}
424
+ */
425
+ getAgents() {
426
+ return [...this._agents];
427
+ }
428
+
429
+ /**
430
+ * 获取所有 SessionContext 的 ID 列表
431
+ * @returns {string[]}
432
+ */
433
+ listSessionContexts() {
434
+ return Array.from(this._sessionContexts.keys());
435
+ }
436
+
327
437
  // ============================================================================
328
438
  // 事件描述注册(供 Ambient Agent 使用)
329
439
  // ============================================================================
@@ -398,10 +508,10 @@ class Framework extends EventEmitter {
398
508
  // ============================================================================
399
509
 
400
510
  /**
401
- * 获取或创建 SessionContext
511
+ * 获取或创建 SessionManager
402
512
  * @param {string} sessionId - 会话 ID
403
513
  * @param {Object} options - 配置选项
404
- * @returns {SessionContext}
514
+ * @returns {SessionManager}
405
515
  */
406
516
  getOrCreateSessionContext(sessionId, options = {}) {
407
517
  if (!sessionId) {
@@ -409,32 +519,42 @@ class Framework extends EventEmitter {
409
519
  }
410
520
 
411
521
  // 先检查内存缓存
412
- let ctx = this._sessionContexts.get(sessionId);
413
- if (ctx) {
414
- return ctx;
522
+ let manager = this._sessionContexts.get(sessionId);
523
+ if (manager) {
524
+ return manager;
415
525
  }
416
526
 
417
- // 尝试从存储同步加载(仅文件存储支持)
418
- const storage = this._getSessionStorage();
419
- const loadedCtx = SessionContext.loadSync(sessionId, this, { ...options, storage });
420
- if (loadedCtx) {
421
- this._sessionContexts.set(sessionId, loadedCtx);
422
- this.emit('session:context-created', loadedCtx);
423
- return loadedCtx;
527
+ // 获取工作目录
528
+ const cwd = options.cwd || process.cwd();
529
+ const sessionDir = path.join(cwd, '.foliko', 'sessions');
530
+
531
+ // 获取或创建 session file path
532
+ const sessionFile = options.sessionFile || path.join(sessionDir, `${sessionId}.jsonl`);
533
+
534
+ // 尝试打开已存在的 session
535
+ try {
536
+ if (require('fs').existsSync(sessionFile)) {
537
+ manager = SessionManager.open(sessionFile, sessionDir, cwd);
538
+ }
539
+ } catch (err) {
540
+ // 文件损坏或不存在,创建新的
424
541
  }
425
542
 
426
- // 创建新的 SessionContext
427
- ctx = new SessionContext(sessionId, this, { ...options, storage });
428
- this._sessionContexts.set(sessionId, ctx);
429
- this.emit('session:context-created', ctx);
430
- return ctx;
543
+ if (!manager) {
544
+ // 创建新的 SessionManager
545
+ manager = new SessionManager(cwd, sessionDir, sessionFile, true);
546
+ }
547
+
548
+ this._sessionContexts.set(sessionId, manager);
549
+ this.emit('session:context-created', { sessionId, manager });
550
+ return manager;
431
551
  }
432
552
 
433
553
  /**
434
- * 异步加载 SessionContext(如果存在)
554
+ * 异步加载 SessionManager(如果存在)
435
555
  * @param {string} sessionId
436
556
  * @param {Object} options
437
- * @returns {Promise<SessionContext|null>}
557
+ * @returns {Promise<SessionManager|null>}
438
558
  */
439
559
  async loadSessionContext(sessionId, options = {}) {
440
560
  if (!sessionId) {
@@ -442,55 +562,49 @@ class Framework extends EventEmitter {
442
562
  }
443
563
 
444
564
  // 先检查内存缓存
445
- let ctx = this._sessionContexts.get(sessionId);
446
- if (ctx) {
447
- return ctx;
565
+ let manager = this._sessionContexts.get(sessionId);
566
+ if (manager) {
567
+ return manager;
448
568
  }
449
569
 
450
- const storage = this._getSessionStorage();
451
- ctx = await SessionContext.load(sessionId, this, { ...options, storage });
452
- if (ctx) {
453
- this._sessionContexts.set(sessionId, ctx);
454
- this.emit('session:context-created', ctx);
455
- }
456
- return ctx;
457
- }
570
+ const cwd = options.cwd || process.cwd();
571
+ const sessionDir = path.join(cwd, '.foliko', 'sessions');
572
+ const sessionFile = path.join(sessionDir, `${sessionId}.jsonl`);
458
573
 
459
- /**
460
- * 获取 Session 存储适配器
461
- * @returns {SessionStorageAdapter}
462
- * @private
463
- */
464
- _getSessionStorage() {
465
- if (!this._sessionStorage) {
466
- this._sessionStorage = new SessionStorageAdapter(this._sessionStorageConfig);
574
+ try {
575
+ if (require('fs').existsSync(sessionFile)) {
576
+ manager = SessionManager.open(sessionFile, sessionDir, cwd);
577
+ this._sessionContexts.set(sessionId, manager);
578
+ this.emit('session:context-created', { sessionId, manager });
579
+ }
580
+ } catch (err) {
581
+ // 文件不存在或损坏
467
582
  }
468
- return this._sessionStorage;
583
+
584
+ return manager || null;
469
585
  }
470
586
 
471
587
  /**
472
- * 配置 Session 存储类型
588
+ * 配置 Session 存储目录
473
589
  * @param {Object} config - 存储配置
474
- * @param {string} [config.type='memory'] - 存储类型: 'memory', 'file'
475
- * @param {string} [config.baseDir='.agent/sessions'] - 文件存储目录
590
+ * @param {string} [config.baseDir='.foliko/sessions'] - 文件存储目录
476
591
  */
477
592
  configureSessionStorage(config) {
478
593
  this._sessionStorageConfig = config;
479
- this._sessionStorage = new SessionStorageAdapter(config);
480
594
  }
481
595
 
482
596
  /**
483
- * 获取当前 Session 存储适配器
484
- * @returns {SessionStorageAdapter}
597
+ * 获取当前 Session 存储配置
598
+ * @returns {Object}
485
599
  */
486
600
  getSessionStorage() {
487
- return this._getSessionStorage();
601
+ return this._sessionStorageConfig;
488
602
  }
489
603
 
490
604
  /**
491
- * 获取 SessionContext
605
+ * 获取 SessionManager
492
606
  * @param {string} sessionId - 会话 ID
493
- * @returns {SessionContext|null}
607
+ * @returns {SessionManager|null}
494
608
  */
495
609
  getSessionContext(sessionId) {
496
610
  return this._sessionContexts.get(sessionId) || null;
@@ -498,11 +612,13 @@ class Framework extends EventEmitter {
498
612
 
499
613
  /**
500
614
  * 获取当前 Session 上下文(从 AsyncLocalStorage 获取)
501
- * @returns {SessionContext|null}
615
+ * @returns {SessionManager|null}
502
616
  */
503
617
  getCurrentSessionContext() {
504
618
  const store = sessionStorage.getStore();
505
- return store?.sessionContext || null;
619
+ if (!store) return null;
620
+ const sessionId = store.sessionId;
621
+ return sessionId ? this._sessionContexts.get(sessionId) || null : null;
506
622
  }
507
623
 
508
624
  /**
@@ -527,27 +643,82 @@ class Framework extends EventEmitter {
527
643
  return fn();
528
644
  }
529
645
 
530
- const sessionCtx = this.getOrCreateSessionContext(sessionId, options);
531
- const context = { sessionContext: sessionCtx, sessionId };
646
+ const manager = this.getOrCreateSessionContext(sessionId, options);
647
+ const context = { sessionContext: manager, sessionId };
532
648
  return sessionStorage.run(context, fn);
533
649
  }
534
650
 
535
651
  /**
536
- * 销毁 Session Context
652
+ * 销毁 Session Manager
537
653
  * @param {string} sessionId
538
654
  */
539
655
  destroySessionContext(sessionId) {
540
656
  if (this._sessionContexts.has(sessionId)) {
541
- const ctx = this._sessionContexts.get(sessionId);
542
- ctx.destroy();
657
+ const manager = this._sessionContexts.get(sessionId);
543
658
  this._sessionContexts.delete(sessionId);
659
+ this.emit('session:context-destroyed', { sessionId });
660
+ }
661
+ }
544
662
 
545
- // 从存储中删除
546
- if (this._sessionStorage) {
547
- this._sessionStorage.delete(sessionId).catch(() => {});
548
- }
663
+ // Session TTL 清理配置
664
+ _sessionTTL = 30 * 60 * 1000; // 默认 30 分钟
665
+ _sessionCleanupInterval = null;
666
+
667
+ /**
668
+ * 配置 Session TTL
669
+ * @param {number} ttlMs - TTL 毫秒数
670
+ */
671
+ setSessionTTL(ttlMs) {
672
+ this._sessionTTL = ttlMs;
673
+ return this;
674
+ }
549
675
 
550
- this.emit('session:context-destroyed', ctx);
676
+ /**
677
+ * 启动 Session 自动清理
678
+ * @param {number} [intervalMs=60000] - 检查间隔
679
+ */
680
+ startSessionCleanup(intervalMs = 60000) {
681
+ if (this._sessionCleanupInterval) {
682
+ clearInterval(this._sessionCleanupInterval);
683
+ }
684
+ this._sessionCleanupInterval = setInterval(() => {
685
+ this._cleanupStaleSessions();
686
+ }, intervalMs);
687
+ if (this._sessionCleanupInterval.unref) {
688
+ this._sessionCleanupInterval.unref();
689
+ }
690
+ return this;
691
+ }
692
+
693
+ /**
694
+ * 停止 Session 自动清理
695
+ */
696
+ stopSessionCleanup() {
697
+ if (this._sessionCleanupInterval) {
698
+ clearInterval(this._sessionCleanupInterval);
699
+ this._sessionCleanupInterval = null;
700
+ }
701
+ return this;
702
+ }
703
+
704
+ /**
705
+ * 清理过期的 Session
706
+ * @private
707
+ */
708
+ _cleanupStaleSessions() {
709
+ const now = Date.now();
710
+ const staleIds = [];
711
+ for (const [sessionId, manager] of this._sessionContexts) {
712
+ const metadata = manager.getMetadata?.();
713
+ if (metadata && metadata.lastActive && (now - metadata.lastActive) > this._sessionTTL) {
714
+ staleIds.push(sessionId);
715
+ }
716
+ }
717
+ for (const id of staleIds) {
718
+ this.destroySessionContext(id);
719
+ }
720
+ if (staleIds.length > 0) {
721
+ this.logger.debug(`Cleaned up ${staleIds.length} stale sessions`);
551
722
  }
552
723
  }
553
724
 
@@ -737,9 +908,9 @@ class Framework extends EventEmitter {
737
908
 
738
909
  /**
739
910
  * 同步主Agent的提示词部分到所有子Agent
740
- * @private
911
+ * @public — 插件可能需要调用
741
912
  */
742
- _syncPromptPartsToSubagents() {
913
+ syncPromptPartsToSubagents() {
743
914
  if (!this._mainAgent || !this._mainAgent._systemPromptBuilder) return;
744
915
  const mainParts = this._mainAgent._systemPromptBuilder._parts;
745
916
  if (!mainParts) return;
@@ -784,8 +955,9 @@ class Framework extends EventEmitter {
784
955
  lines.push('');
785
956
 
786
957
  // 2. 主Agent的系统提示词(基础部分)
787
- if (this._mainAgent) {
788
- const mainPrompt = this._mainAgent._originalPrompt;
958
+ const mainAgent = this.getMainAgent();
959
+ if (mainAgent) {
960
+ const mainPrompt = mainAgent.getOriginalPrompt();
789
961
  if (mainPrompt) {
790
962
  lines.push('## 主Agent的系统提示词');
791
963
  lines.push(mainPrompt);
@@ -833,9 +1005,9 @@ class Framework extends EventEmitter {
833
1005
 
834
1006
  /**
835
1007
  * Bootstrap - 使用默认配置启动框架
836
- * 自动加载 .agent/ 目录下的配置和所有默认插件)
1008
+ * 自动加载 .foliko/ 目录下的配置和所有默认插件)
837
1009
  * @param {Object} config - 配置
838
- * @param {string} [config.agentDir='.agent'] - Agent 配置目录
1010
+ * @param {string} [config.agentDir='.foliko'] - Agent 配置目录
839
1011
  * @param {Object} [config.aiConfig] - AI 配置(可选,覆盖文件配置)
840
1012
  * @returns {Promise<Framework>}
841
1013
  */
@@ -848,7 +1020,7 @@ class Framework extends EventEmitter {
848
1020
 
849
1021
  // 先加载默认插件配置
850
1022
  const defaultsPlugin = new DefaultPlugins({
851
- agentDir: config.agentDir || '.agent',
1023
+ agentDir: config.agentDir || '.foliko',
852
1024
  });
853
1025
 
854
1026
  await this.loadPlugin(defaultsPlugin);
@@ -956,12 +1128,6 @@ class Framework extends EventEmitter {
956
1128
  this._coordinatorManager = null;
957
1129
  }
958
1130
 
959
- // 刷新所有 Session 存储
960
- if (this._sessionStorage) {
961
- await this._sessionStorage.flush();
962
- await this._sessionStorage.destroy();
963
- }
964
-
965
1131
  // 销毁所有 agents
966
1132
  for (const agent of this._agents) {
967
1133
  // 移除 agent 的事件转发监听器
@@ -988,10 +1154,20 @@ class Framework extends EventEmitter {
988
1154
  this._toolRegistryListeners = [];
989
1155
  }
990
1156
 
991
- // 卸载所有插件
1157
+ // 停止 Session 清理
1158
+ this.stopSessionCleanup();
1159
+
1160
+ // 清空 session contexts
1161
+ this._sessionContexts.clear();
1162
+
1163
+ // 卸载所有插件(逐个保护,防止一个失败阻断后续)
992
1164
  const plugins = this.pluginManager.getAll();
993
1165
  for (const { name } of plugins) {
994
- await this.pluginManager.unload(name);
1166
+ try {
1167
+ await this.pluginManager.unload(name);
1168
+ } catch (err) {
1169
+ this.logger.warn(`Failed to unload plugin '${name}': ${err.message}`);
1170
+ }
995
1171
  }
996
1172
 
997
1173
  // 清空工具