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.
- package/.claude/settings.local.json +19 -10
- package/.dockerignore +45 -45
- package/.env.example +56 -56
- package/CLAUDE.md +2 -2
- package/README.md +13 -13
- package/SPEC.md +3 -3
- package/cli/src/commands/chat.js +2 -20
- package/cli/src/commands/list.js +7 -6
- package/cli/src/commands/plugin.js +3 -2
- package/cli/src/daemon.js +2 -2
- package/cli/src/ui/chat-ui-old.js +15 -4
- package/cli/src/ui/chat-ui.js +236 -203
- package/cli/src/ui/footer-bar.js +20 -46
- package/cli/src/ui/message-bubble.js +24 -2
- package/cli/src/ui/status-bar.js +177 -0
- package/cli/src/utils/config.js +29 -0
- package/cli/src/utils/plugin-config.js +1 -1
- package/docker-compose.yml +33 -33
- package/docs/features.md +120 -120
- package/docs/quick-reference.md +160 -160
- package/docs/user-manual.md +1391 -1391
- package/examples/ambient-example.js +2 -2
- package/examples/bootstrap.js +3 -3
- package/examples/test-chat.js +1 -1
- package/examples/test-reload.js +1 -1
- package/examples/test-telegram.js +1 -1
- package/examples/test-tg-bot.js +1 -1
- package/examples/test-tg-simple.js +2 -2
- package/examples/test-tg.js +1 -1
- package/examples/test-think.js +1 -1
- package/examples/test-weixin-feishu.js +3 -3
- package/package.json +3 -1
- package/plugins/ambient-agent/index.js +1 -1
- package/plugins/audit-plugin.js +84 -29
- package/plugins/coordinator-plugin.js +14 -12
- package/plugins/data-splitter-plugin.js +323 -0
- package/plugins/default-plugins.js +23 -12
- package/plugins/email/index.js +1 -1
- package/plugins/extension-executor-plugin.js +87 -9
- package/plugins/feishu-plugin.js +118 -16
- package/plugins/file-system-plugin.js +68 -50
- package/plugins/gate-trading.js +10 -10
- package/plugins/install-plugin.js +7 -7
- package/plugins/memory-plugin.js +9 -12
- package/plugins/plugin-manager-plugin.js +12 -14
- package/plugins/python-executor-plugin.js +1 -1
- package/plugins/python-plugin-loader.js +1 -1
- package/plugins/qq-plugin.js +151 -24
- package/plugins/rules-plugin.js +8 -8
- package/plugins/scheduler-plugin.js +24 -20
- package/plugins/session-plugin.js +313 -397
- package/plugins/storage-plugin.js +235 -175
- package/plugins/subagent-plugin.js +17 -13
- package/plugins/telegram-plugin.js +116 -17
- package/plugins/think-plugin.js +64 -60
- package/plugins/tools-plugin.js +8 -8
- package/plugins/web-plugin.js +2 -2
- package/plugins/weixin-plugin.js +107 -24
- package/skills/find-skills/AGENTS.md +2 -2
- package/skills/find-skills/SKILL.md +133 -133
- package/skills/foliko-dev/AGENTS.md +236 -236
- package/skills/foliko-dev/SKILL.md +19 -19
- package/skills/mcp-usage/SKILL.md +200 -200
- package/skills/plugin-guide/SKILL.md +4 -4
- package/skills/python-plugin-dev/SKILL.md +5 -5
- package/skills/skill-guide/SKILL.md +104 -6
- package/skills/subagent-guide/SKILL.md +237 -237
- package/skills/workflow-guide/SKILL.md +646 -646
- package/src/capabilities/skill-manager.js +124 -17
- package/src/capabilities/workflow-engine.js +3 -3
- package/src/core/agent-chat.js +72 -26
- package/src/core/agent.js +17 -27
- package/src/core/branch-summary-auto.js +206 -0
- package/src/core/chat-session.js +45 -169
- package/src/core/command-registry.js +200 -0
- package/src/core/constants.js +198 -0
- package/src/core/context-compressor.js +702 -326
- package/src/core/context-manager.js +0 -1
- package/src/core/enhanced-context-compressor.js +210 -0
- package/src/core/framework.js +260 -84
- package/src/core/jsonl-storage.js +253 -0
- package/src/core/plugin-base.js +7 -5
- package/src/core/plugin-manager.js +15 -10
- package/src/core/provider-registry.js +159 -0
- package/src/core/provider.js +2 -0
- package/src/core/session-entry.js +225 -0
- package/src/core/session-manager.js +701 -0
- package/src/core/storage-manager.js +494 -0
- package/src/core/sub-agent-config.js +1 -1
- package/src/core/subagent.js +16 -135
- package/src/core/token-counter.js +177 -58
- package/src/core/tool-executor.js +2 -70
- package/src/core/ui-extension-context.js +174 -0
- package/src/executors/mcp-executor.js +27 -16
- package/src/utils/chat-queue.js +11 -22
- package/src/utils/data-splitter.js +345 -0
- package/src/utils/logger.js +152 -180
- package/src/utils/message-validator.js +283 -0
- package/src/utils/plugin-helpers.js +2 -2
- package/src/utils/retry.js +168 -22
- package/website_v2/docs/api.html +1 -1
- package/website_v2/docs/configuration.html +2 -2
- package/website_v2/docs/plugin-development.html +4 -4
- package/website_v2/docs/project-structure.html +2 -2
- package/website_v2/docs/skill-development.html +2 -2
- package/website_v2/index.html +1 -1
- package/website_v2/styles/animations.css +7 -7
- package/.agent/agents/backend-dev.md +0 -102
- package/.agent/agents/data-analyst.md +0 -117
- package/.agent/agents/devops.md +0 -115
- package/.agent/agents/frontend-dev.md +0 -94
- package/.agent/agents/network-requester.md +0 -44
- package/.agent/agents/poster-designer.md +0 -52
- package/.agent/agents/product-manager.md +0 -85
- package/.agent/agents/qa-engineer.md +0 -100
- package/.agent/agents/security-engineer.md +0 -99
- package/.agent/agents/team-lead.md +0 -137
- package/.agent/agents/ui-designer.md +0 -116
- package/.agent/data/default.json +0 -58
- package/.agent/data/email/processed-emails.json +0 -1
- package/.agent/data/plugins-state.json +0 -199
- package/.agent/data/scheduler/tasks.json +0 -1
- package/.agent/data/web/web-config.json +0 -5
- package/.agent/data/weixin/images/file_1776188148383jpg +0 -0
- package/.agent/data/weixin/images/file_1776188458326.jpg +0 -0
- package/.agent/data/weixin/images/file_1776188689423.jpg +0 -0
- package/.agent/data/weixin/images/file_1776188813604.jpg +0 -0
- package/.agent/data/weixin/images/file_1776189097450.jpg +0 -0
- package/.agent/data/weixin/videos/file_1776188318431.mp4 +0 -0
- package/.agent/data/weixin.json +0 -6
- package/.agent/mcp_config.json +0 -14
- package/.agent/memory/user/mof6gk94-kneeuh.md +0 -9
- package/.agent/package.json +0 -8
- package/.agent/plugins/marknative/README.md +0 -134
- package/.agent/plugins/marknative/fonts/SegoeUI Emoji.ttf +0 -0
- package/.agent/plugins/marknative/fonts.zip +0 -0
- package/.agent/plugins/marknative/index.js +0 -256
- package/.agent/plugins/marknative/package.json +0 -12
- package/.agent/plugins/test-plugin.py +0 -99
- package/.agent/plugins.json +0 -14
- package/.agent/python-scripts/test_sample.py +0 -24
- package/.agent/sessions/cli_default.json +0 -247
- package/.agent/skills/agent-browser/SKILL.md +0 -311
- package/.agent/skills/agent-browser/TEST_PLAN.md +0 -200
- package/.agent/skills/sysinfo/SKILL.md +0 -38
- package/.agent/skills/sysinfo/system-info.sh +0 -130
- package/.agent/skills/workflow/SKILL.md +0 -324
- package/.agent/test-agent.js +0 -35
- package/.agent/weixin.json +0 -6
- package/.agent/workflows/email-digest.json +0 -50
- package/.agent/workflows/file-backup.json +0 -21
- package/.agent/workflows/get-ip-notify.json +0 -32
- package/.agent/workflows/news-aggregator.json +0 -93
- package/.agent/workflows/news-dashboard-v2.json +0 -94
- package/.agent/workflows/notification-batch.json +0 -32
- package/src/core/session-context.js +0 -346
- package/src/core/session-storage.js +0 -295
package/src/core/framework.js
CHANGED
|
@@ -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 {
|
|
16
|
-
const { SessionStorageAdapter, getDefaultAdapter } = require('./session-storage');
|
|
15
|
+
const { SessionManager } = require('./session-manager');
|
|
17
16
|
const { CoordinatorManager } = require('./coordinator-manager');
|
|
18
|
-
const {
|
|
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 →
|
|
126
|
+
this._sessionContexts = new Map(); // sessionId → SessionManager
|
|
128
127
|
|
|
129
|
-
// Session
|
|
130
|
-
this.
|
|
131
|
-
|
|
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
|
-
* 获取或创建
|
|
511
|
+
* 获取或创建 SessionManager
|
|
402
512
|
* @param {string} sessionId - 会话 ID
|
|
403
513
|
* @param {Object} options - 配置选项
|
|
404
|
-
* @returns {
|
|
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
|
|
413
|
-
if (
|
|
414
|
-
return
|
|
522
|
+
let manager = this._sessionContexts.get(sessionId);
|
|
523
|
+
if (manager) {
|
|
524
|
+
return manager;
|
|
415
525
|
}
|
|
416
526
|
|
|
417
|
-
//
|
|
418
|
-
const
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
-
* 异步加载
|
|
554
|
+
* 异步加载 SessionManager(如果存在)
|
|
435
555
|
* @param {string} sessionId
|
|
436
556
|
* @param {Object} options
|
|
437
|
-
* @returns {Promise<
|
|
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
|
|
446
|
-
if (
|
|
447
|
-
return
|
|
565
|
+
let manager = this._sessionContexts.get(sessionId);
|
|
566
|
+
if (manager) {
|
|
567
|
+
return manager;
|
|
448
568
|
}
|
|
449
569
|
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
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
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
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
|
-
|
|
583
|
+
|
|
584
|
+
return manager || null;
|
|
469
585
|
}
|
|
470
586
|
|
|
471
587
|
/**
|
|
472
|
-
* 配置 Session
|
|
588
|
+
* 配置 Session 存储目录
|
|
473
589
|
* @param {Object} config - 存储配置
|
|
474
|
-
* @param {string} [config.
|
|
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 {
|
|
597
|
+
* 获取当前 Session 存储配置
|
|
598
|
+
* @returns {Object}
|
|
485
599
|
*/
|
|
486
600
|
getSessionStorage() {
|
|
487
|
-
return this.
|
|
601
|
+
return this._sessionStorageConfig;
|
|
488
602
|
}
|
|
489
603
|
|
|
490
604
|
/**
|
|
491
|
-
* 获取
|
|
605
|
+
* 获取 SessionManager
|
|
492
606
|
* @param {string} sessionId - 会话 ID
|
|
493
|
-
* @returns {
|
|
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 {
|
|
615
|
+
* @returns {SessionManager|null}
|
|
502
616
|
*/
|
|
503
617
|
getCurrentSessionContext() {
|
|
504
618
|
const store = sessionStorage.getStore();
|
|
505
|
-
|
|
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
|
|
531
|
-
const context = { sessionContext:
|
|
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
|
|
652
|
+
* 销毁 Session Manager
|
|
537
653
|
* @param {string} sessionId
|
|
538
654
|
*/
|
|
539
655
|
destroySessionContext(sessionId) {
|
|
540
656
|
if (this._sessionContexts.has(sessionId)) {
|
|
541
|
-
const
|
|
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
|
-
|
|
547
|
-
|
|
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
|
-
|
|
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
|
-
* @
|
|
911
|
+
* @public — 插件可能需要调用
|
|
741
912
|
*/
|
|
742
|
-
|
|
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
|
-
|
|
788
|
-
|
|
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
|
-
* 自动加载 .
|
|
1008
|
+
* 自动加载 .foliko/ 目录下的配置和所有默认插件)
|
|
837
1009
|
* @param {Object} config - 配置
|
|
838
|
-
* @param {string} [config.agentDir='.
|
|
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 || '.
|
|
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
|
-
|
|
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
|
// 清空工具
|