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.
Files changed (212) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/CLAUDE.md +56 -30
  3. package/REFACTORING_PLAN.md +645 -0
  4. package/docs/architecture.md +131 -0
  5. package/docs/migration.md +57 -0
  6. package/docs/public-api.md +138 -0
  7. package/docs/usage.md +385 -0
  8. package/examples/ambient-example.js +20 -137
  9. package/examples/basic.js +21 -48
  10. package/examples/bootstrap.js +16 -74
  11. package/examples/mcp-example.js +6 -29
  12. package/examples/skill-example.js +6 -19
  13. package/examples/workflow.js +8 -56
  14. package/package.json +8 -4
  15. package/plugins/README.md +49 -0
  16. package/plugins/{ambient-agent → ambient}/EventWatcher.js +1 -1
  17. package/plugins/{ambient-agent → ambient}/ExplorerLoop.js +3 -3
  18. package/plugins/{ambient-agent → ambient}/GoalManager.js +2 -2
  19. package/plugins/ambient/README.md +14 -0
  20. package/plugins/{ambient-agent → ambient}/Reflector.js +1 -1
  21. package/plugins/{ambient-agent → ambient}/StateStore.js +1 -1
  22. package/plugins/{ambient-agent → ambient}/index.js +2 -2
  23. package/plugins/{ai-plugin.js → core/ai/index.js} +14 -30
  24. package/plugins/{audit-plugin.js → core/audit/index.js} +3 -30
  25. package/plugins/{coordinator-plugin.js → core/coordinator/index.js} +3 -35
  26. package/plugins/core/default/bootstrap.js +224 -0
  27. package/plugins/core/default/config.js +222 -0
  28. package/plugins/core/default/index.js +58 -0
  29. package/plugins/core/mcp/index.js +1 -0
  30. package/plugins/{python-plugin-loader.js → core/python-loader/index.js} +7 -187
  31. package/plugins/{rules-plugin.js → core/rules/index.js} +121 -64
  32. package/plugins/{scheduler-plugin.js → core/scheduler/index.js} +12 -114
  33. package/plugins/{session-plugin.js → core/session/index.js} +9 -73
  34. package/{src/capabilities/skill-manager.js → plugins/core/skill-manager/index.js} +64 -18
  35. package/plugins/{storage-plugin.js → core/storage/index.js} +5 -29
  36. package/plugins/{subagent-plugin.js → core/sub-agent/index.js} +10 -171
  37. package/plugins/{think-plugin.js → core/think/index.js} +24 -91
  38. package/{src/capabilities/workflow-engine.js → plugins/core/workflow/index.js} +87 -85
  39. package/plugins/default-plugins.js +6 -720
  40. package/plugins/{data-splitter-plugin.js → executors/data-splitter/index.js} +9 -83
  41. package/plugins/{extension-executor-plugin.js → executors/extension/index.js} +13 -97
  42. package/plugins/{python-executor-plugin.js → executors/python/index.js} +6 -31
  43. package/plugins/{shell-executor-plugin.js → executors/shell/index.js} +2 -5
  44. package/plugins/install/README.md +9 -0
  45. package/plugins/{install-plugin.js → install/index.js} +3 -3
  46. package/plugins/{file-system-plugin.js → io/file-system/index.js} +34 -236
  47. package/plugins/{web-plugin.js → io/web/index.js} +11 -113
  48. package/plugins/memory/README.md +13 -0
  49. package/plugins/{memory-plugin.js → memory/index.js} +4 -18
  50. package/plugins/messaging/email/README.md +19 -0
  51. package/plugins/{email → messaging/email}/index.js +3 -3
  52. package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +4 -4
  53. package/plugins/{qq-plugin.js → messaging/qq/index.js} +6 -17
  54. package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +4 -4
  55. package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +16 -16
  56. package/plugins/{plugin-manager-plugin.js → plugin-manager/index.js} +36 -180
  57. package/plugins/{tools-plugin.js → tools/index.js} +68 -116
  58. package/plugins/trading/README.md +15 -0
  59. package/plugins/{gate-trading.js → trading/index.js} +8 -8
  60. package/{examples → sandbox}/test-concurrent-chat.js +2 -2
  61. package/{examples → sandbox}/test-long-chat.js +2 -2
  62. package/{examples → sandbox}/test-session-chat.js +2 -2
  63. package/{examples → sandbox}/test-web-plugin.js +1 -1
  64. package/{examples → sandbox}/test-weixin-feishu.js +2 -2
  65. package/src/agent/base.js +56 -0
  66. package/src/{core/agent-chat.js → agent/chat.js} +11 -11
  67. package/src/{core/coordinator-manager.js → agent/coordinator.js} +3 -3
  68. package/src/agent/index.js +111 -0
  69. package/src/agent/main.js +337 -0
  70. package/src/agent/prompt.js +78 -0
  71. package/src/agent/sub.js +198 -0
  72. package/src/agent/worker.js +104 -0
  73. package/{cli/bin/foliko.js → src/cli/bin.js} +1 -1
  74. package/{cli/src → src/cli}/commands/chat.js +25 -21
  75. package/{cli/src → src/cli}/index.js +1 -0
  76. package/{cli/src → src/cli}/ui/chat-ui-old.js +40 -178
  77. package/{cli/src → src/cli}/ui/chat-ui.js +3 -3
  78. package/{cli/src → src/cli}/ui/components/footer-bar.js +1 -1
  79. package/src/common/errors.js +402 -0
  80. package/src/{utils → common}/logger.js +33 -0
  81. package/src/{utils/chat-queue.js → common/queue.js} +2 -2
  82. package/src/config/plugin-config.js +50 -0
  83. package/src/context/agent.js +32 -0
  84. package/src/context/compaction-prompts.js +170 -0
  85. package/src/context/compaction-utils.js +191 -0
  86. package/src/context/compressor.js +413 -0
  87. package/src/context/index.js +9 -0
  88. package/src/{core/context-manager.js → context/manager.js} +1 -1
  89. package/src/context/request.js +50 -0
  90. package/src/context/session.js +33 -0
  91. package/src/context/storage.js +30 -0
  92. package/src/executors/mcp-client.js +153 -0
  93. package/src/executors/mcp-desc.js +236 -0
  94. package/src/executors/mcp-executor.js +91 -956
  95. package/src/{core → framework}/command-registry.js +1 -1
  96. package/src/framework/framework.js +300 -0
  97. package/src/framework/index.js +18 -0
  98. package/src/framework/lifecycle.js +203 -0
  99. package/src/framework/loader.js +78 -0
  100. package/src/framework/registry.js +86 -0
  101. package/src/{core/ui-extension-context.js → framework/ui-extension.js} +1 -1
  102. package/src/index.js +130 -15
  103. package/src/llm/index.js +26 -0
  104. package/src/llm/provider.js +212 -0
  105. package/src/llm/registry.js +11 -0
  106. package/src/{core/token-counter.js → llm/tokens.js} +4 -37
  107. package/src/{core/plugin-base.js → plugin/base.js} +10 -136
  108. package/src/plugin/index.js +14 -0
  109. package/src/plugin/loader.js +101 -0
  110. package/src/plugin/manager.js +484 -0
  111. package/src/{core → session}/branch-summary-auto.js +2 -2
  112. package/src/{core/chat-session.js → session/chat.js} +2 -2
  113. package/src/session/index.js +7 -0
  114. package/src/{core/session-manager.js → session/session.js} +2 -2
  115. package/src/session/ttl.js +92 -0
  116. package/src/{core/jsonl-storage.js → storage/jsonl.js} +1 -1
  117. package/src/tool/executor.js +85 -0
  118. package/src/tool/index.js +15 -0
  119. package/src/tool/registry.js +143 -0
  120. package/src/{core/tool-router.js → tool/router.js} +17 -124
  121. package/src/tool/schema.js +108 -0
  122. package/src/utils/data-splitter.js +1 -1
  123. package/src/utils/download.js +1 -1
  124. package/src/utils/index.js +6 -6
  125. package/src/utils/message-validator.js +1 -1
  126. package/tests/core/context-storage.test.js +46 -0
  127. package/tests/core/llm.test.js +54 -0
  128. package/tests/core/plugin.test.js +42 -0
  129. package/tests/core/tool.test.js +60 -0
  130. package/tests/setup.js +10 -0
  131. package/tests/smoke.test.js +58 -0
  132. package/vitest.config.js +9 -0
  133. package/cli/src/daemon.js +0 -149
  134. package/docs/CONTEXT_DESIGN.md +0 -1596
  135. package/docs/ai-sdk-optimization.md +0 -655
  136. package/docs/features.md +0 -120
  137. package/docs/qq-bot.md +0 -976
  138. package/docs/quick-reference.md +0 -160
  139. package/docs/user-manual.md +0 -1391
  140. package/images/geometric_shapes.jpg +0 -0
  141. package/images/sunset_mountain_lake.jpg +0 -0
  142. package/skills/poster-guide/SKILL.md +0 -792
  143. package/src/capabilities/index.js +0 -11
  144. package/src/core/agent.js +0 -808
  145. package/src/core/context-compressor.js +0 -959
  146. package/src/core/enhanced-context-compressor.js +0 -210
  147. package/src/core/framework.js +0 -1422
  148. package/src/core/index.js +0 -30
  149. package/src/core/plugin-manager.js +0 -961
  150. package/src/core/provider-registry.js +0 -159
  151. package/src/core/provider.js +0 -156
  152. package/src/core/request-context.js +0 -98
  153. package/src/core/subagent.js +0 -442
  154. package/src/core/system-prompt-builder.js +0 -120
  155. package/src/core/tool-executor.js +0 -202
  156. package/src/core/tool-registry.js +0 -517
  157. package/src/core/worker-agent.js +0 -192
  158. package/src/executors/executor-base.js +0 -58
  159. package/src/utils/error-boundary.js +0 -363
  160. package/src/utils/error.js +0 -374
  161. package/system.md +0 -1645
  162. package/website_v2/README.md +0 -57
  163. package/website_v2/SPEC.md +0 -1
  164. package/website_v2/docs/api.html +0 -128
  165. package/website_v2/docs/configuration.html +0 -147
  166. package/website_v2/docs/plugin-development.html +0 -129
  167. package/website_v2/docs/project-structure.html +0 -89
  168. package/website_v2/docs/skill-development.html +0 -85
  169. package/website_v2/index.html +0 -489
  170. package/website_v2/scripts/main.js +0 -93
  171. package/website_v2/styles/animations.css +0 -8
  172. package/website_v2/styles/docs.css +0 -83
  173. package/website_v2/styles/main.css +0 -417
  174. package/xhs_auth.json +0 -268
  175. package//346/265/267/346/212/245/346/217/222/344/273/266.md +0 -621
  176. /package/plugins/{ambient-agent → ambient}/constants.js +0 -0
  177. /package/plugins/{email → messaging/email}/constants.js +0 -0
  178. /package/plugins/{email → messaging/email}/handlers.js +0 -0
  179. /package/plugins/{email → messaging/email}/monitor.js +0 -0
  180. /package/plugins/{email → messaging/email}/parser.js +0 -0
  181. /package/plugins/{email → messaging/email}/reply.js +0 -0
  182. /package/plugins/{email → messaging/email}/utils.js +0 -0
  183. /package/{examples → sandbox}/test-chat.js +0 -0
  184. /package/{examples → sandbox}/test-mcp.js +0 -0
  185. /package/{examples → sandbox}/test-reload.js +0 -0
  186. /package/{examples → sandbox}/test-telegram.js +0 -0
  187. /package/{examples → sandbox}/test-tg-bot.js +0 -0
  188. /package/{examples → sandbox}/test-tg-simple.js +0 -0
  189. /package/{examples → sandbox}/test-tg.js +0 -0
  190. /package/{examples → sandbox}/test-think.js +0 -0
  191. /package/src/{core/sub-agent-config.js → agent/sub-config.js} +0 -0
  192. /package/{cli/src → src/cli}/commands/daemon.js +0 -0
  193. /package/{cli/src → src/cli}/commands/list.js +0 -0
  194. /package/{cli/src → src/cli}/commands/plugin.js +0 -0
  195. /package/{cli/src → src/cli}/ui/components/agent-mention-provider.js +0 -0
  196. /package/{cli/src → src/cli}/ui/components/chained-autocomplete-provider.js +0 -0
  197. /package/{cli/src → src/cli}/ui/components/message-bubble.js +0 -0
  198. /package/{cli/src → src/cli}/ui/components/status-bar.js +0 -0
  199. /package/{cli/src → src/cli}/utils/ansi.js +0 -0
  200. /package/{cli/src → src/cli}/utils/config.js +0 -0
  201. /package/{cli/src → src/cli}/utils/markdown.js +0 -0
  202. /package/{cli/src → src/cli}/utils/plugin-config.js +0 -0
  203. /package/{cli/src → src/cli}/utils/render-diff.js +0 -0
  204. /package/src/{utils/circuit-breaker.js → common/circuit.js} +0 -0
  205. /package/src/{core → common}/constants.js +0 -0
  206. /package/src/{utils/edit-diff.js → common/diff.js} +0 -0
  207. /package/src/{utils/event-emitter.js → common/events.js} +0 -0
  208. /package/src/{utils → common}/id.js +0 -0
  209. /package/src/{utils → common}/retry.js +0 -0
  210. /package/src/{core/notification-manager.js → notification/manager.js} +0 -0
  211. /package/src/{core/session-entry.js → session/entry.js} +0 -0
  212. /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 };
