foliko 1.1.93 → 2.0.1
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 +2 -1
- package/CLAUDE.md +56 -30
- package/REFACTORING_PLAN.md +645 -0
- package/docs/architecture.md +131 -0
- package/docs/migration.md +57 -0
- package/docs/public-api.md +138 -0
- package/docs/usage.md +385 -0
- package/examples/ambient-example.js +20 -137
- package/examples/basic.js +21 -48
- package/examples/bootstrap.js +16 -74
- package/examples/mcp-example.js +6 -29
- package/examples/skill-example.js +6 -19
- package/examples/workflow.js +8 -56
- package/package.json +8 -4
- package/plugins/README.md +49 -0
- package/plugins/{ambient-agent → ambient}/EventWatcher.js +1 -1
- package/plugins/{ambient-agent → ambient}/ExplorerLoop.js +3 -3
- package/plugins/{ambient-agent → ambient}/GoalManager.js +2 -2
- package/plugins/ambient/README.md +14 -0
- package/plugins/{ambient-agent → ambient}/Reflector.js +1 -1
- package/plugins/{ambient-agent → ambient}/StateStore.js +1 -1
- package/plugins/{ambient-agent → ambient}/index.js +2 -2
- package/plugins/{ai-plugin.js → core/ai/index.js} +14 -30
- package/plugins/{audit-plugin.js → core/audit/index.js} +3 -30
- package/plugins/{coordinator-plugin.js → core/coordinator/index.js} +3 -35
- package/plugins/core/default/bootstrap.js +224 -0
- package/plugins/core/default/config.js +222 -0
- package/plugins/core/default/index.js +58 -0
- package/plugins/core/mcp/index.js +1 -0
- package/plugins/{python-plugin-loader.js → core/python-loader/index.js} +7 -187
- package/plugins/{rules-plugin.js → core/rules/index.js} +121 -64
- package/plugins/{scheduler-plugin.js → core/scheduler/index.js} +12 -114
- package/plugins/{session-plugin.js → core/session/index.js} +9 -73
- package/{src/capabilities/skill-manager.js → plugins/core/skill-manager/index.js} +64 -18
- package/plugins/{storage-plugin.js → core/storage/index.js} +5 -29
- package/plugins/{subagent-plugin.js → core/sub-agent/index.js} +10 -171
- package/plugins/{think-plugin.js → core/think/index.js} +24 -91
- package/{src/capabilities/workflow-engine.js → plugins/core/workflow/index.js} +87 -85
- package/plugins/default-plugins.js +6 -720
- package/plugins/{data-splitter-plugin.js → executors/data-splitter/index.js} +9 -83
- package/plugins/{extension-executor-plugin.js → executors/extension/index.js} +13 -97
- package/plugins/{python-executor-plugin.js → executors/python/index.js} +6 -31
- package/plugins/{shell-executor-plugin.js → executors/shell/index.js} +2 -5
- package/plugins/install/README.md +9 -0
- package/plugins/{install-plugin.js → install/index.js} +3 -3
- package/plugins/{file-system-plugin.js → io/file-system/index.js} +34 -236
- package/plugins/{web-plugin.js → io/web/index.js} +11 -113
- package/plugins/memory/README.md +13 -0
- package/plugins/{memory-plugin.js → memory/index.js} +4 -18
- package/plugins/messaging/email/README.md +19 -0
- package/plugins/{email → messaging/email}/index.js +3 -3
- package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +4 -4
- package/plugins/{qq-plugin.js → messaging/qq/index.js} +6 -17
- package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +4 -4
- package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +16 -16
- package/plugins/{plugin-manager-plugin.js → plugin-manager/index.js} +36 -180
- package/plugins/{tools-plugin.js → tools/index.js} +68 -116
- package/plugins/trading/README.md +15 -0
- package/plugins/{gate-trading.js → trading/index.js} +8 -8
- package/{examples → sandbox}/test-concurrent-chat.js +2 -2
- package/{examples → sandbox}/test-long-chat.js +2 -2
- package/{examples → sandbox}/test-session-chat.js +2 -2
- package/{examples → sandbox}/test-web-plugin.js +1 -1
- package/{examples → sandbox}/test-weixin-feishu.js +2 -2
- package/src/agent/base.js +56 -0
- package/src/{core/agent-chat.js → agent/chat.js} +11 -11
- package/src/{core/coordinator-manager.js → agent/coordinator.js} +3 -3
- package/src/agent/index.js +111 -0
- package/src/agent/main.js +337 -0
- package/src/agent/prompt.js +78 -0
- package/src/agent/sub.js +198 -0
- package/src/agent/worker.js +104 -0
- package/{cli/bin/foliko.js → src/cli/bin.js} +1 -1
- package/{cli/src → src/cli}/commands/chat.js +25 -21
- package/{cli/src → src/cli}/index.js +1 -0
- package/{cli/src → src/cli}/ui/chat-ui-old.js +40 -178
- package/{cli/src → src/cli}/ui/chat-ui.js +3 -3
- package/{cli/src → src/cli}/ui/components/footer-bar.js +1 -1
- package/src/common/errors.js +402 -0
- package/src/{utils → common}/logger.js +33 -0
- package/src/{utils/chat-queue.js → common/queue.js} +2 -2
- package/src/config/plugin-config.js +50 -0
- package/src/context/agent.js +32 -0
- package/src/context/compaction-prompts.js +170 -0
- package/src/context/compaction-utils.js +191 -0
- package/src/context/compressor.js +413 -0
- package/src/context/index.js +9 -0
- package/src/{core/context-manager.js → context/manager.js} +1 -1
- package/src/context/request.js +50 -0
- package/src/context/session.js +33 -0
- package/src/context/storage.js +30 -0
- package/src/executors/mcp-client.js +153 -0
- package/src/executors/mcp-desc.js +236 -0
- package/src/executors/mcp-executor.js +91 -956
- package/src/{core → framework}/command-registry.js +1 -1
- package/src/framework/framework.js +300 -0
- package/src/framework/index.js +18 -0
- package/src/framework/lifecycle.js +203 -0
- package/src/framework/loader.js +78 -0
- package/src/framework/registry.js +86 -0
- package/src/{core/ui-extension-context.js → framework/ui-extension.js} +1 -1
- package/src/index.js +130 -15
- package/src/llm/index.js +26 -0
- package/src/llm/provider.js +212 -0
- package/src/llm/registry.js +11 -0
- package/src/{core/token-counter.js → llm/tokens.js} +4 -37
- package/src/{core/plugin-base.js → plugin/base.js} +10 -136
- package/src/plugin/index.js +14 -0
- package/src/plugin/loader.js +101 -0
- package/src/plugin/manager.js +484 -0
- package/src/{core → session}/branch-summary-auto.js +2 -2
- package/src/{core/chat-session.js → session/chat.js} +2 -2
- package/src/session/index.js +7 -0
- package/src/{core/session-manager.js → session/session.js} +2 -2
- package/src/session/ttl.js +92 -0
- package/src/{core/jsonl-storage.js → storage/jsonl.js} +1 -1
- package/src/tool/executor.js +85 -0
- package/src/tool/index.js +15 -0
- package/src/tool/registry.js +143 -0
- package/src/{core/tool-router.js → tool/router.js} +17 -124
- package/src/tool/schema.js +108 -0
- package/src/utils/data-splitter.js +1 -1
- package/src/utils/download.js +1 -1
- package/src/utils/index.js +6 -6
- package/src/utils/message-validator.js +1 -1
- package/tests/core/context-storage.test.js +46 -0
- package/tests/core/llm.test.js +54 -0
- package/tests/core/plugin.test.js +42 -0
- package/tests/core/tool.test.js +60 -0
- package/tests/setup.js +10 -0
- package/tests/smoke.test.js +58 -0
- package/vitest.config.js +9 -0
- package/cli/src/daemon.js +0 -149
- package/docs/CONTEXT_DESIGN.md +0 -1596
- package/docs/ai-sdk-optimization.md +0 -655
- package/docs/features.md +0 -120
- package/docs/qq-bot.md +0 -976
- package/docs/quick-reference.md +0 -160
- package/docs/user-manual.md +0 -1391
- package/images/geometric_shapes.jpg +0 -0
- package/images/sunset_mountain_lake.jpg +0 -0
- package/skills/poster-guide/SKILL.md +0 -792
- package/src/capabilities/index.js +0 -11
- package/src/core/agent.js +0 -808
- package/src/core/context-compressor.js +0 -959
- package/src/core/enhanced-context-compressor.js +0 -210
- package/src/core/framework.js +0 -1422
- package/src/core/index.js +0 -30
- package/src/core/plugin-manager.js +0 -961
- package/src/core/provider-registry.js +0 -159
- package/src/core/provider.js +0 -156
- package/src/core/request-context.js +0 -98
- package/src/core/subagent.js +0 -442
- package/src/core/system-prompt-builder.js +0 -120
- package/src/core/tool-executor.js +0 -202
- package/src/core/tool-registry.js +0 -517
- package/src/core/worker-agent.js +0 -192
- package/src/executors/executor-base.js +0 -58
- package/src/utils/error-boundary.js +0 -363
- package/src/utils/error.js +0 -374
- package/system.md +0 -1645
- package/website_v2/README.md +0 -57
- package/website_v2/SPEC.md +0 -1
- package/website_v2/docs/api.html +0 -128
- package/website_v2/docs/configuration.html +0 -147
- package/website_v2/docs/plugin-development.html +0 -129
- package/website_v2/docs/project-structure.html +0 -89
- package/website_v2/docs/skill-development.html +0 -85
- package/website_v2/index.html +0 -489
- package/website_v2/scripts/main.js +0 -93
- package/website_v2/styles/animations.css +0 -8
- package/website_v2/styles/docs.css +0 -83
- package/website_v2/styles/main.css +0 -417
- package/xhs_auth.json +0 -268
- package//346/265/267/346/212/245/346/217/222/344/273/266.md +0 -621
- /package/plugins/{ambient-agent → ambient}/constants.js +0 -0
- /package/plugins/{email → messaging/email}/constants.js +0 -0
- /package/plugins/{email → messaging/email}/handlers.js +0 -0
- /package/plugins/{email → messaging/email}/monitor.js +0 -0
- /package/plugins/{email → messaging/email}/parser.js +0 -0
- /package/plugins/{email → messaging/email}/reply.js +0 -0
- /package/plugins/{email → messaging/email}/utils.js +0 -0
- /package/{examples → sandbox}/test-chat.js +0 -0
- /package/{examples → sandbox}/test-mcp.js +0 -0
- /package/{examples → sandbox}/test-reload.js +0 -0
- /package/{examples → sandbox}/test-telegram.js +0 -0
- /package/{examples → sandbox}/test-tg-bot.js +0 -0
- /package/{examples → sandbox}/test-tg-simple.js +0 -0
- /package/{examples → sandbox}/test-tg.js +0 -0
- /package/{examples → sandbox}/test-think.js +0 -0
- /package/src/{core/sub-agent-config.js → agent/sub-config.js} +0 -0
- /package/{cli/src → src/cli}/commands/daemon.js +0 -0
- /package/{cli/src → src/cli}/commands/list.js +0 -0
- /package/{cli/src → src/cli}/commands/plugin.js +0 -0
- /package/{cli/src → src/cli}/ui/components/agent-mention-provider.js +0 -0
- /package/{cli/src → src/cli}/ui/components/chained-autocomplete-provider.js +0 -0
- /package/{cli/src → src/cli}/ui/components/message-bubble.js +0 -0
- /package/{cli/src → src/cli}/ui/components/status-bar.js +0 -0
- /package/{cli/src → src/cli}/utils/ansi.js +0 -0
- /package/{cli/src → src/cli}/utils/config.js +0 -0
- /package/{cli/src → src/cli}/utils/markdown.js +0 -0
- /package/{cli/src → src/cli}/utils/plugin-config.js +0 -0
- /package/{cli/src → src/cli}/utils/render-diff.js +0 -0
- /package/src/{utils/circuit-breaker.js → common/circuit.js} +0 -0
- /package/src/{core → common}/constants.js +0 -0
- /package/src/{utils/edit-diff.js → common/diff.js} +0 -0
- /package/src/{utils/event-emitter.js → common/events.js} +0 -0
- /package/src/{utils → common}/id.js +0 -0
- /package/src/{utils → common}/retry.js +0 -0
- /package/src/{core/notification-manager.js → notification/manager.js} +0 -0
- /package/src/{core/session-entry.js → session/entry.js} +0 -0
- /package/src/{core/storage-manager.js → storage/manager.js} +0 -0
package/src/core/framework.js
DELETED
|
@@ -1,1422 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Framework 核心容器
|
|
3
|
-
* 管理所有子系统,提供统一入口
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const os = require('os');
|
|
8
|
-
const { AsyncLocalStorage } = require('async_hooks');
|
|
9
|
-
const { EventEmitter } = require('../utils/event-emitter');
|
|
10
|
-
const { PluginManager } = require('./plugin-manager');
|
|
11
|
-
const { ToolRegistry } = require('./tool-registry');
|
|
12
|
-
const { Agent } = require('./agent');
|
|
13
|
-
const { Subagent } = require('./subagent');
|
|
14
|
-
const { SubAgentConfigManager } = require('./sub-agent-config');
|
|
15
|
-
const { ContextManager } = require('./context-manager');
|
|
16
|
-
const { SessionManager } = require('./session-manager');
|
|
17
|
-
const { CoordinatorManager } = require('./coordinator-manager');
|
|
18
|
-
const { Logger, LOG_LEVELS } = require('../utils/logger');
|
|
19
|
-
const { AGENT_DIR, HOME_AGENT_DIR_NAME, SYSTEM_PLUGINS } = require('./constants');
|
|
20
|
-
// 创建一个连接到 Framework 的 logger
|
|
21
|
-
function createFrameworkLogger(framework) {
|
|
22
|
-
const logger = new Logger({ namespace: 'foliko' });
|
|
23
|
-
const state = { silent: false };
|
|
24
|
-
|
|
25
|
-
// 获取当前有效的级别(优先用全局级别)
|
|
26
|
-
const getLevel = () => Logger.globalLevel ?? logger._level ?? LOG_LEVELS.INFO;
|
|
27
|
-
|
|
28
|
-
// 日志级别配置表,消除 4×2 重复代码
|
|
29
|
-
const LEVEL_CONFIG = [
|
|
30
|
-
['info', LOG_LEVELS.INFO, 'INFO'],
|
|
31
|
-
['warn', LOG_LEVELS.WARN, 'WARN'],
|
|
32
|
-
['error', LOG_LEVELS.ERROR, 'ERROR'],
|
|
33
|
-
['debug', LOG_LEVELS.DEBUG, 'DEBUG'],
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
// 通用日志拦截包装器
|
|
37
|
-
function wrapLogMethod(targetLogger, originalMethod, levelThreshold, levelName) {
|
|
38
|
-
return (...args) => {
|
|
39
|
-
const lvl = getLevel();
|
|
40
|
-
if (lvl <= levelThreshold) {
|
|
41
|
-
if (!state.silent) originalMethod(...args);
|
|
42
|
-
framework.emit('log', { level: levelName, message: args.join(' '), namespace: targetLogger.namespace });
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 包装所有级别
|
|
48
|
-
const originals = {};
|
|
49
|
-
for (const [name] of LEVEL_CONFIG) {
|
|
50
|
-
originals[name] = logger[name].bind(logger);
|
|
51
|
-
}
|
|
52
|
-
for (const [name, threshold, label] of LEVEL_CONFIG) {
|
|
53
|
-
logger[name] = wrapLogMethod(logger, originals[name], threshold, label);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// 拦截 child 方法
|
|
57
|
-
const originalChild = logger.child.bind(logger);
|
|
58
|
-
logger.child = (...args) => {
|
|
59
|
-
const childLogger = originalChild(...args);
|
|
60
|
-
const childOriginals = {};
|
|
61
|
-
for (const [name] of LEVEL_CONFIG) {
|
|
62
|
-
childOriginals[name] = childLogger[name].bind(childLogger);
|
|
63
|
-
}
|
|
64
|
-
for (const [name, threshold, label] of LEVEL_CONFIG) {
|
|
65
|
-
childLogger[name] = wrapLogMethod(childLogger, childOriginals[name], threshold, label);
|
|
66
|
-
}
|
|
67
|
-
return childLogger;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// 暴露 setGlobalLogLevel 方法
|
|
71
|
-
logger.setGlobalLogLevel = (level) => { Logger.globalLevel = level; };
|
|
72
|
-
|
|
73
|
-
// 暴露 setSilent 方法
|
|
74
|
-
logger.setSilent = (v) => { state.silent = v; };
|
|
75
|
-
|
|
76
|
-
return logger;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// 添加框架目录的 node_modules 到全局搜索路径
|
|
80
|
-
// 让项目插件能 require 框架内置的包(如 zod)
|
|
81
|
-
const frameworkNodeModules = path.join(__dirname, '..', '..', 'node_modules');
|
|
82
|
-
if (!module.paths.includes(frameworkNodeModules)) {
|
|
83
|
-
module.paths.unshift(frameworkNodeModules);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 现有的 AsyncLocalStorage(保持向后兼容)
|
|
87
|
-
const asyncLocalStorage = new AsyncLocalStorage();
|
|
88
|
-
|
|
89
|
-
// 新增:两层 AsyncLocalStorage
|
|
90
|
-
const requestStorage = new AsyncLocalStorage(); // Request 级别
|
|
91
|
-
const sessionStorage = new AsyncLocalStorage(); // Session 级别
|
|
92
|
-
|
|
93
|
-
class Framework extends EventEmitter {
|
|
94
|
-
/**
|
|
95
|
-
* @param {Object} config - 配置
|
|
96
|
-
* @param {boolean} [config.debug=false] - 调试模式
|
|
97
|
-
* @param {boolean} [config.silent=false] - 是否静默(不输出日志)
|
|
98
|
-
* @param {string} [config.cwd] - 初始工作目录(默认 process.cwd(),可后续用 setCwd 切换)
|
|
99
|
-
* @param {string} [config.agentDir='.foliko'] - 项目级 Agent 目录名
|
|
100
|
-
* @param {string} [config.homeAgentDir] - 用户级 Agent 目录(默认 ~/.foliko,可被 FOLIKO_HOME 环境变量覆盖)
|
|
101
|
-
*/
|
|
102
|
-
constructor(config = {}) {
|
|
103
|
-
super();
|
|
104
|
-
this._silent=config.silent||false;
|
|
105
|
-
this._debug = config.debug || false;
|
|
106
|
-
this._ready = false;
|
|
107
|
-
this._readyPromise = null;
|
|
108
|
-
this._resolveReady = null;
|
|
109
|
-
|
|
110
|
-
// 初始化子系统
|
|
111
|
-
this.toolRegistry = new ToolRegistry();
|
|
112
|
-
this.pluginManager = new PluginManager(this);
|
|
113
|
-
|
|
114
|
-
// Agent 管理
|
|
115
|
-
this._agents = []; // 所有创建的 agent
|
|
116
|
-
this._mainAgent = null; // 主 agent
|
|
117
|
-
|
|
118
|
-
// 保存 toolRegistry 监听器引用,用于清理
|
|
119
|
-
this._toolRegistryListeners = [];
|
|
120
|
-
|
|
121
|
-
// 子代理配置管理器
|
|
122
|
-
this._subAgentConfigManager = null;
|
|
123
|
-
|
|
124
|
-
// 执行上下文(工具调用时可用)
|
|
125
|
-
this._executionContext = null;
|
|
126
|
-
|
|
127
|
-
// ========== 分层上下文系统 ==========
|
|
128
|
-
// Context 管理器
|
|
129
|
-
this._contextManager = new ContextManager(this);
|
|
130
|
-
|
|
131
|
-
// SessionContext 缓存(轻量管理,不依赖 ContextManager)
|
|
132
|
-
this._sessionContexts = new Map(); // sessionId → SessionManager
|
|
133
|
-
|
|
134
|
-
// Session 存储配置(JSONL 格式)
|
|
135
|
-
this._sessionStorageConfig = {
|
|
136
|
-
baseDir: '.foliko/sessions',
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
// 工作目录与 Agent 目录
|
|
140
|
-
this._cwd = config.cwd || process.cwd();
|
|
141
|
-
this._agentDir = config.agentDir || AGENT_DIR;
|
|
142
|
-
this._homeAgentDir =
|
|
143
|
-
config.homeAgentDir ||
|
|
144
|
-
process.env.FOLIKO_HOME ||
|
|
145
|
-
path.join(os.homedir(), HOME_AGENT_DIR_NAME);
|
|
146
|
-
this._changingCwd = false;
|
|
147
|
-
|
|
148
|
-
// 注册 logger 到 framework
|
|
149
|
-
this.logger = createFrameworkLogger(this);
|
|
150
|
-
|
|
151
|
-
// Coordinator管理器(用于Coordinator模式)
|
|
152
|
-
this._coordinatorManager = null;
|
|
153
|
-
|
|
154
|
-
// 事件转发 - ToolRegistry 事件
|
|
155
|
-
const toolRegisteredHandler = (tool) => {
|
|
156
|
-
this.emit('tool:registered', tool);
|
|
157
|
-
};
|
|
158
|
-
const toolCallHandler = (data) => {
|
|
159
|
-
this.emit('tool:call', data);
|
|
160
|
-
// 兼容连字符格式
|
|
161
|
-
this.emit('tool-call', data);
|
|
162
|
-
};
|
|
163
|
-
const toolResultHandler = (data) => {
|
|
164
|
-
this.emit('tool:result', data);
|
|
165
|
-
};
|
|
166
|
-
const toolErrorHandler = (data) => {
|
|
167
|
-
this.emit('tool:error', data);
|
|
168
|
-
};
|
|
169
|
-
this.toolRegistry.on('tool:registered', toolRegisteredHandler);
|
|
170
|
-
this.toolRegistry.on('tool:call', toolCallHandler);
|
|
171
|
-
this.toolRegistry.on('tool:result', toolResultHandler);
|
|
172
|
-
this.toolRegistry.on('tool:error', toolErrorHandler);
|
|
173
|
-
this._toolRegistryListeners = [
|
|
174
|
-
{ event: 'tool:registered', handler: toolRegisteredHandler },
|
|
175
|
-
{ event: 'tool:call', handler: toolCallHandler },
|
|
176
|
-
{ event: 'tool:result', handler: toolResultHandler },
|
|
177
|
-
{ event: 'tool:error', handler: toolErrorHandler },
|
|
178
|
-
];
|
|
179
|
-
|
|
180
|
-
// 事件转发 - Agent 事件
|
|
181
|
-
this._setupAgentEventForwarding();
|
|
182
|
-
|
|
183
|
-
this._registerBuiltinTools();
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* 设置 Agent 事件转发
|
|
188
|
-
* @private
|
|
189
|
-
*/
|
|
190
|
-
_setupAgentEventForwarding() {
|
|
191
|
-
// 保存 agent 创建时的转发监听器引用,用于清理
|
|
192
|
-
this._agentForwardingListeners = new Map();
|
|
193
|
-
// 监听 agent:created 事件,为新创建的 Agent 设置事件转发
|
|
194
|
-
this.on('agent:created', (agent) => {
|
|
195
|
-
// 转发 Agent 的 tool-call/result/error 事件
|
|
196
|
-
const toolCallHandler = (data) => {
|
|
197
|
-
this.emit('tool:call', data);
|
|
198
|
-
};
|
|
199
|
-
const toolResultHandler = (data) => {
|
|
200
|
-
this.emit('tool:result', data);
|
|
201
|
-
};
|
|
202
|
-
const toolErrorHandler = (data) => {
|
|
203
|
-
this.emit('tool:error', data);
|
|
204
|
-
};
|
|
205
|
-
const messageHandler = (msg) => {
|
|
206
|
-
this.emit('agent:message', msg);
|
|
207
|
-
};
|
|
208
|
-
agent.on('tool-call', toolCallHandler);
|
|
209
|
-
agent.on('tool-result', toolResultHandler);
|
|
210
|
-
agent.on('tool-error', toolErrorHandler);
|
|
211
|
-
agent.on('message', messageHandler);
|
|
212
|
-
// 保存引用用于后续清理
|
|
213
|
-
this._agentForwardingListeners.set(agent, {
|
|
214
|
-
toolCallHandler,
|
|
215
|
-
toolResultHandler,
|
|
216
|
-
toolErrorHandler,
|
|
217
|
-
messageHandler,
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* 从 AI 插件合并配置到目标配置对象
|
|
224
|
-
* @param {Object} target - 目标配置对象
|
|
225
|
-
* @private
|
|
226
|
-
*/
|
|
227
|
-
_mergeAIConfig(target) {
|
|
228
|
-
if (target.apiKey) return target; // 已有 apiKey 不需要合并
|
|
229
|
-
|
|
230
|
-
const aiPlugin = this.pluginManager.get('ai');
|
|
231
|
-
if (!aiPlugin) return target;
|
|
232
|
-
|
|
233
|
-
const defaults = {
|
|
234
|
-
apiKey: aiPlugin.config.apiKey,
|
|
235
|
-
provider: aiPlugin.config.provider,
|
|
236
|
-
model: aiPlugin.config.model,
|
|
237
|
-
baseURL: aiPlugin.config.baseURL,
|
|
238
|
-
providerOptions: aiPlugin.config.providerOptions || {},
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
return {
|
|
242
|
-
...target,
|
|
243
|
-
apiKey: target.apiKey || defaults.apiKey,
|
|
244
|
-
provider: target.provider || defaults.provider,
|
|
245
|
-
model: target.model || defaults.model,
|
|
246
|
-
baseURL: target.baseURL || defaults.baseURL,
|
|
247
|
-
providerOptions: target.providerOptions || defaults.providerOptions,
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* 注册插件(不加载)
|
|
253
|
-
* @param {Plugin|Object} plugin - 插件
|
|
254
|
-
*/
|
|
255
|
-
registerPlugin(plugin) {
|
|
256
|
-
this.pluginManager.register(plugin);
|
|
257
|
-
return this;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* 注册 LLM Provider
|
|
262
|
-
* @param {string} name - Provider 名称
|
|
263
|
-
* @param {Object} config - Provider 配置
|
|
264
|
-
*/
|
|
265
|
-
registerProvider(name, config) {
|
|
266
|
-
const { registerProvider } = require('./provider');
|
|
267
|
-
registerProvider(name, config);
|
|
268
|
-
return this;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* 获取 Provider Registry
|
|
273
|
-
*/
|
|
274
|
-
getProviderRegistry() {
|
|
275
|
-
const { getProviderRegistry } = require('./provider');
|
|
276
|
-
return getProviderRegistry();
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* 注册 CLI 命令
|
|
281
|
-
* @param {string} name - 命令名称
|
|
282
|
-
* @param {Function} handler - 命令处理函数
|
|
283
|
-
* @param {string} description - 命令描述
|
|
284
|
-
* @param {Function} [getArgumentCompletions] - 自动补全函数
|
|
285
|
-
*/
|
|
286
|
-
registerCommand(name, handler, description, getArgumentCompletions) {
|
|
287
|
-
const { getCommandRegistry } = require('./command-registry');
|
|
288
|
-
const registry = getCommandRegistry();
|
|
289
|
-
registry.registerCommand(name, handler, description, getArgumentCompletions);
|
|
290
|
-
return this;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* 获取 CommandRegistry
|
|
295
|
-
*/
|
|
296
|
-
getCommandRegistry() {
|
|
297
|
-
const { getCommandRegistry } = require('./command-registry');
|
|
298
|
-
return getCommandRegistry();
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* 创建 UI 扩展上下文
|
|
303
|
-
* @param {string} pluginName - 插件名称
|
|
304
|
-
* @param {Object} chatUI - ChatUI 实例
|
|
305
|
-
* @returns {ExtensionUIContext}
|
|
306
|
-
*/
|
|
307
|
-
createUIContext(pluginName, chatUI) {
|
|
308
|
-
const { ExtensionUIContext } = require('./ui-extension-context');
|
|
309
|
-
if (!this._uiExtensions) {
|
|
310
|
-
this._uiExtensions = new Map();
|
|
311
|
-
}
|
|
312
|
-
const ctx = new ExtensionUIContext(chatUI, pluginName);
|
|
313
|
-
this._uiExtensions.set(pluginName, ctx);
|
|
314
|
-
return ctx;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* 获取 UI 扩展上下文
|
|
319
|
-
*/
|
|
320
|
-
getUIContext(pluginName) {
|
|
321
|
-
return this._uiExtensions?.get(pluginName) || null;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* 加载并启动插件)
|
|
326
|
-
* @param {Plugin|Object} plugin - 插件
|
|
327
|
-
*/
|
|
328
|
-
async loadPlugin(plugin) {
|
|
329
|
-
await this.pluginManager.load(plugin);
|
|
330
|
-
return this;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* 卸载插件
|
|
335
|
-
* @param {string} name - 插件名称
|
|
336
|
-
*/
|
|
337
|
-
async unloadPlugin(name) {
|
|
338
|
-
await this.pluginManager.unload(name);
|
|
339
|
-
return this;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* 重载单个插件
|
|
344
|
-
* @param {string} name - 插件名称
|
|
345
|
-
*/
|
|
346
|
-
async reloadPlugin(name) {
|
|
347
|
-
await this.pluginManager.reload(name);
|
|
348
|
-
return this;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* 重载所有插件)
|
|
353
|
-
*/
|
|
354
|
-
async reloadAllPlugins() {
|
|
355
|
-
await this.pluginManager.reloadAll();
|
|
356
|
-
return this;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* 启用插件
|
|
361
|
-
* @param {string} name - 插件名称
|
|
362
|
-
*/
|
|
363
|
-
async enablePlugin(name) {
|
|
364
|
-
await this.pluginManager.enable(name);
|
|
365
|
-
return this;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* 禁用插件
|
|
370
|
-
* @param {string} name - 插件名称
|
|
371
|
-
*/
|
|
372
|
-
async disablePlugin(name) {
|
|
373
|
-
await this.pluginManager.disable(name);
|
|
374
|
-
return this;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* 更新插件配置
|
|
379
|
-
* @param {string} name - 插件名称
|
|
380
|
-
* @param {Object} config - 新配置
|
|
381
|
-
*/
|
|
382
|
-
updatePluginConfig(name, config) {
|
|
383
|
-
return this.pluginManager.updatePluginConfig(name, config);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* 注册工具
|
|
388
|
-
* @param {Object} tool - 工具定义
|
|
389
|
-
*/
|
|
390
|
-
registerTool(tool) {
|
|
391
|
-
this.toolRegistry.register(tool);
|
|
392
|
-
return this;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* 获取所有工具)
|
|
397
|
-
* @returns {Array<Object>}
|
|
398
|
-
*/
|
|
399
|
-
getTools() {
|
|
400
|
-
return this.toolRegistry.getAll();
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// ============================================================================
|
|
404
|
-
// 公开 API — 减少外部直接访问私有属性
|
|
405
|
-
// ============================================================================
|
|
406
|
-
|
|
407
|
-
/**
|
|
408
|
-
* 获取主 Agent
|
|
409
|
-
* @returns {Agent|null}
|
|
410
|
-
*/
|
|
411
|
-
getMainAgent() {
|
|
412
|
-
return this._mainAgent;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* 获取主 Agent 的 System Prompt
|
|
417
|
-
* @returns {string}
|
|
418
|
-
*/
|
|
419
|
-
getSystemPrompt() {
|
|
420
|
-
if (this._mainAgent) {
|
|
421
|
-
return this._mainAgent._buildSystemPrompt();
|
|
422
|
-
}
|
|
423
|
-
return '';
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* 获取插件实例(安全访问)
|
|
428
|
-
* @param {string} name - 插件名称
|
|
429
|
-
* @returns {Object|null}
|
|
430
|
-
*/
|
|
431
|
-
getPluginInstance(name) {
|
|
432
|
-
const entry = this.pluginManager.get(name);
|
|
433
|
-
return entry || null;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* 获取所有 Agent 列表
|
|
438
|
-
* @returns {Array<Agent>}
|
|
439
|
-
*/
|
|
440
|
-
getAgents() {
|
|
441
|
-
return [...this._agents];
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* 获取所有 SessionContext 的 ID 列表
|
|
446
|
-
* @returns {string[]}
|
|
447
|
-
*/
|
|
448
|
-
listSessionContexts() {
|
|
449
|
-
return Array.from(this._sessionContexts.keys());
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// ============================================================================
|
|
453
|
-
// 事件描述注册(供 Ambient Agent 使用)
|
|
454
|
-
// ============================================================================
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
* 注册事件描述
|
|
458
|
-
* @param {string} eventType - 事件类型,如 'tool:result'
|
|
459
|
-
* @param {string} description - 事件描述
|
|
460
|
-
* @param {Object} schema - 事件参数 Schema(可选)
|
|
461
|
-
*/
|
|
462
|
-
registerEventDescription(eventType, description, schema = null) {
|
|
463
|
-
this.pluginManager.registerEventDescription(eventType, description, schema);
|
|
464
|
-
return this;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* 获取所有已注册的事件描述
|
|
469
|
-
* @returns {Map<string, Object>} 事件描述 Map
|
|
470
|
-
*/
|
|
471
|
-
getEventDescriptions() {
|
|
472
|
-
return this.pluginManager.getEventDescriptions();
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* 获取单个事件描述
|
|
477
|
-
* @param {string} eventType - 事件类型
|
|
478
|
-
* @returns {Object|null} 事件描述
|
|
479
|
-
*/
|
|
480
|
-
getEventDescription(eventType) {
|
|
481
|
-
return this.pluginManager.getEventDescription(eventType);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
/**
|
|
485
|
-
* 执行工具
|
|
486
|
-
* @param {string} name - 工具名称
|
|
487
|
-
* @param {Object} args - 参数
|
|
488
|
-
*/
|
|
489
|
-
async executeTool(name, args) {
|
|
490
|
-
return this.toolRegistry.execute(name, args, this);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* 在上下文中执行函数(支持并行处理,自动隔离 session)
|
|
495
|
-
* @param {Object} context - 执行上下文 { sessionId, ... }
|
|
496
|
-
* @param {Function} fn - 要执行的异步函数
|
|
497
|
-
* @returns {Promise}
|
|
498
|
-
*/
|
|
499
|
-
runWithContext(context, fn) {
|
|
500
|
-
return asyncLocalStorage.run(context, fn);
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
/**
|
|
504
|
-
* 获取当前执行上下文(在 runWithContext 内部调用)
|
|
505
|
-
* @returns {Object|null}
|
|
506
|
-
*/
|
|
507
|
-
getExecutionContext() {
|
|
508
|
-
return asyncLocalStorage.getStore() || null;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* @deprecated 使用 runWithContext 代替
|
|
513
|
-
*/
|
|
514
|
-
setExecutionContext() {}
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* @deprecated 使用 runWithContext 代替
|
|
518
|
-
*/
|
|
519
|
-
clearExecutionContext() {}
|
|
520
|
-
|
|
521
|
-
// ============================================================================
|
|
522
|
-
// 分层上下文系统 - Session Context
|
|
523
|
-
// ============================================================================
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
* 获取或创建 SessionManager
|
|
527
|
-
* @param {string} sessionId - 会话 ID
|
|
528
|
-
* @param {Object} options - 配置选项
|
|
529
|
-
* @returns {SessionManager}
|
|
530
|
-
*/
|
|
531
|
-
getOrCreateSessionContext(sessionId, options = {}) {
|
|
532
|
-
if (!sessionId) {
|
|
533
|
-
return null;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// 先检查内存缓存
|
|
537
|
-
let manager = this._sessionContexts.get(sessionId);
|
|
538
|
-
if (manager) {
|
|
539
|
-
return manager;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
// 获取工作目录
|
|
543
|
-
const cwd = options.cwd || this._cwd;
|
|
544
|
-
const sessionDir = path.join(cwd, '.foliko', 'sessions');
|
|
545
|
-
|
|
546
|
-
// 获取或创建 session file path
|
|
547
|
-
const sessionFile = options.sessionFile || path.join(sessionDir, `${sessionId}.jsonl`);
|
|
548
|
-
|
|
549
|
-
// 尝试打开已存在的 session
|
|
550
|
-
try {
|
|
551
|
-
if (require('fs').existsSync(sessionFile)) {
|
|
552
|
-
manager = SessionManager.open(sessionFile, sessionDir, cwd);
|
|
553
|
-
}
|
|
554
|
-
} catch (err) {
|
|
555
|
-
// 文件损坏或不存在,创建新的
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
if (!manager) {
|
|
559
|
-
// 创建新的 SessionManager
|
|
560
|
-
manager = new SessionManager(cwd, sessionDir, sessionFile, true);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
this._sessionContexts.set(sessionId, manager);
|
|
564
|
-
this.emit('session:context-created', { sessionId, manager });
|
|
565
|
-
return manager;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
* 异步加载 SessionManager(如果存在)
|
|
570
|
-
* @param {string} sessionId
|
|
571
|
-
* @param {Object} options
|
|
572
|
-
* @returns {Promise<SessionManager|null>}
|
|
573
|
-
*/
|
|
574
|
-
async loadSessionContext(sessionId, options = {}) {
|
|
575
|
-
if (!sessionId) {
|
|
576
|
-
return null;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// 先检查内存缓存
|
|
580
|
-
let manager = this._sessionContexts.get(sessionId);
|
|
581
|
-
if (manager) {
|
|
582
|
-
return manager;
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
const cwd = options.cwd || this._cwd;
|
|
586
|
-
const sessionDir = path.join(cwd, '.foliko', 'sessions');
|
|
587
|
-
const sessionFile = path.join(sessionDir, `${sessionId}.jsonl`);
|
|
588
|
-
|
|
589
|
-
try {
|
|
590
|
-
if (require('fs').existsSync(sessionFile)) {
|
|
591
|
-
manager = SessionManager.open(sessionFile, sessionDir, cwd);
|
|
592
|
-
this._sessionContexts.set(sessionId, manager);
|
|
593
|
-
this.emit('session:context-created', { sessionId, manager });
|
|
594
|
-
}
|
|
595
|
-
} catch (err) {
|
|
596
|
-
// 文件不存在或损坏
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
return manager || null;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/**
|
|
603
|
-
* 配置 Session 存储目录
|
|
604
|
-
* @param {Object} config - 存储配置
|
|
605
|
-
* @param {string} [config.baseDir='.foliko/sessions'] - 文件存储目录
|
|
606
|
-
*/
|
|
607
|
-
configureSessionStorage(config) {
|
|
608
|
-
this._sessionStorageConfig = config;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
/**
|
|
612
|
-
* 获取当前 Session 存储配置
|
|
613
|
-
* @returns {Object}
|
|
614
|
-
*/
|
|
615
|
-
getSessionStorage() {
|
|
616
|
-
return this._sessionStorageConfig;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
/**
|
|
620
|
-
* 获取 SessionManager
|
|
621
|
-
* @param {string} sessionId - 会话 ID
|
|
622
|
-
* @returns {SessionManager|null}
|
|
623
|
-
*/
|
|
624
|
-
getSessionContext(sessionId) {
|
|
625
|
-
return this._sessionContexts.get(sessionId) || null;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
/**
|
|
629
|
-
* 获取当前 Session 上下文(从 AsyncLocalStorage 获取)
|
|
630
|
-
* @returns {SessionManager|null}
|
|
631
|
-
*/
|
|
632
|
-
getCurrentSessionContext() {
|
|
633
|
-
const store = sessionStorage.getStore();
|
|
634
|
-
if (!store) return null;
|
|
635
|
-
const sessionId = store.sessionId;
|
|
636
|
-
return sessionId ? this._sessionContexts.get(sessionId) || null : null;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
/**
|
|
640
|
-
* 获取当前 sessionId(从 AsyncLocalStorage 获取)
|
|
641
|
-
* @returns {string|null}
|
|
642
|
-
*/
|
|
643
|
-
getCurrentSessionId() {
|
|
644
|
-
const store = sessionStorage.getStore();
|
|
645
|
-
return store?.sessionId || null;
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
/**
|
|
649
|
-
* 在 Session 上下文中执行函数
|
|
650
|
-
* @param {string} sessionId - 会话 ID
|
|
651
|
-
* @param {Object} options - Session 选项
|
|
652
|
-
* @param {Function} fn - 异步函数
|
|
653
|
-
* @returns {Promise}
|
|
654
|
-
*/
|
|
655
|
-
runInSession(sessionId, options, fn) {
|
|
656
|
-
if (!sessionId) {
|
|
657
|
-
// 无 session,直接执行
|
|
658
|
-
return fn();
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
const manager = this.getOrCreateSessionContext(sessionId, options);
|
|
662
|
-
const context = { sessionContext: manager, sessionId };
|
|
663
|
-
return sessionStorage.run(context, fn);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
/**
|
|
667
|
-
* 销毁 Session Manager
|
|
668
|
-
* @param {string} sessionId
|
|
669
|
-
*/
|
|
670
|
-
destroySessionContext(sessionId) {
|
|
671
|
-
if (this._sessionContexts.has(sessionId)) {
|
|
672
|
-
const manager = this._sessionContexts.get(sessionId);
|
|
673
|
-
this._sessionContexts.delete(sessionId);
|
|
674
|
-
this.emit('session:context-destroyed', { sessionId });
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
// Session TTL 清理配置
|
|
679
|
-
_sessionTTL = 30 * 60 * 1000; // 默认 30 分钟
|
|
680
|
-
_sessionCleanupInterval = null;
|
|
681
|
-
|
|
682
|
-
/**
|
|
683
|
-
* 配置 Session TTL
|
|
684
|
-
* @param {number} ttlMs - TTL 毫秒数
|
|
685
|
-
*/
|
|
686
|
-
setSessionTTL(ttlMs) {
|
|
687
|
-
this._sessionTTL = ttlMs;
|
|
688
|
-
return this;
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
// ============================================================================
|
|
692
|
-
// 工作目录与 Agent 目录
|
|
693
|
-
// ============================================================================
|
|
694
|
-
|
|
695
|
-
/**
|
|
696
|
-
* 获取当前工作目录
|
|
697
|
-
* @returns {string}
|
|
698
|
-
*/
|
|
699
|
-
getCwd() {
|
|
700
|
-
return this._cwd;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
/**
|
|
704
|
-
* 获取项目级 Agent 目录(绝对路径)
|
|
705
|
-
* @returns {string}
|
|
706
|
-
*/
|
|
707
|
-
getAgentDir() {
|
|
708
|
-
return path.resolve(this._cwd, this._agentDir);
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
/**
|
|
712
|
-
* 获取用户级 Agent 目录(绝对路径)
|
|
713
|
-
* @returns {string}
|
|
714
|
-
*/
|
|
715
|
-
getHomeAgentDir() {
|
|
716
|
-
return this._homeAgentDir;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
/**
|
|
720
|
-
* 解析项目级 Agent 目录下的相对路径
|
|
721
|
-
* @param {...string} parts
|
|
722
|
-
* @returns {string}
|
|
723
|
-
*/
|
|
724
|
-
resolveAgentPath(...parts) {
|
|
725
|
-
return path.join(this.getAgentDir(), ...parts);
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
/**
|
|
729
|
-
* 解析用户级 Agent 目录下的相对路径
|
|
730
|
-
* @param {...string} parts
|
|
731
|
-
* @returns {string}
|
|
732
|
-
*/
|
|
733
|
-
resolveHomeAgentPath(...parts) {
|
|
734
|
-
return path.join(this.getHomeAgentDir(), ...parts);
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
/**
|
|
738
|
-
* 动态切换工作目录
|
|
739
|
-
*
|
|
740
|
-
* 切换后会自动:
|
|
741
|
-
* 1. 更新 PluginManager 状态文件位置
|
|
742
|
-
* 2. 销毁已存在的 Session 上下文(旧路径失效)
|
|
743
|
-
* 3. 通知所有已加载插件(onCwdChanged 钩子)并触发 reload
|
|
744
|
-
* 4. 发送 cwd:changed 事件
|
|
745
|
-
*
|
|
746
|
-
* @param {string} newCwd - 新工作目录(相对或绝对路径)
|
|
747
|
-
* @param {Object} [options]
|
|
748
|
-
* @param {boolean} [options.reload=true] - 是否自动重载已加载插件
|
|
749
|
-
* @param {string} [options.agentDir] - 顺带覆盖项目 agent 目录名
|
|
750
|
-
* @param {string} [options.homeAgentDir] - 顺带覆盖用户 agent 目录路径
|
|
751
|
-
* @returns {Promise<this>}
|
|
752
|
-
*/
|
|
753
|
-
async setCwd(newCwd, options = {}) {
|
|
754
|
-
if (this._changingCwd) {
|
|
755
|
-
throw new Error('setCwd re-entered during cwd change');
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
const oldCwd = this._cwd;
|
|
759
|
-
const resolved = path.resolve(newCwd);
|
|
760
|
-
if (resolved === oldCwd) {
|
|
761
|
-
return this;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
this._changingCwd = true;
|
|
765
|
-
try {
|
|
766
|
-
this._cwd = resolved;
|
|
767
|
-
if (options.agentDir) this._agentDir = options.agentDir;
|
|
768
|
-
if (options.homeAgentDir) this._homeAgentDir = options.homeAgentDir;
|
|
769
|
-
|
|
770
|
-
// 1. 更新 PluginManager 状态文件位置
|
|
771
|
-
this.pluginManager._setStateFile(resolved);
|
|
772
|
-
|
|
773
|
-
// 2. 销毁已存在的 Session 上下文(旧路径失效)
|
|
774
|
-
const count = this._sessionContexts.size;
|
|
775
|
-
for (const sid of Array.from(this._sessionContexts.keys())) {
|
|
776
|
-
this.destroySessionContext(sid);
|
|
777
|
-
}
|
|
778
|
-
this.emit('session:contexts-invalidated', { oldCwd, newCwd: resolved, count });
|
|
779
|
-
|
|
780
|
-
// 3. 通知已加载插件 + 触发 reload
|
|
781
|
-
if (options.reload !== false) {
|
|
782
|
-
for (const entry of this.pluginManager.getAll()) {
|
|
783
|
-
const inst = entry.instance;
|
|
784
|
-
try {
|
|
785
|
-
if (typeof inst?.onCwdChanged === 'function') {
|
|
786
|
-
await inst.onCwdChanged(oldCwd, resolved, this);
|
|
787
|
-
}
|
|
788
|
-
await this.pluginManager.reload(entry.name);
|
|
789
|
-
} catch (err) {
|
|
790
|
-
this.logger.warn(`Plugin ${entry.name} failed during cwd change: ${err.message}`);
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
// 4. 发送 cwd:changed 事件
|
|
796
|
-
this.emit('cwd:changed', { oldCwd, newCwd: resolved, framework: this });
|
|
797
|
-
} finally {
|
|
798
|
-
this._changingCwd = false;
|
|
799
|
-
}
|
|
800
|
-
return this;
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
// ============================================================================
|
|
804
|
-
// 项目重扫 — 重新扫描当前 cwd 下的插件与技能
|
|
805
|
-
// ============================================================================
|
|
806
|
-
|
|
807
|
-
/**
|
|
808
|
-
* 重新扫描当前 cwd 下的插件与技能目录
|
|
809
|
-
*
|
|
810
|
-
* 工作流程:
|
|
811
|
-
* 1. 卸载 keepLoaded 列表外的所有已加载插件
|
|
812
|
-
* 2. 重新读取 DefaultPlugins 配置(新的 skillsDirs / agentsDir / pluginLinks)
|
|
813
|
-
* 3. 重新跑 bootstrapDefaults 加载所有默认插件(核心 + 自定义)
|
|
814
|
-
* 4. 同步 SkillManager._skillsDirs 并触发技能重扫
|
|
815
|
-
* 5. 发出 rescan:complete 事件
|
|
816
|
-
*
|
|
817
|
-
* 典型用法: 配合 setCwd 切换到新项目
|
|
818
|
-
* await framework.setCwd('D:/new-project');
|
|
819
|
-
* await framework.rescanProject();
|
|
820
|
-
*
|
|
821
|
-
* @param {Object} [options]
|
|
822
|
-
* @param {string[]} [options.keepLoaded] - 保留的插件名列表(默认保留系统插件)
|
|
823
|
-
* @param {boolean} [options.reloadKept=false] - 是否对 keepLoaded 中的插件也执行 reload
|
|
824
|
-
* @param {boolean} [options.reloadSkillManager=true] - 是否重扫技能
|
|
825
|
-
* @returns {Promise<{unloaded: string[], loaded: string[], keepLoaded: string[]}>}
|
|
826
|
-
*/
|
|
827
|
-
async rescanProject(options = {}) {
|
|
828
|
-
const { bootstrapDefaults, DefaultPlugins, loadAgentConfig } = require('../../plugins/default-plugins');
|
|
829
|
-
const keepList = options.keepLoaded || Array.from(SYSTEM_PLUGINS);
|
|
830
|
-
const keepSet = new Set(keepList);
|
|
831
|
-
const unloadList = [];
|
|
832
|
-
const loadList = [];
|
|
833
|
-
|
|
834
|
-
// 1. 卸载 keepSet 之外的插件
|
|
835
|
-
for (const entry of Array.from(this.pluginManager.getAll())) {
|
|
836
|
-
if (keepSet.has(entry.name)) continue;
|
|
837
|
-
const name = entry.name;
|
|
838
|
-
try {
|
|
839
|
-
await this.pluginManager.unload(name);
|
|
840
|
-
// 同步从注册表移除,避免 bootstrapDefaults 的 has() 跳过
|
|
841
|
-
this.pluginManager._plugins.delete(name);
|
|
842
|
-
unloadList.push(name);
|
|
843
|
-
} catch (err) {
|
|
844
|
-
this.logger.warn(`rescanProject: failed to unload ${name}: ${err.message}`);
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
// 2. 确保 defaults 插件已加载,重新读取配置
|
|
849
|
-
let defaultsPlugin = this.pluginManager.get('defaults');
|
|
850
|
-
if (!defaultsPlugin) {
|
|
851
|
-
const dp = new DefaultPlugins({ agentDir: this._agentDir });
|
|
852
|
-
try {
|
|
853
|
-
await this.loadPlugin(dp);
|
|
854
|
-
defaultsPlugin = dp;
|
|
855
|
-
} catch (err) {
|
|
856
|
-
this.logger.warn(`rescanProject: failed to load defaults: ${err.message}`);
|
|
857
|
-
}
|
|
858
|
-
} else {
|
|
859
|
-
try {
|
|
860
|
-
await defaultsPlugin.reload(this);
|
|
861
|
-
} catch (err) {
|
|
862
|
-
this.logger.warn(`rescanProject: failed to reload defaults: ${err.message}`);
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
const agentConfig = defaultsPlugin
|
|
866
|
-
? defaultsPlugin.getConfig() || loadAgentConfig(this, this._agentDir)
|
|
867
|
-
: loadAgentConfig(this, this._agentDir);
|
|
868
|
-
|
|
869
|
-
// 3. 跑 bootstrapDefaults 加载默认插件(核心 + 自定义)
|
|
870
|
-
const beforeNames = new Set(this.pluginManager.getAll().map((p) => p.name));
|
|
871
|
-
try {
|
|
872
|
-
await bootstrapDefaults(this, { _config: agentConfig, _skipConfigLoad: true });
|
|
873
|
-
} catch (err) {
|
|
874
|
-
this.logger.warn(`rescanProject: bootstrapDefaults failed: ${err.message}`);
|
|
875
|
-
}
|
|
876
|
-
for (const name of this.pluginManager.getAll().map((p) => p.name)) {
|
|
877
|
-
if (!beforeNames.has(name)) loadList.push(name);
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
// 4. 同步并重扫技能
|
|
881
|
-
if (options.reloadSkillManager !== false) {
|
|
882
|
-
const skillManager = this.pluginManager.get('skill-manager');
|
|
883
|
-
if (skillManager) {
|
|
884
|
-
if (Array.isArray(agentConfig.skillsDirs)) {
|
|
885
|
-
skillManager._skillsDirs = agentConfig.skillsDirs;
|
|
886
|
-
}
|
|
887
|
-
try {
|
|
888
|
-
await skillManager.reload(this);
|
|
889
|
-
} catch (err) {
|
|
890
|
-
this.logger.warn(`rescanProject: skill-manager reload failed: ${err.message}`);
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
// 5. (可选) 对保留的插件也执行 reload
|
|
896
|
-
if (options.reloadKept) {
|
|
897
|
-
for (const name of keepList) {
|
|
898
|
-
if (!this.pluginManager.has(name)) continue;
|
|
899
|
-
try {
|
|
900
|
-
await this.pluginManager.reload(name);
|
|
901
|
-
} catch (err) {
|
|
902
|
-
this.logger.warn(`rescanProject: reload kept ${name} failed: ${err.message}`);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
const result = { unloaded: unloadList, loaded: loadList, keepLoaded: keepList };
|
|
908
|
-
this.emit('rescan:complete', result);
|
|
909
|
-
this.logger.info(
|
|
910
|
-
`rescanProject: unloaded=${unloadList.length} loaded=${loadList.length} kept=${keepList.length}`
|
|
911
|
-
);
|
|
912
|
-
return result;
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
/**
|
|
916
|
-
* 启动 Session 自动清理
|
|
917
|
-
* @param {number} [intervalMs=60000] - 检查间隔
|
|
918
|
-
*/
|
|
919
|
-
startSessionCleanup(intervalMs = 60000) {
|
|
920
|
-
if (this._sessionCleanupInterval) {
|
|
921
|
-
clearInterval(this._sessionCleanupInterval);
|
|
922
|
-
}
|
|
923
|
-
this._sessionCleanupInterval = setInterval(() => {
|
|
924
|
-
this._cleanupStaleSessions();
|
|
925
|
-
}, intervalMs);
|
|
926
|
-
if (this._sessionCleanupInterval.unref) {
|
|
927
|
-
this._sessionCleanupInterval.unref();
|
|
928
|
-
}
|
|
929
|
-
return this;
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
/**
|
|
933
|
-
* 停止 Session 自动清理
|
|
934
|
-
*/
|
|
935
|
-
stopSessionCleanup() {
|
|
936
|
-
if (this._sessionCleanupInterval) {
|
|
937
|
-
clearInterval(this._sessionCleanupInterval);
|
|
938
|
-
this._sessionCleanupInterval = null;
|
|
939
|
-
}
|
|
940
|
-
return this;
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
/**
|
|
944
|
-
* 清理过期的 Session
|
|
945
|
-
* @private
|
|
946
|
-
*/
|
|
947
|
-
_cleanupStaleSessions() {
|
|
948
|
-
const now = Date.now();
|
|
949
|
-
const staleIds = [];
|
|
950
|
-
for (const [sessionId, manager] of this._sessionContexts) {
|
|
951
|
-
const metadata = manager.getMetadata?.();
|
|
952
|
-
if (metadata && metadata.lastActive && (now - metadata.lastActive) > this._sessionTTL) {
|
|
953
|
-
staleIds.push(sessionId);
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
for (const id of staleIds) {
|
|
957
|
-
this.destroySessionContext(id);
|
|
958
|
-
}
|
|
959
|
-
if (staleIds.length > 0) {
|
|
960
|
-
this.logger.debug(`Cleaned up ${staleIds.length} stale sessions`);
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
/**
|
|
965
|
-
* 列出所有活跃的 Session ID
|
|
966
|
-
* @returns {string[]}
|
|
967
|
-
*/
|
|
968
|
-
listActiveSessions() {
|
|
969
|
-
return Array.from(this._sessionContexts.keys());
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
// ============================================================================
|
|
973
|
-
// 分层上下文系统 - Request Context
|
|
974
|
-
// ============================================================================
|
|
975
|
-
|
|
976
|
-
/**
|
|
977
|
-
* 获取当前 Request 上下文
|
|
978
|
-
* @returns {Object|null}
|
|
979
|
-
*/
|
|
980
|
-
getRequestContext() {
|
|
981
|
-
return requestStorage.getStore() || null;
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
/**
|
|
985
|
-
* 在 Request 上下文中执行函数
|
|
986
|
-
* @param {Object} context - Request 上下文
|
|
987
|
-
* @param {Function} fn - 异步函数
|
|
988
|
-
* @returns {Promise}
|
|
989
|
-
*/
|
|
990
|
-
runWithRequestContext(context, fn) {
|
|
991
|
-
return requestStorage.run(context, fn);
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
// ============================================================================
|
|
995
|
-
// 分层上下文系统 - 组合上下文
|
|
996
|
-
// ============================================================================
|
|
997
|
-
|
|
998
|
-
/**
|
|
999
|
-
* 在完整上下文中执行(Request + Session)
|
|
1000
|
-
* @param {Object} requestOptions - Request 选项
|
|
1001
|
-
* @param {string} sessionId - Session ID
|
|
1002
|
-
* @param {Function} fn - 异步函数
|
|
1003
|
-
* @returns {Promise}
|
|
1004
|
-
*/
|
|
1005
|
-
async runInContext(requestOptions, sessionId, fn) {
|
|
1006
|
-
const sessionCtx = sessionId ? this.getOrCreateSessionContext(sessionId) : null;
|
|
1007
|
-
|
|
1008
|
-
return requestStorage.run(requestOptions, () => {
|
|
1009
|
-
if (sessionCtx) {
|
|
1010
|
-
return sessionStorage.run({ sessionContext: sessionCtx, sessionId }, fn);
|
|
1011
|
-
}
|
|
1012
|
-
return fn();
|
|
1013
|
-
});
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
/**
|
|
1017
|
-
* 创建 Agent
|
|
1018
|
-
* @param {Object} config - Agent 配置
|
|
1019
|
-
*/
|
|
1020
|
-
createAgent(config) {
|
|
1021
|
-
// 合并 AI 插件配置
|
|
1022
|
-
config = this._mergeAIConfig(config);
|
|
1023
|
-
|
|
1024
|
-
const agent = new Agent(this, config);
|
|
1025
|
-
|
|
1026
|
-
// 跟踪 agent
|
|
1027
|
-
this._agents.push(agent);
|
|
1028
|
-
if (!this._mainAgent) {
|
|
1029
|
-
this._mainAgent = agent;
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
this.emit('agent:created', agent);
|
|
1033
|
-
return agent;
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
/**
|
|
1037
|
-
* 为指定 session 创建独立 Agent 实例
|
|
1038
|
-
* @param {string} sessionId - session ID
|
|
1039
|
-
* @param {Object} config - Agent 配置
|
|
1040
|
-
*/
|
|
1041
|
-
createSessionAgent(sessionId, config = {}) {
|
|
1042
|
-
const agentConfig = {
|
|
1043
|
-
name: `session_${sessionId}`,
|
|
1044
|
-
...config,
|
|
1045
|
-
};
|
|
1046
|
-
|
|
1047
|
-
// 合并 AI 插件配置
|
|
1048
|
-
const merged = this._mergeAIConfig(agentConfig);
|
|
1049
|
-
|
|
1050
|
-
const agent = new Agent(this, merged);
|
|
1051
|
-
this._agents.push(agent);
|
|
1052
|
-
|
|
1053
|
-
// 如果提供了 systemPrompt,需要替换 AgentChatHandler
|
|
1054
|
-
if (merged.systemPrompt) {
|
|
1055
|
-
agent.setSystemPrompt(merged.systemPrompt);
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
this.emit('agent:created', agent);
|
|
1059
|
-
return agent;
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
/**
|
|
1063
|
-
* 创建子Agent(轻量级,单次任务)
|
|
1064
|
-
* @param {Object} config - 子Agent配置
|
|
1065
|
-
* @param {string} config.name - 子Agent名称
|
|
1066
|
-
* @param {string} [config.role] - 角色描述
|
|
1067
|
-
* @param {string} [config.description] - 描述
|
|
1068
|
-
* @param {string} [config.systemPrompt] - 直接指定的系统提示词(优先使用)
|
|
1069
|
-
* @param {Object} [config.tools] - 自定义工具 { name: toolDef }
|
|
1070
|
-
* @param {string[]} [config.parentTools] - 从父Agent继承的工具名称列表
|
|
1071
|
-
* @param {Object} [config.llmConfig] - 独立LLM配置
|
|
1072
|
-
* @returns {Subagent} 创建的子Agent
|
|
1073
|
-
*/
|
|
1074
|
-
createSubAgent(config) {
|
|
1075
|
-
const {
|
|
1076
|
-
name,
|
|
1077
|
-
role,
|
|
1078
|
-
description = '',
|
|
1079
|
-
systemPrompt: providedPrompt,
|
|
1080
|
-
tools = {},
|
|
1081
|
-
parentTools, // 如果不传,默认继承主agent所有工具
|
|
1082
|
-
llmConfig = null,
|
|
1083
|
-
maxRetries,
|
|
1084
|
-
disableTools,
|
|
1085
|
-
} = config;
|
|
1086
|
-
|
|
1087
|
-
// 获取 AI 配置
|
|
1088
|
-
const aiPlugin = this.pluginManager.get('ai');
|
|
1089
|
-
const aiConfig = aiPlugin ? aiPlugin.getConfig() : {};
|
|
1090
|
-
const mergedAIConfig = { ...aiConfig, ...(llmConfig || {}) };
|
|
1091
|
-
|
|
1092
|
-
// 构建工具列表
|
|
1093
|
-
// const toolList = [];
|
|
1094
|
-
|
|
1095
|
-
// // 添加自定义工具
|
|
1096
|
-
// for (const [toolName, toolDef] of Object.entries(tools)) {
|
|
1097
|
-
// if (toolDef && typeof toolDef === 'object') {
|
|
1098
|
-
// toolList.push({
|
|
1099
|
-
// name: toolName,
|
|
1100
|
-
// description: toolDef.description || '',
|
|
1101
|
-
// inputSchema: toolDef.inputSchema,
|
|
1102
|
-
// execute: toolDef.execute,
|
|
1103
|
-
// });
|
|
1104
|
-
// }
|
|
1105
|
-
// }
|
|
1106
|
-
|
|
1107
|
-
// 创建 Subagent
|
|
1108
|
-
// 如果提供了 systemPrompt 则直接使用,否则让 Subagent 动态构建
|
|
1109
|
-
const subagent = new Subagent({
|
|
1110
|
-
name: `subagent_${name}`,
|
|
1111
|
-
role: role || name,
|
|
1112
|
-
description: description,
|
|
1113
|
-
systemPrompt: providedPrompt,
|
|
1114
|
-
model: mergedAIConfig.model || 'deepseek-chat',
|
|
1115
|
-
provider: mergedAIConfig.provider || 'deepseek',
|
|
1116
|
-
apiKey: mergedAIConfig.apiKey,
|
|
1117
|
-
baseURL: mergedAIConfig.baseURL,
|
|
1118
|
-
providerOptions: {
|
|
1119
|
-
maxOutputTokens: mergedAIConfig.maxOutputTokens || 8192,
|
|
1120
|
-
temperature: mergedAIConfig.temperature || 0.3,
|
|
1121
|
-
},
|
|
1122
|
-
tools: tools,
|
|
1123
|
-
parentTools: parentTools,
|
|
1124
|
-
framework: this, // 传递 framework 引用用于动态构建系统提示词
|
|
1125
|
-
maxRetries,
|
|
1126
|
-
disableTools,
|
|
1127
|
-
});
|
|
1128
|
-
|
|
1129
|
-
// 注册主 Agent 的提示词部分到子 Agent(选择性注册)
|
|
1130
|
-
if (this._mainAgent && this._mainAgent._systemPromptBuilder) {
|
|
1131
|
-
const mainParts = this._mainAgent._systemPromptBuilder._parts;
|
|
1132
|
-
if (mainParts) {
|
|
1133
|
-
for (const [name, part] of mainParts) {
|
|
1134
|
-
// 子 Agent 跳过这些不适合的部分
|
|
1135
|
-
if (['original-prompt', 'shared-prompt', 'datetime', 'metadata', 'capabilities'].includes(name)) {
|
|
1136
|
-
continue;
|
|
1137
|
-
}
|
|
1138
|
-
subagent.registerPromptPart(name, part.priority, part.provider);
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
this._agents.push(subagent);
|
|
1144
|
-
this.emit('agent:created', subagent);
|
|
1145
|
-
return subagent;
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
/**
|
|
1149
|
-
* 同步主Agent的提示词部分到所有子Agent
|
|
1150
|
-
* @public — 插件可能需要调用
|
|
1151
|
-
*/
|
|
1152
|
-
syncPromptPartsToSubagents() {
|
|
1153
|
-
if (!this._mainAgent || !this._mainAgent._systemPromptBuilder) return;
|
|
1154
|
-
const mainParts = this._mainAgent._systemPromptBuilder._parts;
|
|
1155
|
-
if (!mainParts) return;
|
|
1156
|
-
|
|
1157
|
-
// 遍历所有 agents,排除主agent
|
|
1158
|
-
for (const agent of this._agents) {
|
|
1159
|
-
if (agent === this._mainAgent) continue;
|
|
1160
|
-
// 只同步 Subagent
|
|
1161
|
-
if (!agent._isSubagent) continue;
|
|
1162
|
-
|
|
1163
|
-
// Build a Set of already registered parts for O(1) lookup
|
|
1164
|
-
const registeredNames = new Set(agent._promptParts?.map(p => p.name) || []);
|
|
1165
|
-
|
|
1166
|
-
for (const [name, part] of mainParts) {
|
|
1167
|
-
// 子 Agent 跳过这些不适合的部分
|
|
1168
|
-
if (['original-prompt', 'shared-prompt', 'datetime', 'metadata', 'capabilities'].includes(name)) {
|
|
1169
|
-
continue;
|
|
1170
|
-
}
|
|
1171
|
-
// O(1) lookup instead of O(m)
|
|
1172
|
-
if (!registeredNames.has(name)) {
|
|
1173
|
-
agent.registerPromptPart(name, part.priority, part.provider);
|
|
1174
|
-
registeredNames.add(name);
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
/**
|
|
1181
|
-
* 构建子Agent的系统提示词
|
|
1182
|
-
* @private
|
|
1183
|
-
*/
|
|
1184
|
-
_buildSubAgentSystemPrompt(role, description, tools) {
|
|
1185
|
-
const lines = [];
|
|
1186
|
-
const roleName = role || '助手';
|
|
1187
|
-
const descText = description || '';
|
|
1188
|
-
|
|
1189
|
-
// 1. 角色定义
|
|
1190
|
-
lines.push(`你是 ${roleName}。`);
|
|
1191
|
-
if (descText) {
|
|
1192
|
-
lines.push(descText);
|
|
1193
|
-
}
|
|
1194
|
-
lines.push('');
|
|
1195
|
-
|
|
1196
|
-
// 2. 主Agent的系统提示词(基础部分)
|
|
1197
|
-
const mainAgent = this.getMainAgent();
|
|
1198
|
-
if (mainAgent) {
|
|
1199
|
-
const mainPrompt = mainAgent.getOriginalPrompt();
|
|
1200
|
-
if (mainPrompt) {
|
|
1201
|
-
lines.push('## 主Agent的系统提示词');
|
|
1202
|
-
lines.push(mainPrompt);
|
|
1203
|
-
lines.push('');
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
// 3. 工具描述
|
|
1208
|
-
if (tools && tools.length > 0) {
|
|
1209
|
-
lines.push('## 可用工具');
|
|
1210
|
-
lines.push('');
|
|
1211
|
-
for (const tool of tools) {
|
|
1212
|
-
const desc = tool.description || '无描述';
|
|
1213
|
-
lines.push(`- **${tool.name}**: ${desc}`);
|
|
1214
|
-
}
|
|
1215
|
-
lines.push('');
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
// 4. 子Agent核心规则
|
|
1219
|
-
lines.push(`## ${roleName} 核心规则
|
|
1220
|
-
|
|
1221
|
-
1. **专注本职任务**:只完成与 ${roleName} 相关的任务,不要处理其他领域的请求。
|
|
1222
|
-
2. **必须先调用工具**:需要信息或操作时,必须调用工具获取真实结果,禁止直接编造。
|
|
1223
|
-
3. **结果导向**:基于工具返回结果回答,不重复工具内部实现细节。
|
|
1224
|
-
4. **多步骤任务**:复杂任务拆解为多个工具调用,逐步完成。
|
|
1225
|
-
5. **响应规范**:直接给出结论,不说"我需要..."等铺垫话术。
|
|
1226
|
-
|
|
1227
|
-
## 禁止事项
|
|
1228
|
-
|
|
1229
|
-
- 不调用工具就直接回答
|
|
1230
|
-
- 编造数据、文件、订单等信息
|
|
1231
|
-
- 处理本职外的任务
|
|
1232
|
-
- 回复含糊不清`);
|
|
1233
|
-
|
|
1234
|
-
return lines.join('\n');
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
/**
|
|
1238
|
-
* 获取 AI 插件
|
|
1239
|
-
* @returns {Object|undefined}
|
|
1240
|
-
*/
|
|
1241
|
-
getAIPlugin() {
|
|
1242
|
-
return this.pluginManager.get('ai');
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
/**
|
|
1246
|
-
* Bootstrap - 使用默认配置启动框架
|
|
1247
|
-
* 自动加载 .foliko/ 目录下的配置和所有默认插件)
|
|
1248
|
-
* @param {Object} config - 配置
|
|
1249
|
-
* @param {string} [config.agentDir='.foliko'] - Agent 配置目录
|
|
1250
|
-
* @param {Object} [config.aiConfig] - AI 配置(可选,覆盖文件配置)
|
|
1251
|
-
* @returns {Promise<Framework>}
|
|
1252
|
-
*/
|
|
1253
|
-
async bootstrap(config = {}) {
|
|
1254
|
-
const {
|
|
1255
|
-
bootstrapDefaults,
|
|
1256
|
-
DefaultPlugins,
|
|
1257
|
-
loadAgentConfig,
|
|
1258
|
-
} = require('../../plugins/default-plugins');
|
|
1259
|
-
|
|
1260
|
-
// 先加载默认插件配置
|
|
1261
|
-
const defaultsPlugin = new DefaultPlugins({
|
|
1262
|
-
agentDir: config.agentDir || '.foliko',
|
|
1263
|
-
});
|
|
1264
|
-
|
|
1265
|
-
await this.loadPlugin(defaultsPlugin);
|
|
1266
|
-
|
|
1267
|
-
// 获取配置
|
|
1268
|
-
const agentConfig = defaultsPlugin.getConfig();
|
|
1269
|
-
|
|
1270
|
-
// 如果提供了 AI 配置,覆盖文件配置
|
|
1271
|
-
if (config.aiConfig) {
|
|
1272
|
-
agentConfig.ai = { ...agentConfig.ai, ...config.aiConfig };
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
// 加载所有默认插件(传入已加载的配置避免重复加载)
|
|
1276
|
-
await bootstrapDefaults(this, { _config: agentConfig, _skipConfigLoad: true });
|
|
1277
|
-
|
|
1278
|
-
this._setReady();
|
|
1279
|
-
return this;
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
/**
|
|
1283
|
-
* 等待框架就绪
|
|
1284
|
-
*/
|
|
1285
|
-
async ready() {
|
|
1286
|
-
if (this._ready) {
|
|
1287
|
-
return this;
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
if (!this._readyPromise) {
|
|
1291
|
-
this._readyPromise = new Promise((resolve) => {
|
|
1292
|
-
this._resolveReady = resolve;
|
|
1293
|
-
});
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
await this._readyPromise;
|
|
1297
|
-
return this;
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
/**
|
|
1301
|
-
* 标记框架就绪
|
|
1302
|
-
* @private
|
|
1303
|
-
*/
|
|
1304
|
-
_setReady() {
|
|
1305
|
-
this._ready = true;
|
|
1306
|
-
if (this._resolveReady) {
|
|
1307
|
-
this._resolveReady(this);
|
|
1308
|
-
}
|
|
1309
|
-
this.emit('framework:ready', this);
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
/**
|
|
1313
|
-
* 注册内置工具
|
|
1314
|
-
* @private
|
|
1315
|
-
*/
|
|
1316
|
-
_registerBuiltinTools() {
|
|
1317
|
-
// 内置工具会在 Agent 创建时添加)
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
// ============================================================================
|
|
1321
|
-
// Coordinator 模式管理
|
|
1322
|
-
// ============================================================================
|
|
1323
|
-
|
|
1324
|
-
/**
|
|
1325
|
-
* 初始化Coordinator管理器
|
|
1326
|
-
* @param {Agent} coordinatorAgent - Coordinator Agent实例
|
|
1327
|
-
*/
|
|
1328
|
-
initCoordinatorManager(coordinatorAgent) {
|
|
1329
|
-
if (!this._coordinatorManager) {
|
|
1330
|
-
this._coordinatorManager = new CoordinatorManager(this);
|
|
1331
|
-
}
|
|
1332
|
-
this._coordinatorManager.setCoordinatorAgent(coordinatorAgent);
|
|
1333
|
-
return this._coordinatorManager;
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
/**
|
|
1337
|
-
* 获取Coordinator管理器
|
|
1338
|
-
* @returns {CoordinatorManager|null}
|
|
1339
|
-
*/
|
|
1340
|
-
getCoordinatorManager() {
|
|
1341
|
-
return this._coordinatorManager;
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
/**
|
|
1345
|
-
* 创建Worker(由coordinator-plugin调用)
|
|
1346
|
-
* @param {Object} config - Worker配置
|
|
1347
|
-
* @param {string} config.name - Worker名称
|
|
1348
|
-
* @param {string} config.task - 初始任务
|
|
1349
|
-
* @param {string} [config.systemPrompt] - 可选系统提示
|
|
1350
|
-
* @param {string[]} [config.tools] - 可用工具列表
|
|
1351
|
-
* @returns {Promise<{workerId: string, status: string}>}
|
|
1352
|
-
*/
|
|
1353
|
-
async createWorker(config) {
|
|
1354
|
-
if (!this._coordinatorManager) {
|
|
1355
|
-
throw new Error('CoordinatorManager not initialized. Call initCoordinatorManager first.');
|
|
1356
|
-
}
|
|
1357
|
-
return this._coordinatorManager.createWorker(config);
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
/**
|
|
1361
|
-
* 销毁框架
|
|
1362
|
-
*/
|
|
1363
|
-
async destroy() {
|
|
1364
|
-
// 销毁Coordinator管理器
|
|
1365
|
-
if (this._coordinatorManager) {
|
|
1366
|
-
this._coordinatorManager.destroy();
|
|
1367
|
-
this._coordinatorManager = null;
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
// 销毁所有 agents
|
|
1371
|
-
for (const agent of this._agents) {
|
|
1372
|
-
// 移除 agent 的事件转发监听器
|
|
1373
|
-
const listeners = this._agentForwardingListeners?.get(agent);
|
|
1374
|
-
if (listeners) {
|
|
1375
|
-
agent.off('tool-call', listeners.toolCallHandler);
|
|
1376
|
-
agent.off('tool-result', listeners.toolResultHandler);
|
|
1377
|
-
agent.off('tool-error', listeners.toolErrorHandler);
|
|
1378
|
-
agent.off('message', listeners.messageHandler);
|
|
1379
|
-
this._agentForwardingListeners.delete(agent);
|
|
1380
|
-
}
|
|
1381
|
-
// 调用 agent.destroy() 清理自身资源
|
|
1382
|
-
if (typeof agent.destroy === 'function') {
|
|
1383
|
-
agent.destroy();
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
this._agents = [];
|
|
1387
|
-
|
|
1388
|
-
// 移除 toolRegistry 的事件监听器
|
|
1389
|
-
if (this._toolRegistryListeners) {
|
|
1390
|
-
for (const { event, handler } of this._toolRegistryListeners) {
|
|
1391
|
-
this.toolRegistry.off(event, handler);
|
|
1392
|
-
}
|
|
1393
|
-
this._toolRegistryListeners = [];
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
// 停止 Session 清理
|
|
1397
|
-
this.stopSessionCleanup();
|
|
1398
|
-
|
|
1399
|
-
// 清空 session contexts
|
|
1400
|
-
this._sessionContexts.clear();
|
|
1401
|
-
|
|
1402
|
-
// 卸载所有插件(逐个保护,防止一个失败阻断后续)
|
|
1403
|
-
const plugins = this.pluginManager.getAll();
|
|
1404
|
-
for (const { name } of plugins) {
|
|
1405
|
-
try {
|
|
1406
|
-
await this.pluginManager.unload(name);
|
|
1407
|
-
} catch (err) {
|
|
1408
|
-
this.logger.warn(`Failed to unload plugin '${name}': ${err.message}`);
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
// 清空工具
|
|
1413
|
-
this.toolRegistry.clear();
|
|
1414
|
-
|
|
1415
|
-
// 清空事件
|
|
1416
|
-
this.removeAllListeners();
|
|
1417
|
-
|
|
1418
|
-
this.emit('framework:destroyed');
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
module.exports = { Framework };
|