foliko 1.1.92 → 2.0.0
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 +202 -0
- package/plugins/core/default/config.js +220 -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 +2 -2
- package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +3 -3
- package/plugins/{qq-plugin.js → messaging/qq/index.js} +5 -16
- package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +3 -3
- package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +15 -15
- 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/{core → common}/constants.js +3 -0
- 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 +261 -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/{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
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WorkerAgent - 独立 Worker Agent
|
|
5
|
+
* 用于 Coordinator 模式下的 Worker,基于 SubAgent 轻量级实现
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { BaseAgent } = require('./base');
|
|
9
|
+
|
|
10
|
+
class WorkerAgent extends BaseAgent {
|
|
11
|
+
constructor(framework, config) {
|
|
12
|
+
super(framework, config);
|
|
13
|
+
|
|
14
|
+
this.workerId = config.workerId;
|
|
15
|
+
this.coordinatorId = config.coordinatorId;
|
|
16
|
+
this._task = config.task || '';
|
|
17
|
+
|
|
18
|
+
const aiPlugin = framework.pluginManager?.get('ai');
|
|
19
|
+
const aiConfig = aiPlugin ? aiPlugin.getConfig() : {};
|
|
20
|
+
const mergedAIConfig = { ...aiConfig, ...config.llmConfig };
|
|
21
|
+
|
|
22
|
+
const toolList = [];
|
|
23
|
+
if (config.tools) {
|
|
24
|
+
const allTools = framework.getTools();
|
|
25
|
+
const allowedTools = new Set(config.tools);
|
|
26
|
+
for (const toolName of allowedTools) {
|
|
27
|
+
const toolDef = allTools.find(t => t.name === toolName);
|
|
28
|
+
if (toolDef) toolList.push(toolDef);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const { SubAgent } = require('./sub');
|
|
33
|
+
this._subagent = new SubAgent({
|
|
34
|
+
name: config.name || `worker_${this.workerId}`,
|
|
35
|
+
role: config.role || 'Worker',
|
|
36
|
+
description: config.description || '',
|
|
37
|
+
systemPrompt: config.systemPrompt || WorkerAgent.defaultPrompt(),
|
|
38
|
+
model: config.model || mergedAIConfig.model || 'deepseek-chat',
|
|
39
|
+
provider: config.provider || mergedAIConfig.provider || 'deepseek',
|
|
40
|
+
apiKey: config.apiKey || mergedAIConfig.apiKey,
|
|
41
|
+
baseURL: config.baseURL || mergedAIConfig.baseURL,
|
|
42
|
+
tools: toolList,
|
|
43
|
+
framework: framework,
|
|
44
|
+
maxRetries: config.maxRetries ?? 2,
|
|
45
|
+
disableTools: config.disableTools ?? false,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
this._subagent.on('chunk', chunk => this.emit('chunk', chunk));
|
|
49
|
+
this._subagent.on('complete', data => { this._status = 'completed'; this.emit('complete', data); });
|
|
50
|
+
this._subagent.on('error', data => { this._status = 'error'; this.emit('error', data); });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getStatus() { return this._status; }
|
|
54
|
+
setStatus(status) { this._status = status; }
|
|
55
|
+
getSubagent() { return this._subagent; }
|
|
56
|
+
|
|
57
|
+
async runTask(task, options) {
|
|
58
|
+
this._task = task;
|
|
59
|
+
this._status = 'running';
|
|
60
|
+
try {
|
|
61
|
+
const result = await this._subagent.chat(task, options);
|
|
62
|
+
if (!result.success) this._status = 'error';
|
|
63
|
+
return result;
|
|
64
|
+
} catch (err) {
|
|
65
|
+
this._status = 'error';
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async sendMessage(message, options) {
|
|
71
|
+
if (this._status === 'stopped') throw new Error(`Worker ${this.workerId} has been stopped`);
|
|
72
|
+
this._status = 'running';
|
|
73
|
+
try { return await this._subagent.chat(message, options); }
|
|
74
|
+
catch (err) { this._status = 'error'; throw err; }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
stop() {
|
|
78
|
+
this._status = 'stopped';
|
|
79
|
+
this._subagent.destroy();
|
|
80
|
+
this.emit('stopped', { workerId: this.workerId });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static formatNotification(workerId, status, result, error = null) {
|
|
84
|
+
const notification = { worker_id: workerId, status, timestamp: new Date().toISOString() };
|
|
85
|
+
if (error) notification.error = error;
|
|
86
|
+
else if (result) notification.result = result;
|
|
87
|
+
return JSON.stringify(notification);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static defaultPrompt() {
|
|
91
|
+
return `你是一个独立的Worker Agent。
|
|
92
|
+
你的职责:
|
|
93
|
+
1. 接收并理解Coordinator分配的任务
|
|
94
|
+
2. 使用你的工具集完成任务
|
|
95
|
+
3. 返回完整的任务结果
|
|
96
|
+
|
|
97
|
+
重要规则:
|
|
98
|
+
- 任务完成或出错时,在响应末尾添加JSON格式的<task-notification>说明状态
|
|
99
|
+
- 不要主动联系Coordinator,由Coordinator主动查询结果
|
|
100
|
+
- 如果需要更多信息,等待Coordinator发送continue消息`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = { WorkerAgent };
|
|
@@ -4,11 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const foliko = require('../../../src');
|
|
8
8
|
const { ChatUI } = require('../ui/chat-ui');
|
|
9
|
-
const {
|
|
10
|
-
const {
|
|
11
|
-
const { DEFAULT_PROVIDERS } = require('../../../src/core/provider');
|
|
9
|
+
const { logger, LOG_LEVELS } = require('../../../src/common/logger');
|
|
10
|
+
const { DEFAULT_PROVIDERS } = foliko.LLM;
|
|
12
11
|
|
|
13
12
|
// // 加载 .env 文件
|
|
14
13
|
// dotenv.config({ override: true,quiet: true});
|
|
@@ -80,7 +79,7 @@ function parseArgs(args) {
|
|
|
80
79
|
} else if (arg === '--no-quiet') {
|
|
81
80
|
options.quiet = false;
|
|
82
81
|
} else if (arg === '--old') {
|
|
83
|
-
options.
|
|
82
|
+
options.oldUI = true;
|
|
84
83
|
}
|
|
85
84
|
}
|
|
86
85
|
|
|
@@ -151,21 +150,20 @@ async function chatCommand(args) {
|
|
|
151
150
|
// console.log('输入 exit 或 quit 退出\n');
|
|
152
151
|
// TUI 模式下抑制日志输出(静默 console.log 但保持 logEmitter 事件)
|
|
153
152
|
|
|
154
|
-
// 初始化框架
|
|
155
|
-
const framework =
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
reasoning_split: true, // MiniMax API 要求的参数
|
|
153
|
+
// 初始化框架 — 使用新门面 API
|
|
154
|
+
const framework = await foliko.bootstrap({
|
|
155
|
+
_config: {
|
|
156
|
+
agentDir: process.cwd() + '/.foliko',
|
|
157
|
+
ai: {
|
|
158
|
+
provider: options.provider,
|
|
159
|
+
model: options.model,
|
|
160
|
+
baseURL: options.baseURL,
|
|
161
|
+
apiKey: options.apiKey,
|
|
162
|
+
providerOptions: {
|
|
163
|
+
maxOutputTokens: 8192,
|
|
164
|
+
minimax: {
|
|
165
|
+
reasoning_split: true,
|
|
166
|
+
},
|
|
169
167
|
},
|
|
170
168
|
},
|
|
171
169
|
},
|
|
@@ -203,7 +201,13 @@ async function chatCommand(args) {
|
|
|
203
201
|
|
|
204
202
|
|
|
205
203
|
// 初始化 UI
|
|
206
|
-
|
|
204
|
+
let UI;
|
|
205
|
+
if (options.oldUI) {
|
|
206
|
+
const { ChatUIOld } = require('../ui/chat-ui-old');
|
|
207
|
+
UI = ChatUIOld;
|
|
208
|
+
} else {
|
|
209
|
+
UI = ChatUI;
|
|
210
|
+
}
|
|
207
211
|
const ui = new UI(agent, { stream: options.stream });
|
|
208
212
|
|
|
209
213
|
// 监听通知事件,在 CLI 中显示
|
|
@@ -1,68 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* 使用 readline question()
|
|
2
|
+
* 旧版聊天界面组件
|
|
3
|
+
* 使用 readline question() 实现多行输入,轻量无 TUI 依赖
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const readline = require('readline');
|
|
7
7
|
const { CLEAR_LINE, CYAN, DIM, GREEN, RED, YELLOW, colored } = require('../utils/ansi');
|
|
8
8
|
const { renderLine } = require('../utils/markdown');
|
|
9
|
-
const {
|
|
10
|
-
const { logEmitter } = require('../../../src/utils/logger');
|
|
11
|
-
const Queue = require('js-queue');
|
|
9
|
+
const { logger } = require('../../../src/common/logger');
|
|
12
10
|
|
|
13
|
-
class
|
|
11
|
+
class ChatUIOld {
|
|
14
12
|
constructor(agent, options = {}) {
|
|
15
13
|
this.agent = agent;
|
|
16
14
|
this.rl = null;
|
|
17
|
-
this.lines = [];
|
|
15
|
+
this.lines = [];
|
|
18
16
|
|
|
19
|
-
// 流式模式(默认开启)
|
|
20
17
|
this.stream = options.stream !== false;
|
|
21
|
-
|
|
22
|
-
// 会话管理
|
|
23
18
|
this.sessionId = options.sessionId || 'cli_default';
|
|
24
19
|
this.sessionPlugin = null;
|
|
25
|
-
|
|
26
|
-
// 队列系统
|
|
27
|
-
this.queue = new Queue();
|
|
28
|
-
|
|
29
|
-
// 中断状态(供监听器访问)
|
|
30
20
|
this.interrupted = false;
|
|
31
|
-
|
|
32
|
-
// session scope 事件监听
|
|
33
21
|
this.sessionScope = null;
|
|
34
|
-
|
|
35
|
-
// 流式输出缓冲区(可重置)
|
|
36
22
|
this._lineBuffer = '';
|
|
37
23
|
|
|
38
|
-
// 基础命令
|
|
39
24
|
this.baseCommands = [
|
|
40
|
-
{ name:
|
|
41
|
-
{ name:
|
|
42
|
-
{ name:
|
|
43
|
-
{ name:
|
|
44
|
-
{ name:
|
|
25
|
+
{ name: 'compress', description: '压缩记录' },
|
|
26
|
+
{ name: 'clear', description: '清除记录' },
|
|
27
|
+
{ name: 'exit', description: '退出' },
|
|
28
|
+
{ name: 'help', description: '显示帮助信息' },
|
|
29
|
+
{ name: 'reload', description: '重载技能' },
|
|
45
30
|
];
|
|
46
31
|
|
|
47
|
-
// 如果 agent 有 framework,获取 SessionPlugin
|
|
48
32
|
if (agent.framework) {
|
|
49
33
|
this.sessionPlugin = agent.framework.pluginManager.get('session');
|
|
50
|
-
// 确保会话存在
|
|
51
34
|
if (this.sessionPlugin) {
|
|
52
35
|
this.sessionPlugin.getOrCreateSession(this.sessionId, {
|
|
53
36
|
metadata: { platform: 'cli' },
|
|
54
37
|
});
|
|
55
38
|
}
|
|
56
|
-
// 创建 session scope 并设置监听器(只需一次)
|
|
57
39
|
this.sessionScope = this.agent.createSessionScope(this.sessionId);
|
|
58
40
|
this._setupSessionListeners();
|
|
59
41
|
this._setupFrameworkListeners();
|
|
60
42
|
}
|
|
61
43
|
}
|
|
62
44
|
|
|
63
|
-
/**
|
|
64
|
-
* 清理监听器
|
|
65
|
-
*/
|
|
66
45
|
dispose() {
|
|
67
46
|
if (this._logHandler && this.agent.framework) {
|
|
68
47
|
logger.off('log', this._logHandler);
|
|
@@ -82,67 +61,46 @@ class ChatUI {
|
|
|
82
61
|
}
|
|
83
62
|
}
|
|
84
63
|
|
|
85
|
-
/**
|
|
86
|
-
* 设置 framework 事件监听(日志、通知、Usage、热重载)
|
|
87
|
-
*/
|
|
88
64
|
_setupFrameworkListeners() {
|
|
89
65
|
const levelDict = {
|
|
90
|
-
DEBUG: 'cyan',
|
|
91
|
-
|
|
92
|
-
LOG: 'dim',
|
|
93
|
-
WARN: 'yellow',
|
|
94
|
-
ERROR: 'red',
|
|
66
|
+
DEBUG: 'cyan', INFO: 'dim', LOG: 'dim',
|
|
67
|
+
WARN: 'yellow', ERROR: 'red',
|
|
95
68
|
};
|
|
96
69
|
|
|
97
|
-
// 日志监听
|
|
98
70
|
this._logHandler = (data) => {
|
|
99
71
|
if (!Object.keys(levelDict).includes(data.level)) return;
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
console.log(
|
|
72
|
+
const tag = colored(`[${data.level}]`, RED);
|
|
73
|
+
const msg = colored(data.message, levelDict[data.level] || 'dim');
|
|
74
|
+
console.log(tag + msg);
|
|
103
75
|
};
|
|
104
76
|
logger.on('log', this._logHandler);
|
|
105
77
|
|
|
106
|
-
// 通知监听
|
|
107
78
|
this._notificationHandler = (data) => {
|
|
108
79
|
if (data.sessionId && data.sessionId !== this.sessionId) return;
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
console.log(colored(
|
|
112
|
-
if (data.message) {
|
|
113
|
-
console.log(colored(data.message, DIM));
|
|
114
|
-
}
|
|
80
|
+
const t = data.timestamp ? new Date(data.timestamp).toLocaleTimeString('zh-CN') : '';
|
|
81
|
+
console.log(colored(`[通知] ${data.title}${t ? ` (${t})` : ''}`, CYAN));
|
|
82
|
+
if (data.message) console.log(colored(data.message, DIM));
|
|
115
83
|
};
|
|
116
84
|
this.agent.framework.on('notification', this._notificationHandler);
|
|
117
85
|
|
|
118
|
-
// Usage 监听
|
|
119
86
|
this._usageHandler = (data) => {
|
|
120
87
|
if (data.usage) {
|
|
121
88
|
const u = data.usage;
|
|
122
89
|
const input = u.promptTokens || u.prompt_tokens || 0;
|
|
123
90
|
const output = u.completionTokens || u.completion_tokens || 0;
|
|
124
|
-
// 更新 footerBar 如果存在
|
|
125
|
-
if (this.footerBar) {
|
|
126
|
-
this.footerBar.updateUsage(input, output);
|
|
127
|
-
}
|
|
128
91
|
}
|
|
129
92
|
};
|
|
130
93
|
this.agent.framework.on('agent:usage', this._usageHandler);
|
|
131
94
|
|
|
132
|
-
// Skill 热重载监听
|
|
133
95
|
this._skillReloadedHandler = () => {
|
|
134
96
|
console.log(colored('[提示] 技能已重载', CYAN));
|
|
135
97
|
};
|
|
136
98
|
this.agent.framework.on('skill:reloaded', this._skillReloadedHandler);
|
|
137
99
|
}
|
|
138
100
|
|
|
139
|
-
/**
|
|
140
|
-
* 统一命令执行入口
|
|
141
|
-
*/
|
|
142
101
|
async _executeCommand(trimmed) {
|
|
143
102
|
const cmd = trimmed.toLowerCase();
|
|
144
103
|
|
|
145
|
-
// 基础命令
|
|
146
104
|
if (cmd === '/clear' || cmd === '/clean') {
|
|
147
105
|
this._clearContext();
|
|
148
106
|
console.log(colored('[提示] 对话上下文已清除', CYAN));
|
|
@@ -167,7 +125,7 @@ class ChatUI {
|
|
|
167
125
|
return true;
|
|
168
126
|
}
|
|
169
127
|
|
|
170
|
-
//
|
|
128
|
+
// skill 命令 (格式: skillname:cmdname)
|
|
171
129
|
if (trimmed.startsWith('/') && trimmed.includes(':')) {
|
|
172
130
|
const cmdName = trimmed.slice(1).split(' ')[0];
|
|
173
131
|
const cmdArgs = trimmed.slice(cmdName.length + 2);
|
|
@@ -189,71 +147,25 @@ class ChatUI {
|
|
|
189
147
|
return true;
|
|
190
148
|
}
|
|
191
149
|
|
|
192
|
-
// CommandRegistry 命令
|
|
193
|
-
const { getCommandRegistry } = require('../../../src/core/command-registry');
|
|
194
|
-
const registry = getCommandRegistry();
|
|
195
|
-
const cmdName = trimmed.slice(1).split(' ')[0];
|
|
196
|
-
const cmdArgs = trimmed.slice(cmdName.length + 2);
|
|
197
|
-
if (trimmed.startsWith('/') && cmdName) {
|
|
198
|
-
const cmd = registry.getCommand(cmdName);
|
|
199
|
-
if (cmd) {
|
|
200
|
-
try {
|
|
201
|
-
const context = {
|
|
202
|
-
sessionId: this.sessionId,
|
|
203
|
-
agent: this.agent,
|
|
204
|
-
ui: this,
|
|
205
|
-
framework: this.agent.framework,
|
|
206
|
-
};
|
|
207
|
-
registry.execute(cmdName, cmdArgs, context).catch(err => {
|
|
208
|
-
console.log(colored(`[命令错误] ${err.message}`, RED));
|
|
209
|
-
});
|
|
210
|
-
} catch (err) {
|
|
211
|
-
console.log(colored(`[命令错误] ${err.message}`, RED));
|
|
212
|
-
}
|
|
213
|
-
return true;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
150
|
return false;
|
|
218
151
|
}
|
|
219
152
|
|
|
220
|
-
/**
|
|
221
|
-
* 显示帮助信息
|
|
222
|
-
*/
|
|
223
153
|
async _showHelp() {
|
|
224
|
-
const { getCommandRegistry } = require('../../../src/core/command-registry');
|
|
225
|
-
const registry = getCommandRegistry();
|
|
226
|
-
const commands = registry.getAllCommands();
|
|
227
|
-
|
|
228
154
|
console.log(colored('[帮助] 可用命令:', CYAN));
|
|
229
155
|
this.baseCommands.forEach(c => {
|
|
230
156
|
console.log(` ${colored('/' + c.name, CYAN)} - ${c.description}`);
|
|
231
157
|
});
|
|
232
158
|
|
|
233
|
-
// Skill 命令
|
|
234
159
|
const extExecutor = this.agent.framework?.pluginManager?.get('extension-executor');
|
|
235
160
|
if (extExecutor && typeof extExecutor.getSkillCommands === 'function') {
|
|
236
|
-
const
|
|
237
|
-
if (
|
|
161
|
+
const cmds = extExecutor.getSkillCommands();
|
|
162
|
+
if (cmds.length > 0) {
|
|
238
163
|
console.log(colored('\n[技能命令]', CYAN));
|
|
239
|
-
|
|
240
|
-
console.log(` ${colored('/' + cmd.name, CYAN)} - ${cmd.description || '无描述'}`);
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (commands.length > 0) {
|
|
246
|
-
console.log(colored('\n[扩展命令]', CYAN));
|
|
247
|
-
for (const cmd of commands) {
|
|
248
|
-
const pluginTag = cmd.registeredBy ? ` [${cmd.registeredBy}]` : '';
|
|
249
|
-
console.log(` ${colored('/' + cmd.name, CYAN)}${pluginTag} - ${cmd.description || '无描述'}`);
|
|
164
|
+
cmds.forEach(c => console.log(` ${colored('/' + c.name, CYAN)} - ${c.description || '无描述'}`));
|
|
250
165
|
}
|
|
251
166
|
}
|
|
252
167
|
}
|
|
253
168
|
|
|
254
|
-
/**
|
|
255
|
-
* 重载技能
|
|
256
|
-
*/
|
|
257
169
|
async _reloadSkills() {
|
|
258
170
|
try {
|
|
259
171
|
const result = await this.agent.framework.executeTool('reloadSkills', {});
|
|
@@ -267,27 +179,18 @@ class ChatUI {
|
|
|
267
179
|
}
|
|
268
180
|
}
|
|
269
181
|
|
|
270
|
-
/**
|
|
271
|
-
* 清除上下文
|
|
272
|
-
*/
|
|
273
182
|
_clearContext() {
|
|
274
|
-
|
|
275
|
-
this.agent.clearContext(sessionId);
|
|
276
|
-
|
|
183
|
+
this.agent.clearContext(this.sessionId);
|
|
277
184
|
if (this.sessionPlugin) {
|
|
278
|
-
const
|
|
279
|
-
if (
|
|
185
|
+
const mgr = this.sessionPlugin._getSessionManager(this.sessionId);
|
|
186
|
+
if (mgr) mgr.clearMessages();
|
|
280
187
|
}
|
|
281
188
|
}
|
|
282
189
|
|
|
283
|
-
/**
|
|
284
|
-
* 压缩上下文
|
|
285
|
-
*/
|
|
286
190
|
async _compressContext() {
|
|
287
191
|
console.log(colored('[压缩] 压缩中...', YELLOW));
|
|
288
192
|
try {
|
|
289
|
-
const
|
|
290
|
-
const result = await this.agent.compressContext(sessionId);
|
|
193
|
+
const result = await this.agent.compressContext(this.sessionId);
|
|
291
194
|
console.log(colored(`[压缩] 压缩后: ${result.after} 条 (保留${result.keepRecent}条)`, YELLOW));
|
|
292
195
|
} catch (err) {
|
|
293
196
|
const msg = err.message || String(err);
|
|
@@ -299,9 +202,6 @@ class ChatUI {
|
|
|
299
202
|
}
|
|
300
203
|
}
|
|
301
204
|
|
|
302
|
-
/**
|
|
303
|
-
* 设置 session scope 事件监听
|
|
304
|
-
*/
|
|
305
205
|
_setupSessionListeners() {
|
|
306
206
|
const renderState = { inThink: false, inCodeBlock: false };
|
|
307
207
|
|
|
@@ -310,10 +210,7 @@ class ChatUI {
|
|
|
310
210
|
|
|
311
211
|
if (chunk.type === 'text') {
|
|
312
212
|
this._lineBuffer += chunk.text;
|
|
313
|
-
|
|
314
213
|
while (this._lineBuffer.includes('\n')) {
|
|
315
|
-
if (this.interrupted) break;
|
|
316
|
-
|
|
317
214
|
const nlIndex = this._lineBuffer.indexOf('\n');
|
|
318
215
|
const line = this._lineBuffer.substring(0, nlIndex);
|
|
319
216
|
this._lineBuffer = this._lineBuffer.substring(nlIndex + 1);
|
|
@@ -325,13 +222,9 @@ class ChatUI {
|
|
|
325
222
|
const args = chunk.input ? JSON.stringify(chunk.input).slice(0, 30) : '';
|
|
326
223
|
console.log(colored(`[Tool] ${chunk.toolName} ${args}...`, YELLOW));
|
|
327
224
|
} else if (chunk.type === 'tool-result') {
|
|
328
|
-
const
|
|
329
|
-
const
|
|
330
|
-
console.log(colored(`[Tool] ${chunk.toolName} ${
|
|
331
|
-
} else if (chunk.type === 'usage') {
|
|
332
|
-
if (this.footerBar) {
|
|
333
|
-
this.footerBar.updateUsage(chunk.inputTokens || 0, chunk.outputTokens || 0);
|
|
334
|
-
}
|
|
225
|
+
const r = chunk.result;
|
|
226
|
+
const short = typeof r === 'string' ? r.slice(0, 30) : JSON.stringify(r).slice(0, 30);
|
|
227
|
+
console.log(colored(`[Tool] ${chunk.toolName} ${short}...`, GREEN));
|
|
335
228
|
}
|
|
336
229
|
});
|
|
337
230
|
|
|
@@ -339,17 +232,15 @@ class ChatUI {
|
|
|
339
232
|
console.log(colored('● ', GREEN));
|
|
340
233
|
});
|
|
341
234
|
|
|
342
|
-
this.sessionScope.on('message:complete', async ({ content
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
} catch (err) {}
|
|
235
|
+
this.sessionScope.on('message:complete', async ({ content }) => {
|
|
236
|
+
if (!content) {
|
|
237
|
+
try {
|
|
238
|
+
await this.agent.sendMessage('继续', { sessionId: this.sessionId, priority: 1 });
|
|
239
|
+
} catch {}
|
|
240
|
+
}
|
|
349
241
|
});
|
|
350
242
|
|
|
351
243
|
this.sessionScope.on('queue:completed', () => {
|
|
352
|
-
// 清理状态
|
|
353
244
|
if (this._lineBuffer.trim() && !this.interrupted) {
|
|
354
245
|
console.log(renderLine(this._lineBuffer, renderState));
|
|
355
246
|
}
|
|
@@ -361,9 +252,6 @@ class ChatUI {
|
|
|
361
252
|
});
|
|
362
253
|
}
|
|
363
254
|
|
|
364
|
-
/**
|
|
365
|
-
* 启动聊天界面
|
|
366
|
-
*/
|
|
367
255
|
start() {
|
|
368
256
|
this.rl = readline.createInterface({
|
|
369
257
|
input: process.stdin,
|
|
@@ -375,9 +263,6 @@ class ChatUI {
|
|
|
375
263
|
this.promptUser();
|
|
376
264
|
}
|
|
377
265
|
|
|
378
|
-
/**
|
|
379
|
-
* 打印欢迎信息
|
|
380
|
-
*/
|
|
381
266
|
printWelcome() {
|
|
382
267
|
console.log(`${colored('Foliko', CYAN)} - 持续对话聊天`);
|
|
383
268
|
console.log(
|
|
@@ -385,9 +270,6 @@ class ChatUI {
|
|
|
385
270
|
);
|
|
386
271
|
}
|
|
387
272
|
|
|
388
|
-
/**
|
|
389
|
-
* 获取多行输入
|
|
390
|
-
*/
|
|
391
273
|
getMultilineInput() {
|
|
392
274
|
return new Promise((resolve) => {
|
|
393
275
|
const lines = [];
|
|
@@ -395,21 +277,14 @@ class ChatUI {
|
|
|
395
277
|
const question = (isFirst) => {
|
|
396
278
|
const prompt = isFirst ? colored('> ', GREEN) : colored('- ', DIM);
|
|
397
279
|
this.rl.question(prompt, (input) => {
|
|
398
|
-
// 输入 !! 立即结束
|
|
399
280
|
if (input.trim() === '!!') {
|
|
400
|
-
|
|
401
|
-
resolve(result);
|
|
281
|
+
resolve(lines.join('\n').trim());
|
|
402
282
|
return;
|
|
403
283
|
}
|
|
404
|
-
|
|
405
|
-
// 空行:结束输入并发送(第一次空行就发送)
|
|
406
284
|
if (input.trim() === '') {
|
|
407
|
-
|
|
408
|
-
resolve(result);
|
|
285
|
+
resolve(lines.join('\n').trim());
|
|
409
286
|
return;
|
|
410
287
|
}
|
|
411
|
-
|
|
412
|
-
// 非空行:添加到 lines,继续输入
|
|
413
288
|
lines.push(input);
|
|
414
289
|
question(false);
|
|
415
290
|
});
|
|
@@ -419,9 +294,6 @@ class ChatUI {
|
|
|
419
294
|
});
|
|
420
295
|
}
|
|
421
296
|
|
|
422
|
-
/**
|
|
423
|
-
* 提示用户输入
|
|
424
|
-
*/
|
|
425
297
|
async promptUser() {
|
|
426
298
|
try {
|
|
427
299
|
const input = await this.getMultilineInput();
|
|
@@ -431,23 +303,18 @@ class ChatUI {
|
|
|
431
303
|
return;
|
|
432
304
|
}
|
|
433
305
|
|
|
434
|
-
// 尝试执行命令
|
|
435
306
|
if (await this._executeCommand(input.trim())) {
|
|
436
307
|
await this.promptUser();
|
|
437
308
|
return;
|
|
438
309
|
}
|
|
439
310
|
|
|
440
|
-
// 退出命令
|
|
441
311
|
if (input.toLowerCase() === 'exit' || input.toLowerCase() === 'quit') {
|
|
442
312
|
console.log(colored('[再见] 感谢使用 Foliko!', CYAN));
|
|
443
313
|
this.rl.close();
|
|
444
314
|
return;
|
|
445
315
|
}
|
|
446
316
|
|
|
447
|
-
// 发送消息(不等待,响应通过事件处理)
|
|
448
317
|
await this.sendMessage(input);
|
|
449
|
-
|
|
450
|
-
// 继续等待下一条消息
|
|
451
318
|
await this.promptUser();
|
|
452
319
|
} catch (err) {
|
|
453
320
|
console.error(`\n${colored('[错误]', RED)} ${err.message}`);
|
|
@@ -455,15 +322,10 @@ class ChatUI {
|
|
|
455
322
|
}
|
|
456
323
|
}
|
|
457
324
|
|
|
458
|
-
/**
|
|
459
|
-
* 发送消息并显示响应
|
|
460
|
-
*/
|
|
461
325
|
async sendMessage(message) {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
self._lineBuffer = '';
|
|
465
|
-
// 注意:不等待,直接返回。响应通过 sessionScope 事件处理
|
|
326
|
+
await this.agent.sendMessage(message, { sessionId: this.sessionId });
|
|
327
|
+
this._lineBuffer = '';
|
|
466
328
|
}
|
|
467
329
|
}
|
|
468
330
|
|
|
469
|
-
module.exports = {
|
|
331
|
+
module.exports = { ChatUIOld };
|
|
@@ -7,7 +7,7 @@ const figlet = require('figlet');
|
|
|
7
7
|
const { CLEAR_LINE, CYAN, DIM, GREEN, RED, YELLOW, colored } = require('../utils/ansi');
|
|
8
8
|
const { TUI, ProcessTerminal, Editor, Text, CombinedAutocompleteProvider, matchesKey, Container } = require('@earendil-works/pi-tui');
|
|
9
9
|
const { cleanResponse, logger } = require('../../../src/utils');
|
|
10
|
-
const { logEmitter } = require('../../../src/
|
|
10
|
+
const { logEmitter } = require('../../../src/common/logger');
|
|
11
11
|
const { renderLine } = require('../utils/markdown');
|
|
12
12
|
const { MessageBubble } = require('./components/message-bubble');
|
|
13
13
|
const Queue=require('js-queue');
|
|
@@ -335,7 +335,7 @@ class ChatUI {
|
|
|
335
335
|
|
|
336
336
|
// /help 命令显示所有注册的命令
|
|
337
337
|
if (cmd === '/help') {
|
|
338
|
-
const { getCommandRegistry } = require('../../../src/
|
|
338
|
+
const { getCommandRegistry } = require('../../../src/framework/command-registry');
|
|
339
339
|
const registry = getCommandRegistry();
|
|
340
340
|
const commands = registry.getAllCommands();
|
|
341
341
|
|
|
@@ -391,7 +391,7 @@ class ChatUI {
|
|
|
391
391
|
}
|
|
392
392
|
|
|
393
393
|
// 检查 CommandRegistry 中的命令
|
|
394
|
-
const { getCommandRegistry } = require('../../../src/
|
|
394
|
+
const { getCommandRegistry } = require('../../../src/framework/command-registry');
|
|
395
395
|
const registry = getCommandRegistry();
|
|
396
396
|
const cmd = registry.getCommand(cmdName);
|
|
397
397
|
if (cmd) {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const chalk = require('chalk').default;
|
|
9
9
|
const { visibleWidth, truncateToWidth } = require('@earendil-works/pi-tui');
|
|
10
|
-
const { TokenCounter } = require('../../../../src/
|
|
10
|
+
const { TokenCounter } = require('../../../../src/llm/tokens');
|
|
11
11
|
|
|
12
12
|
// Foliko 配色
|
|
13
13
|
const folikoDim = chalk.hex('#6B7280');
|
|
@@ -117,6 +117,9 @@ const DEFAULT_PLUGIN_PRIORITY = 100;
|
|
|
117
117
|
const SYSTEM_PLUGINS = new Set([
|
|
118
118
|
'ai', 'defaults', 'tools', 'skill-manager', 'session',
|
|
119
119
|
'storage', 'audit', 'rules', 'file-system',
|
|
120
|
+
// extension-executor 也是 system:true,必须保留以保证 rescanProject 后
|
|
121
|
+
// 用户持有的 extExec 引用始终有效,否则该实例会被卸载重建导致引用悬空
|
|
122
|
+
'extension-executor',
|
|
120
123
|
]);
|
|
121
124
|
|
|
122
125
|
/** Agent 配置目录名 */
|