foliko 1.1.63 → 1.1.65
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/.agent/data/plugins-state.json +8 -0
- package/.agent/sessions/cli_default.json +258 -260
- package/cli/bin/foliko.js +2 -2
- package/cli/src/commands/chat.js +15 -26
- package/cli/src/ui/chat-ui.js +102 -165
- package/cli/src/ui/footer-bar.js +7 -32
- package/cli/src/ui/message-bubble.js +24 -2
- package/cli/src/ui/status-bar.js +177 -0
- package/package.json +1 -2
- package/plugins/audit-plugin.js +11 -7
- package/plugins/coordinator-plugin.js +14 -12
- package/plugins/data-splitter-plugin.js +323 -0
- package/plugins/default-plugins.js +12 -1
- package/plugins/extension-executor-plugin.js +2 -2
- package/plugins/file-system-plugin.js +68 -50
- package/plugins/gate-trading.js +10 -10
- package/plugins/install-plugin.js +3 -3
- package/plugins/memory-plugin.js +8 -11
- package/plugins/plugin-manager-plugin.js +9 -11
- package/plugins/qq-plugin.js +9 -9
- package/plugins/rules-plugin.js +7 -7
- package/plugins/scheduler-plugin.js +22 -18
- package/plugins/session-plugin.js +14 -14
- package/plugins/storage-plugin.js +11 -10
- package/plugins/subagent-plugin.js +13 -9
- package/plugins/think-plugin.js +63 -59
- package/plugins/tools-plugin.js +8 -8
- package/plugins/weixin-plugin.js +5 -5
- package/src/capabilities/skill-manager.js +23 -15
- package/src/capabilities/workflow-engine.js +2 -2
- package/src/core/agent-chat.js +70 -26
- package/src/core/agent.js +17 -27
- package/src/core/chat-session.js +7 -161
- package/src/core/constants.js +198 -0
- package/src/core/context-compressor.js +6 -181
- package/src/core/framework.js +125 -6
- package/src/core/plugin-base.js +7 -5
- package/src/core/provider.js +6 -0
- package/src/core/subagent.js +16 -135
- package/src/core/tool-executor.js +2 -70
- package/src/executors/mcp-executor.js +12 -10
- package/src/utils/chat-queue.js +11 -22
- package/src/utils/data-splitter.js +345 -0
- package/src/utils/download.js +5 -4
- package/src/utils/message-validator.js +283 -0
- package/src/utils/retry.js +168 -22
- package/src/utils/sandbox.js +60 -207
- package/cli/src/utils/debounce.js +0 -106
package/src/core/agent-chat.js
CHANGED
|
@@ -14,20 +14,24 @@ const {
|
|
|
14
14
|
tool: aiTool,
|
|
15
15
|
ToolLoopAgent,
|
|
16
16
|
isLoopFinished,
|
|
17
|
-
generateText,
|
|
18
|
-
streamText,
|
|
19
|
-
RetryError,
|
|
20
|
-
APICallError,
|
|
21
17
|
} = require('ai');
|
|
22
|
-
const {
|
|
18
|
+
const { autoSplitToolResult } = require('../../plugins/data-splitter-plugin');
|
|
19
|
+
const { cleanResponse } = require('../utils');
|
|
23
20
|
const { ChatQueueManager } = require('../utils/chat-queue');
|
|
24
21
|
const { TokenCounter } = require('./token-counter');
|
|
25
22
|
const { isThinkingModel } = require('./provider');
|
|
26
|
-
const fs = require('fs/promises');
|
|
27
23
|
// 新模块
|
|
28
24
|
const { ChatSession } = require('./chat-session');
|
|
29
25
|
const { ToolExecutor } = require('./tool-executor');
|
|
30
26
|
const { ContextCompressor } = require('./context-compressor');
|
|
27
|
+
const {
|
|
28
|
+
DEFAULT_MAX_OUTPUT_TOKENS,
|
|
29
|
+
DEFAULT_TEMPERATURE,
|
|
30
|
+
DEFAULT_MAX_STEPS,
|
|
31
|
+
DEFAULT_MAX_CONCURRENT,
|
|
32
|
+
DEFAULT_RETRY_ATTEMPTS,
|
|
33
|
+
DEFAULT_RETRY_DELAY_MS,
|
|
34
|
+
} = require('./constants');
|
|
31
35
|
|
|
32
36
|
class AgentChatHandler extends EventEmitter {
|
|
33
37
|
/**
|
|
@@ -104,9 +108,9 @@ class AgentChatHandler extends EventEmitter {
|
|
|
104
108
|
|
|
105
109
|
// ChatQueueManager: 队列管理
|
|
106
110
|
this.queueManager = new ChatQueueManager({
|
|
107
|
-
maxConcurrent: config.maxConcurrent ||
|
|
108
|
-
retryAttempts: config.retryAttempts ||
|
|
109
|
-
retryDelay: config.retryDelay ||
|
|
111
|
+
maxConcurrent: config.maxConcurrent || DEFAULT_MAX_CONCURRENT,
|
|
112
|
+
retryAttempts: config.retryAttempts || DEFAULT_RETRY_ATTEMPTS,
|
|
113
|
+
retryDelay: config.retryDelay || DEFAULT_RETRY_DELAY_MS,
|
|
110
114
|
});
|
|
111
115
|
|
|
112
116
|
// AI client
|
|
@@ -183,10 +187,15 @@ class AgentChatHandler extends EventEmitter {
|
|
|
183
187
|
|
|
184
188
|
// ==================== 工具管理(委托给 ToolExecutor) ====================
|
|
185
189
|
|
|
190
|
+
/** AI tools 格式缓存 (避免每次 chat 重建) */
|
|
191
|
+
_aiToolsCache = null;
|
|
192
|
+
_aiToolsCacheKey = ''; // 工具列表的哈希摘要
|
|
193
|
+
|
|
186
194
|
registerTool(tool) {
|
|
187
195
|
this._toolExecutor.registerTool(tool);
|
|
188
196
|
// 工具列表变了,清除缓存
|
|
189
197
|
this._toolsTokensCacheVersion = 0;
|
|
198
|
+
this._aiToolsCache = null; // 清除 AI tools 缓存
|
|
190
199
|
return this;
|
|
191
200
|
}
|
|
192
201
|
|
|
@@ -463,8 +472,7 @@ class AgentChatHandler extends EventEmitter {
|
|
|
463
472
|
throw new Error('AI client not configured.');
|
|
464
473
|
}
|
|
465
474
|
|
|
466
|
-
const systemPrompt = framework.
|
|
467
|
-
// await fs.writeFile('system.md',systemPrompt)
|
|
475
|
+
const systemPrompt = framework.getSystemPrompt();
|
|
468
476
|
const tools = this._getAITools(aiTool);
|
|
469
477
|
const agent = new ToolLoopAgent({
|
|
470
478
|
model: this._aiClient,
|
|
@@ -576,7 +584,9 @@ class AgentChatHandler extends EventEmitter {
|
|
|
576
584
|
yield { type: 'error', error: friendlyMessage };
|
|
577
585
|
} finally {
|
|
578
586
|
const messageStore = this._getSessionMessageStore(sessionId);
|
|
579
|
-
messageStore.save()
|
|
587
|
+
messageStore.save().catch((err) => {
|
|
588
|
+
logger.error(`[${sessionId}] Failed to save message store: ${err.message}`);
|
|
589
|
+
});
|
|
580
590
|
}
|
|
581
591
|
}
|
|
582
592
|
|
|
@@ -602,10 +612,14 @@ class AgentChatHandler extends EventEmitter {
|
|
|
602
612
|
}
|
|
603
613
|
|
|
604
614
|
/**
|
|
605
|
-
*
|
|
615
|
+
* 入队消息(走共享的 ChatQueueManager,与 sendMessage 共用同一队列)
|
|
606
616
|
*/
|
|
607
617
|
enqueue(sessionId, message, options = {}) {
|
|
608
|
-
|
|
618
|
+
const requestId = this.generateRequestId();
|
|
619
|
+
return this.queueManager.enqueue(requestId, sessionId, message, {
|
|
620
|
+
...options,
|
|
621
|
+
executeFunction: this.chatStream.bind(this),
|
|
622
|
+
});
|
|
609
623
|
}
|
|
610
624
|
|
|
611
625
|
/**
|
|
@@ -621,7 +635,7 @@ class AgentChatHandler extends EventEmitter {
|
|
|
621
635
|
if (!this._aiClient) {
|
|
622
636
|
throw new Error('AI client not configured.');
|
|
623
637
|
}
|
|
624
|
-
const systemPrompt = framework.
|
|
638
|
+
const systemPrompt = framework.getSystemPrompt();
|
|
625
639
|
const tools = this._getAITools(aiTool);
|
|
626
640
|
|
|
627
641
|
// DeepSeek thinking mode: 处理参数
|
|
@@ -688,7 +702,11 @@ class AgentChatHandler extends EventEmitter {
|
|
|
688
702
|
};
|
|
689
703
|
} finally {
|
|
690
704
|
const messageStore = this._getSessionMessageStore(sessionId);
|
|
691
|
-
|
|
705
|
+
try {
|
|
706
|
+
await messageStore.save();
|
|
707
|
+
} catch (err) {
|
|
708
|
+
logger.error(`[${sessionId}] Failed to save message store: ${err.message}`);
|
|
709
|
+
}
|
|
692
710
|
}
|
|
693
711
|
}
|
|
694
712
|
|
|
@@ -799,11 +817,25 @@ class AgentChatHandler extends EventEmitter {
|
|
|
799
817
|
}
|
|
800
818
|
|
|
801
819
|
/**
|
|
802
|
-
*
|
|
820
|
+
* 计算工具列表缓存 key
|
|
821
|
+
* @private
|
|
822
|
+
*/
|
|
823
|
+
_computeToolsCacheKey() {
|
|
824
|
+
const allTools = this.agent.framework.getTools();
|
|
825
|
+
return allTools.map((t) => `${t.name}:${t.description ? t.description.length : 0}`).join('|');
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* 获取 AI 工具格式(带缓存)
|
|
803
830
|
* 使用 AI SDK 的 tool() 格式
|
|
804
831
|
* @private
|
|
805
832
|
*/
|
|
806
833
|
_getAITools(toolFn) {
|
|
834
|
+
const currentKey = this._computeToolsCacheKey();
|
|
835
|
+
if (this._aiToolsCache && this._aiToolsCacheKey === currentKey) {
|
|
836
|
+
return this._aiToolsCache;
|
|
837
|
+
}
|
|
838
|
+
|
|
807
839
|
const tools = {};
|
|
808
840
|
const allTools = this.agent.framework.getTools();
|
|
809
841
|
for (const toolDef of allTools) {
|
|
@@ -822,19 +854,29 @@ class AgentChatHandler extends EventEmitter {
|
|
|
822
854
|
logger.info(`[Tool] Call: ${toolName}`);
|
|
823
855
|
try {
|
|
824
856
|
const result = await toolDef.execute(cleanedArgs, this.agent.framework);
|
|
825
|
-
|
|
826
|
-
//
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
857
|
+
|
|
858
|
+
// 自动检测大工具结果,透明分拆
|
|
859
|
+
// 仅在数据分拆插件已加载时生效
|
|
860
|
+
let finalResult = result;
|
|
861
|
+
try {
|
|
862
|
+
const splitCheck = await autoSplitToolResult(toolName, result, this.agent.framework);
|
|
863
|
+
if (splitCheck.wasSplit && splitCheck.result) {
|
|
864
|
+
finalResult = splitCheck.result;
|
|
865
|
+
logger.info(
|
|
866
|
+
`[AutoSplit] 工具 "${toolName}" 结果过大,已自动分拆处理`
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
} catch (splitErr) {
|
|
870
|
+
// 分拆失败不阻断主流程,继续使用原始结果
|
|
871
|
+
logger.warn(`[AutoSplit] 自动分拆跳过: ${splitErr.message}`);
|
|
832
872
|
}
|
|
833
|
-
|
|
873
|
+
|
|
874
|
+
this.emit('tool-result', { name: toolName, args: cleanedArgs, result: finalResult });
|
|
875
|
+
return finalResult;
|
|
834
876
|
} catch (err) {
|
|
835
877
|
this.emit('tool-error', { name: toolName, args: cleanedArgs, error: err.message });
|
|
836
878
|
// 返回错误信息字符串,而不是抛出异常
|
|
837
|
-
return
|
|
879
|
+
return { success: false,error: err.message}
|
|
838
880
|
}
|
|
839
881
|
},
|
|
840
882
|
};
|
|
@@ -850,6 +892,8 @@ class AgentChatHandler extends EventEmitter {
|
|
|
850
892
|
tools[toolName] = toolFn(toolConfig);
|
|
851
893
|
}
|
|
852
894
|
|
|
895
|
+
this._aiToolsCache = tools;
|
|
896
|
+
this._aiToolsCacheKey = currentKey;
|
|
853
897
|
return tools;
|
|
854
898
|
}
|
|
855
899
|
|
package/src/core/agent.js
CHANGED
|
@@ -9,39 +9,21 @@ const { SystemPromptBuilder } = require('./system-prompt-builder');
|
|
|
9
9
|
const { NotificationManager } = require('./notification-manager');
|
|
10
10
|
const { Logger, LOG_LEVELS } = require('../utils/logger');
|
|
11
11
|
const os = require('os');
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const PROMPT_PRIORITY = {
|
|
18
|
-
DATETIME: 50,
|
|
19
|
-
ORIGINAL_PROMPT: 100,
|
|
20
|
-
SHARED_PROMPT: 200,
|
|
21
|
-
METADATA: 300,
|
|
22
|
-
TOOLS: 400,
|
|
23
|
-
SKILLS: 500,
|
|
24
|
-
SUB_AGENTS: 600,
|
|
25
|
-
CAPABILITIES: 700,
|
|
26
|
-
MCP_TOOLS: 750,
|
|
27
|
-
EXTENSION_TOOLS: 800,
|
|
28
|
-
TOOL_CORE_RULES: 1000,
|
|
29
|
-
};
|
|
12
|
+
const {
|
|
13
|
+
PROMPT_PRIORITY,
|
|
14
|
+
DEFAULT_MAX_OUTPUT_TOKENS,
|
|
15
|
+
DEFAULT_TEMPERATURE,
|
|
16
|
+
} = require('./constants');
|
|
30
17
|
|
|
31
18
|
/**
|
|
32
19
|
* Agent 配置常量
|
|
33
|
-
* 统一管理配置参数,避免魔法数字
|
|
34
20
|
*/
|
|
35
21
|
const AGENT_CONFIG = {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
TEMPERATURE: 0.3,
|
|
39
|
-
// 通知数量限制
|
|
22
|
+
MAX_OUTPUT_TOKENS: DEFAULT_MAX_OUTPUT_TOKENS,
|
|
23
|
+
TEMPERATURE: DEFAULT_TEMPERATURE,
|
|
40
24
|
MAX_NOTIFICATIONS: 5,
|
|
41
|
-
// 元数据提取配置
|
|
42
25
|
CAPABILITY_MAX_LENGTH: 50,
|
|
43
26
|
CAPABILITY_MAX_PARTS: 3,
|
|
44
|
-
// 错误重试配置
|
|
45
27
|
MAX_RETRIES: 3,
|
|
46
28
|
};
|
|
47
29
|
|
|
@@ -77,8 +59,8 @@ class Agent extends EventEmitter {
|
|
|
77
59
|
this.provider = config.provider || 'deepseek';
|
|
78
60
|
this.providerOptions = {
|
|
79
61
|
...config.providerOptions,
|
|
80
|
-
maxOutputTokens: config.providerOptions?.maxOutputTokens ??
|
|
81
|
-
temperature: config.providerOptions?.temperature ??
|
|
62
|
+
maxOutputTokens: config.providerOptions?.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS,
|
|
63
|
+
temperature: config.providerOptions?.temperature ?? DEFAULT_TEMPERATURE,
|
|
82
64
|
};
|
|
83
65
|
// 原始 system prompt
|
|
84
66
|
this._originalPrompt =
|
|
@@ -439,6 +421,14 @@ class Agent extends EventEmitter {
|
|
|
439
421
|
this._syncTools();
|
|
440
422
|
}
|
|
441
423
|
|
|
424
|
+
/**
|
|
425
|
+
* 获取原始系统提示文本
|
|
426
|
+
* @returns {string}
|
|
427
|
+
*/
|
|
428
|
+
getOriginalPrompt() {
|
|
429
|
+
return this._originalPrompt;
|
|
430
|
+
}
|
|
431
|
+
|
|
442
432
|
/**
|
|
443
433
|
* 设置系统提示
|
|
444
434
|
*/
|
package/src/core/chat-session.js
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
const { EventEmitter } = require('../utils/event-emitter');
|
|
12
|
-
const { ChatQueueManager } = require('../utils/chat-queue');
|
|
13
12
|
const { logger } = require('../utils/logger');
|
|
14
13
|
|
|
15
14
|
/**
|
|
@@ -113,21 +112,8 @@ class ChatSession extends EventEmitter {
|
|
|
113
112
|
// Session 消息存储 Map: sessionId -> { messages: [], historyLoaded: false, compressionState: {} }
|
|
114
113
|
this._sessionMessageStores = new Map();
|
|
115
114
|
|
|
116
|
-
// Session 队列: sessionId -> Queue of {message, options, resolve, reject}
|
|
117
|
-
this._sessionQueues = new Map();
|
|
118
|
-
this._processingSessions = new Set();
|
|
119
|
-
|
|
120
115
|
// Session 事件作用域 Map: sessionId -> Set<{event, handler}>
|
|
121
116
|
this._sessionScopes = new Map();
|
|
122
|
-
|
|
123
|
-
// 队列管理器
|
|
124
|
-
this.queueManager = new ChatQueueManager({
|
|
125
|
-
maxConcurrent: config.maxConcurrent || 1,
|
|
126
|
-
retryAttempts: config.retryAttempts || 3,
|
|
127
|
-
retryDelay: config.retryDelay || 1000,
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
this._setupQueueEvents();
|
|
131
117
|
}
|
|
132
118
|
|
|
133
119
|
/**
|
|
@@ -138,29 +124,7 @@ class ChatSession extends EventEmitter {
|
|
|
138
124
|
this._messageProcessor = processor;
|
|
139
125
|
}
|
|
140
126
|
|
|
141
|
-
/**
|
|
142
|
-
* 设置队列事件转发
|
|
143
|
-
* @private
|
|
144
|
-
*/
|
|
145
|
-
_setupQueueEvents() {
|
|
146
|
-
const events = [
|
|
147
|
-
'queue:added',
|
|
148
|
-
'queue:processing',
|
|
149
|
-
'queue:completed',
|
|
150
|
-
'queue:failed',
|
|
151
|
-
'queue:retry',
|
|
152
|
-
'queue:empty',
|
|
153
|
-
'queue:cleared',
|
|
154
|
-
'queue:session-removed',
|
|
155
|
-
'stream:chunk',
|
|
156
|
-
];
|
|
157
127
|
|
|
158
|
-
events.forEach((eventName) => {
|
|
159
|
-
this.queueManager.on(eventName, (data) => {
|
|
160
|
-
this.emit(eventName, data);
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
128
|
|
|
165
129
|
/**
|
|
166
130
|
* 获取或创建 SessionScope
|
|
@@ -219,6 +183,7 @@ class ChatSession extends EventEmitter {
|
|
|
219
183
|
},
|
|
220
184
|
save: () => {
|
|
221
185
|
this.saveHistory(sessionId, store.messages);
|
|
186
|
+
return Promise.resolve();
|
|
222
187
|
},
|
|
223
188
|
};
|
|
224
189
|
this._sessionMessageStores.set(sessionId, store);
|
|
@@ -310,123 +275,24 @@ class ChatSession extends EventEmitter {
|
|
|
310
275
|
}
|
|
311
276
|
|
|
312
277
|
/**
|
|
313
|
-
*
|
|
278
|
+
* 入队消息(直接调用消息处理器,排队由上层 ChatQueueManager 处理)
|
|
314
279
|
* @param {string} sessionId - Session ID
|
|
315
280
|
* @param {Object} message - 消息
|
|
316
281
|
* @param {Object} options - 选项
|
|
317
282
|
* @returns {Promise}
|
|
318
283
|
*/
|
|
319
284
|
enqueue(sessionId, message, options = {}) {
|
|
320
|
-
|
|
321
|
-
this._sessionQueues.set(sessionId, []);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return new Promise((resolve, reject) => {
|
|
325
|
-
const item = {
|
|
326
|
-
message,
|
|
327
|
-
options,
|
|
328
|
-
resolve,
|
|
329
|
-
reject,
|
|
330
|
-
timestamp: Date.now(),
|
|
331
|
-
executeFunction: options.executeFunction,
|
|
332
|
-
};
|
|
333
|
-
this._sessionQueues.get(sessionId).push(item);
|
|
334
|
-
this.emit('queue:added', {
|
|
335
|
-
sessionId,
|
|
336
|
-
message,
|
|
337
|
-
queueSize: this._sessionQueues.get(sessionId).length,
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
// 触发队列处理
|
|
341
|
-
this._processNext(sessionId);
|
|
342
|
-
});
|
|
285
|
+
return this._processMessageDirect(sessionId, message, options);
|
|
343
286
|
}
|
|
344
287
|
|
|
345
288
|
/**
|
|
346
|
-
*
|
|
347
|
-
* @param {string} sessionId - Session ID
|
|
289
|
+
* 直接处理消息(不经过额外的队列)
|
|
348
290
|
* @private
|
|
349
291
|
*/
|
|
350
|
-
async
|
|
351
|
-
if (this._processingSessions.has(sessionId)) {
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
const queue = this._sessionQueues.get(sessionId);
|
|
356
|
-
if (!queue || queue.length === 0) {
|
|
357
|
-
this.emit('queue:empty', { sessionId });
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
this._processingSessions.add(sessionId);
|
|
362
|
-
this.emit('queue:processing', { sessionId });
|
|
363
|
-
|
|
364
|
-
const item = queue.shift();
|
|
365
|
-
try {
|
|
366
|
-
// 如果有 executeFunction,使用流式处理
|
|
367
|
-
if (item.executeFunction) {
|
|
368
|
-
const chunks = [];
|
|
369
|
-
const stream = item.executeFunction(item.message, { ...item.options, sessionId });
|
|
370
|
-
let hasError = false;
|
|
371
|
-
let errorMessage = '';
|
|
372
|
-
|
|
373
|
-
for await (const chunk of stream) {
|
|
374
|
-
// 检查是否是错误 chunk,如果是立即拒绝并终止
|
|
375
|
-
if (chunk.type === 'error') {
|
|
376
|
-
hasError = true;
|
|
377
|
-
errorMessage = chunk.error || 'Unknown error';
|
|
378
|
-
this.emit('message:error', { sessionId, error: errorMessage });
|
|
379
|
-
item.reject(new Error(errorMessage));
|
|
380
|
-
return; // 立即返回,不继续迭代
|
|
381
|
-
}
|
|
382
|
-
chunks.push(chunk);
|
|
383
|
-
this.emit('stream:chunk', {
|
|
384
|
-
sessionId,
|
|
385
|
-
chunk,
|
|
386
|
-
accumulated: chunks.length,
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
if (hasError) {
|
|
391
|
-
// 有错误 chunk,拒绝 Promise
|
|
392
|
-
this.emit('message:error', { sessionId, error: errorMessage });
|
|
393
|
-
item.reject(new Error(errorMessage));
|
|
394
|
-
} else {
|
|
395
|
-
const fullText = chunks
|
|
396
|
-
.filter((a) => a.type === 'text')
|
|
397
|
-
.map((item) => item.text)
|
|
398
|
-
.join('');
|
|
399
|
-
|
|
400
|
-
item.resolve({ text: fullText, chunks });
|
|
401
|
-
}
|
|
402
|
-
} else {
|
|
403
|
-
const result = await this._processMessage(sessionId, item.message, item.options);
|
|
404
|
-
item.resolve(result);
|
|
405
|
-
}
|
|
406
|
-
} catch (err) {
|
|
407
|
-
this.emit('message:error', { sessionId, error: err.message });
|
|
408
|
-
item.reject(err);
|
|
409
|
-
} finally {
|
|
410
|
-
this._processingSessions.delete(sessionId);
|
|
411
|
-
// 处理下一条
|
|
412
|
-
setImmediate(() => this._processNext(sessionId));
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* 处理单条消息(子类实现)
|
|
418
|
-
* @param {string} sessionId - Session ID
|
|
419
|
-
* @param {Object} message - 消息
|
|
420
|
-
* @param {Object} options - 选项
|
|
421
|
-
* @returns {Promise}
|
|
422
|
-
* @protected
|
|
423
|
-
*/
|
|
424
|
-
async _processMessage(sessionId, message, options) {
|
|
425
|
-
// 如果设置了处理器,使用处理器
|
|
292
|
+
async _processMessageDirect(sessionId, message, options) {
|
|
426
293
|
if (this._messageProcessor) {
|
|
427
294
|
return this._messageProcessor(sessionId, message, options);
|
|
428
295
|
}
|
|
429
|
-
// 否则使用 agent 的处理逻辑
|
|
430
296
|
if (this.agent?._chatHandler) {
|
|
431
297
|
return this.agent._chatHandler._processMessage(sessionId, message, options);
|
|
432
298
|
}
|
|
@@ -434,18 +300,10 @@ class ChatSession extends EventEmitter {
|
|
|
434
300
|
}
|
|
435
301
|
|
|
436
302
|
/**
|
|
437
|
-
*
|
|
303
|
+
* 取消会话队列(委托给 ChatQueueManager)
|
|
438
304
|
* @param {string} sessionId - Session ID
|
|
439
305
|
*/
|
|
440
306
|
cancelSession(sessionId) {
|
|
441
|
-
const queue = this._sessionQueues.get(sessionId);
|
|
442
|
-
if (queue) {
|
|
443
|
-
queue.forEach((item) => {
|
|
444
|
-
item.reject(new Error('Session cancelled'));
|
|
445
|
-
});
|
|
446
|
-
queue.length = 0;
|
|
447
|
-
}
|
|
448
|
-
this._processingSessions.delete(sessionId);
|
|
449
307
|
this.emit('queue:session-removed', { sessionId });
|
|
450
308
|
}
|
|
451
309
|
|
|
@@ -455,12 +313,7 @@ class ChatSession extends EventEmitter {
|
|
|
455
313
|
* @returns {Object}
|
|
456
314
|
*/
|
|
457
315
|
getQueueStatus(sessionId) {
|
|
458
|
-
|
|
459
|
-
return {
|
|
460
|
-
sessionId,
|
|
461
|
-
size: queue.length,
|
|
462
|
-
processing: this._processingSessions.has(sessionId),
|
|
463
|
-
};
|
|
316
|
+
return { sessionId, size: 0, processing: false };
|
|
464
317
|
}
|
|
465
318
|
|
|
466
319
|
/**
|
|
@@ -468,13 +321,6 @@ class ChatSession extends EventEmitter {
|
|
|
468
321
|
* @param {string} sessionId - Session ID
|
|
469
322
|
*/
|
|
470
323
|
clearQueue(sessionId) {
|
|
471
|
-
const queue = this._sessionQueues.get(sessionId);
|
|
472
|
-
if (queue) {
|
|
473
|
-
queue.forEach((item) => {
|
|
474
|
-
item.reject(new Error('Queue cleared'));
|
|
475
|
-
});
|
|
476
|
-
queue.length = 0;
|
|
477
|
-
}
|
|
478
324
|
this.emit('queue:cleared', { sessionId });
|
|
479
325
|
}
|
|
480
326
|
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Foliko Framework 统一常量
|
|
3
|
+
*
|
|
4
|
+
* 集中管理所有 magic numbers / strings,消除散落各处的重复值。
|
|
5
|
+
* 所有模块应引用此文件而非硬编码。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// AI 模型与提供者
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
/** 默认 AI 提供者 */
|
|
15
|
+
const DEFAULT_PROVIDER = 'deepseek';
|
|
16
|
+
|
|
17
|
+
/** 默认 AI 模型 */
|
|
18
|
+
const DEFAULT_MODEL = 'deepseek-chat';
|
|
19
|
+
|
|
20
|
+
/** 默认 Max Output Tokens */
|
|
21
|
+
const DEFAULT_MAX_OUTPUT_TOKENS = 8192;
|
|
22
|
+
|
|
23
|
+
/** 默认 Temperature */
|
|
24
|
+
const DEFAULT_TEMPERATURE = 0.3;
|
|
25
|
+
|
|
26
|
+
/** 默认 Max Steps(工具调用最大轮数) */
|
|
27
|
+
const DEFAULT_MAX_STEPS = 20;
|
|
28
|
+
|
|
29
|
+
/** 需要禁用 temperature 的 thinking mode 模型列表 */
|
|
30
|
+
const THINKING_MODELS = [
|
|
31
|
+
'deepseek-v4-pro',
|
|
32
|
+
'deepseek-v4-flash',
|
|
33
|
+
'deepseek-reasoner',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Session 上下文
|
|
38
|
+
// ============================================================================
|
|
39
|
+
|
|
40
|
+
/** Session TTL:默认 30 分钟 */
|
|
41
|
+
const DEFAULT_SESSION_TTL_MS = 30 * 60 * 1000;
|
|
42
|
+
|
|
43
|
+
/** Session 清理检查间隔 */
|
|
44
|
+
const DEFAULT_SESSION_CLEANUP_INTERVAL_MS = 60 * 1000;
|
|
45
|
+
|
|
46
|
+
/** 每个 Session 最大消息数 */
|
|
47
|
+
const DEFAULT_MAX_MESSAGES_PER_SESSION = 1000;
|
|
48
|
+
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// 上下文压缩
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
/** 保留最近的 N 条消息 */
|
|
54
|
+
const KEEP_RECENT_MESSAGES = 20;
|
|
55
|
+
|
|
56
|
+
/** 智能压缩启用 */
|
|
57
|
+
const ENABLE_SMART_COMPRESS = true;
|
|
58
|
+
|
|
59
|
+
/** 压缩超时(毫秒) */
|
|
60
|
+
const COMPRESSION_TIMEOUT_MS = 1_200_000;
|
|
61
|
+
|
|
62
|
+
/** 工具结果最大字符数 */
|
|
63
|
+
const MAX_TOOL_RESULT_SIZE = 4000;
|
|
64
|
+
|
|
65
|
+
// ============================================================================
|
|
66
|
+
// 消息队列
|
|
67
|
+
// ============================================================================
|
|
68
|
+
|
|
69
|
+
/** 默认最大并发数 */
|
|
70
|
+
const DEFAULT_MAX_CONCURRENT = 1;
|
|
71
|
+
|
|
72
|
+
/** 默认重试次数 */
|
|
73
|
+
const DEFAULT_RETRY_ATTEMPTS = 3;
|
|
74
|
+
|
|
75
|
+
/** 默认重试延迟(毫秒) */
|
|
76
|
+
const DEFAULT_RETRY_DELAY_MS = 2000;
|
|
77
|
+
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// System Prompt 优先级
|
|
80
|
+
// ============================================================================
|
|
81
|
+
|
|
82
|
+
const PROMPT_PRIORITY = {
|
|
83
|
+
DATETIME: 50,
|
|
84
|
+
ORIGINAL_PROMPT: 100,
|
|
85
|
+
SHARED_PROMPT: 200,
|
|
86
|
+
METADATA: 300,
|
|
87
|
+
TOOLS: 400,
|
|
88
|
+
SKILLS: 500,
|
|
89
|
+
SUB_AGENTS: 600,
|
|
90
|
+
CAPABILITIES: 700,
|
|
91
|
+
MCP_TOOLS: 750,
|
|
92
|
+
EXTENSION_TOOLS: 800,
|
|
93
|
+
TOOL_CORE_RULES: 95,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// 熔断器
|
|
98
|
+
// ============================================================================
|
|
99
|
+
|
|
100
|
+
/** 熔断器默认失败阈值 */
|
|
101
|
+
const DEFAULT_CIRCUIT_FAILURE_THRESHOLD = 3;
|
|
102
|
+
|
|
103
|
+
/** 熔断器默认成功阈值 */
|
|
104
|
+
const DEFAULT_CIRCUIT_SUCCESS_THRESHOLD = 2;
|
|
105
|
+
|
|
106
|
+
/** 熔断器默认超时(毫秒) */
|
|
107
|
+
const DEFAULT_CIRCUIT_TIMEOUT_MS = 60_000;
|
|
108
|
+
|
|
109
|
+
// ============================================================================
|
|
110
|
+
// 插件
|
|
111
|
+
// ============================================================================
|
|
112
|
+
|
|
113
|
+
/** 默认插件优先级 */
|
|
114
|
+
const DEFAULT_PLUGIN_PRIORITY = 100;
|
|
115
|
+
|
|
116
|
+
/** 系统插件名称集合 */
|
|
117
|
+
const SYSTEM_PLUGINS = new Set([
|
|
118
|
+
'ai', 'defaults', 'tools', 'skill-manager', 'session',
|
|
119
|
+
'storage', 'audit', 'rules', 'file-system',
|
|
120
|
+
]);
|
|
121
|
+
|
|
122
|
+
/** Agent 配置目录名 */
|
|
123
|
+
const AGENT_DIR = '.agent';
|
|
124
|
+
|
|
125
|
+
// ============================================================================
|
|
126
|
+
// Session 存储
|
|
127
|
+
// ============================================================================
|
|
128
|
+
|
|
129
|
+
/** 默认 Session 存储类型 */
|
|
130
|
+
const DEFAULT_SESSION_STORAGE_TYPE = 'file';
|
|
131
|
+
|
|
132
|
+
/** 默认 Session 存储目录 */
|
|
133
|
+
const DEFAULT_SESSION_STORAGE_DIR = path.join(AGENT_DIR, 'sessions');
|
|
134
|
+
|
|
135
|
+
// ============================================================================
|
|
136
|
+
// Token 计数(估算系数)
|
|
137
|
+
// ============================================================================
|
|
138
|
+
|
|
139
|
+
/** 每字符对应的 token 数(中文系数更大) */
|
|
140
|
+
const TOKENS_PER_CHAR = 0.25;
|
|
141
|
+
|
|
142
|
+
/** 每工具对应的 token 数 */
|
|
143
|
+
const TOKENS_PER_TOOL = 500;
|
|
144
|
+
|
|
145
|
+
/** 每消息固定开销 token */
|
|
146
|
+
const TOKENS_PER_MESSAGE_OVERHEAD = 4;
|
|
147
|
+
|
|
148
|
+
// ============================================================================
|
|
149
|
+
// 导出
|
|
150
|
+
// ============================================================================
|
|
151
|
+
|
|
152
|
+
module.exports = {
|
|
153
|
+
// AI
|
|
154
|
+
DEFAULT_PROVIDER,
|
|
155
|
+
DEFAULT_MODEL,
|
|
156
|
+
DEFAULT_MAX_OUTPUT_TOKENS,
|
|
157
|
+
DEFAULT_TEMPERATURE,
|
|
158
|
+
DEFAULT_MAX_STEPS,
|
|
159
|
+
THINKING_MODELS,
|
|
160
|
+
|
|
161
|
+
// Session
|
|
162
|
+
DEFAULT_SESSION_TTL_MS,
|
|
163
|
+
DEFAULT_SESSION_CLEANUP_INTERVAL_MS,
|
|
164
|
+
DEFAULT_MAX_MESSAGES_PER_SESSION,
|
|
165
|
+
|
|
166
|
+
// Compression
|
|
167
|
+
KEEP_RECENT_MESSAGES,
|
|
168
|
+
ENABLE_SMART_COMPRESS,
|
|
169
|
+
COMPRESSION_TIMEOUT_MS,
|
|
170
|
+
MAX_TOOL_RESULT_SIZE,
|
|
171
|
+
|
|
172
|
+
// Queue
|
|
173
|
+
DEFAULT_MAX_CONCURRENT,
|
|
174
|
+
DEFAULT_RETRY_ATTEMPTS,
|
|
175
|
+
DEFAULT_RETRY_DELAY_MS,
|
|
176
|
+
|
|
177
|
+
// Priority
|
|
178
|
+
PROMPT_PRIORITY,
|
|
179
|
+
|
|
180
|
+
// Circuit Breaker
|
|
181
|
+
DEFAULT_CIRCUIT_FAILURE_THRESHOLD,
|
|
182
|
+
DEFAULT_CIRCUIT_SUCCESS_THRESHOLD,
|
|
183
|
+
DEFAULT_CIRCUIT_TIMEOUT_MS,
|
|
184
|
+
|
|
185
|
+
// Plugin
|
|
186
|
+
DEFAULT_PLUGIN_PRIORITY,
|
|
187
|
+
SYSTEM_PLUGINS,
|
|
188
|
+
AGENT_DIR,
|
|
189
|
+
|
|
190
|
+
// Storage
|
|
191
|
+
DEFAULT_SESSION_STORAGE_TYPE,
|
|
192
|
+
DEFAULT_SESSION_STORAGE_DIR,
|
|
193
|
+
|
|
194
|
+
// Token
|
|
195
|
+
TOKENS_PER_CHAR,
|
|
196
|
+
TOKENS_PER_TOOL,
|
|
197
|
+
TOKENS_PER_MESSAGE_OVERHEAD,
|
|
198
|
+
};
|