@@ -6,6 +6,6 @@
6
6
  // 加载 .env 文件(覆盖系统环境变量,确保 .env 优先级最高)
7
7
  require('dotenv').config({ override: true });
8
8
 
9
- const { cli } = require('../src');
9
+ const { cli } = require('./index');
10
10
 
11
11
  cli();
@@ -4,11 +4,10 @@
4
4
 
5
5
  const path = require('path');
6
6
 
7
- const { Framework } = require('../../../src');
7
+ const foliko = require('../../../src');
8
8
  const { ChatUI } = require('../ui/chat-ui');
9
- const { ChatUI: OldChatUI } = require('../ui/chat-ui-old');
10
- const { logger, LOG_LEVELS } = require('../../../src/utils/logger');
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.useOldUI = true;
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 = new Framework({ debug: false });
156
-
157
-
158
- await framework.bootstrap({
159
- agentDir: process.cwd() + '/.foliko',
160
- aiConfig: {
161
- provider: options.provider,
162
- model: options.model,
163
- baseURL: options.baseURL,
164
- apiKey: options.apiKey,
165
- providerOptions: {
166
- maxOutputTokens: 8192,
167
- minimax: { // 这里的 key 必须和上面 createOpenAICompatible 里的 name 一致
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
- const UI = options.useOldUI ? OldChatUI : ChatUI;
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 中显示
@@ -108,6 +108,7 @@ Chat Options:
108
108
  --model <name> 指定 AI 模型
109
109
  --provider <name> 指定 AI 提供商
110
110
  --base-url <url> 指定 API 基础地址
111
+ --old 使用传统 readline 界面(轻量,无 TUI 依赖)
111
112
 
112
113
  Examples:
113
114
  foliko chat
@@ -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 { cleanResponse, logger } = require('../../../src/utils');
10
- const { logEmitter } = require('../../../src/utils/logger');
11
- const Queue = require('js-queue');
9
+ const { logger } = require('../../../src/common/logger');
12
10
 
13
- class ChatUI {
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: "compress", description: "压缩记录" },
41
- { name: "clear", description: "清除记录" },
42
- { name: "exit", description: "退出" },
43
- { name: "help", description: "显示帮助信息" },
44
- { name: "reload", description: "重载技能" },
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
- INFO: 'dim',
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 level = `[${data.level}]`;
101
- const levelKey = levelDict[data.level] || 'dim';
102
- console.log(colored(level, RED) + colored(data.message, levelKey));
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 time = data.timestamp ? new Date(data.timestamp).toLocaleTimeString('zh-CN') : '';
110
- const notificationText = `[通知] ${data.title}${time ? ` (${time})` : ''}`;
111
- console.log(colored(notificationText, CYAN));
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
- // Skill 命令 (格式: skillname:cmdname)
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 skillCommands = extExecutor.getSkillCommands();
237
- if (skillCommands.length > 0) {
161
+ const cmds = extExecutor.getSkillCommands();
162
+ if (cmds.length > 0) {
238
163
  console.log(colored('\n[技能命令]', CYAN));
239
- skillCommands.forEach(cmd => {
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
- const { sessionId } = this;
275
- this.agent.clearContext(sessionId);
276
-
183
+ this.agent.clearContext(this.sessionId);
277
184
  if (this.sessionPlugin) {
278
- const manager = this.sessionPlugin._getSessionManager(sessionId);
279
- if (manager) manager.clearMessages();
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 { sessionId } = this;
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 result = chunk.result;
329
- const shortResult = typeof result === 'string' ? result.slice(0, 30) : JSON.stringify(result).slice(0, 30);
330
- console.log(colored(`[Tool] ${chunk.toolName} ${shortResult}...`, GREEN));
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, requestId }) => {
343
- try {
344
- if (!content) {
345
- const msg = '继续';
346
- await this.agent.sendMessage(msg, { sessionId: this.sessionId, priority: 1 });
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
- const result = lines.join('\n').trim();
401
- resolve(result);
281
+ resolve(lines.join('\n').trim());
402
282
  return;
403
283
  }
404
-
405
- // 空行:结束输入并发送(第一次空行就发送)
406
284
  if (input.trim() === '') {
407
- const result = lines.join('\n').trim();
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
- const self = this;
463
- const e=await this.agent.sendMessage(message, { sessionId: self.sessionId })
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 = { ChatUI };
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/utils/logger');
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/core/command-registry');
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/core/command-registry');
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/core/token-counter');
10
+ const { TokenCounter } = require('../../../../src/llm/tokens');
11
11
 
12
12
  // Foliko 配色
13
13
  const folikoDim = chalk.hex('#6B7280');