foliko 1.1.93 → 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.
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 +202 -0
  27. package/plugins/core/default/config.js +220 -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 +2 -2
  52. package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +3 -3
  53. package/plugins/{qq-plugin.js → messaging/qq/index.js} +5 -16
  54. package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +3 -3
  55. package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +15 -15
  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 +261 -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,402 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Foliko Errors - 统一错误处理系统
5
+ * 合并了 error.js (错误类型) + error-boundary.js (错误边界)
6
+ */
7
+
8
+ const { EventEmitter } = require('./events');
9
+ const { logger } = require('./logger');
10
+
11
+ // ============================================
12
+ // 错误类型
13
+ // ============================================
14
+
15
+ /**
16
+ * Foliko 错误基类
17
+ */
18
+ class FolikoError extends Error {
19
+ constructor(message, options = {}) {
20
+ super(message);
21
+ this.name = this.constructor.name;
22
+ this.code = options.code || 'FOLIKO_ERROR';
23
+ this.context = options.context || {};
24
+ if (options.stack) {
25
+ this.stack = options.stack;
26
+ } else {
27
+ Error.captureStackTrace(this, this.constructor);
28
+ }
29
+ }
30
+
31
+ toJSON() {
32
+ return {
33
+ name: this.name,
34
+ message: this.message,
35
+ code: this.code,
36
+ context: this.context,
37
+ stack: this.stack,
38
+ };
39
+ }
40
+ }
41
+
42
+ class PluginError extends FolikoError {
43
+ constructor(message, options = {}) {
44
+ super(message, { code: 'PLUGIN_ERROR', context: options.context });
45
+ }
46
+ }
47
+
48
+ class ToolError extends FolikoError {
49
+ constructor(message, options = {}) {
50
+ super(message, {
51
+ code: 'TOOL_ERROR',
52
+ context: { toolName: options.toolName, args: options.args, ...options.context },
53
+ });
54
+ }
55
+ }
56
+
57
+ class AIError extends FolikoError {
58
+ constructor(message, options = {}) {
59
+ super(message, {
60
+ code: 'AI_ERROR',
61
+ context: { model: options.model, provider: options.provider, statusCode: options.statusCode, ...options.context },
62
+ });
63
+ }
64
+ }
65
+
66
+ class SessionError extends FolikoError {
67
+ constructor(message, options = {}) {
68
+ super(message, { code: 'SESSION_ERROR', context: { sessionId: options.sessionId, ...options.context } });
69
+ }
70
+ }
71
+
72
+ class ConfigError extends FolikoError {
73
+ constructor(message, options = {}) {
74
+ super(message, {
75
+ code: 'CONFIG_ERROR',
76
+ context: { configKey: options.configKey, expected: options.expected, actual: options.actual, ...options.context },
77
+ });
78
+ }
79
+ }
80
+
81
+ class WorkflowError extends FolikoError {
82
+ constructor(message, options = {}) {
83
+ super(message, {
84
+ code: 'WORKFLOW_ERROR',
85
+ context: { workflowName: options.workflowName, stepName: options.stepName, stepIndex: options.stepIndex, ...options.context },
86
+ });
87
+ }
88
+ }
89
+
90
+ class ValidationError extends FolikoError {
91
+ constructor(message, options = {}) {
92
+ super(message, {
93
+ code: 'VALIDATION_ERROR',
94
+ context: { field: options.field, value: options.value, expected: options.expected, ...options.context },
95
+ });
96
+ }
97
+ }
98
+
99
+ class ToolNotFoundError extends ToolError {
100
+ constructor(toolName) {
101
+ super(`Tool not found: ${toolName}`, { toolName });
102
+ this.code = 'TOOL_NOT_FOUND';
103
+ }
104
+ }
105
+
106
+ class PluginNotFoundError extends PluginError {
107
+ constructor(pluginName) {
108
+ super(`Plugin not found: ${pluginName}`, { context: { pluginName } });
109
+ this.code = 'PLUGIN_NOT_FOUND';
110
+ }
111
+ }
112
+
113
+ class NetworkError extends FolikoError {
114
+ constructor(message, options = {}) {
115
+ super(message, {
116
+ code: 'NETWORK_ERROR',
117
+ context: { host: options.host, port: options.port, code: options.code, ...options.context },
118
+ });
119
+ }
120
+ }
121
+
122
+ class AuthError extends FolikoError {
123
+ constructor(message, options = {}) {
124
+ super(message, {
125
+ code: 'AUTH_ERROR',
126
+ context: { authType: options.authType, userId: options.userId, ...options.context },
127
+ });
128
+ }
129
+ }
130
+
131
+ class RateLimitError extends FolikoError {
132
+ constructor(message, options = {}) {
133
+ super(message, {
134
+ code: 'RATE_LIMIT_ERROR',
135
+ context: { retryAfter: options.retryAfter, limit: options.limit, ...options.context },
136
+ });
137
+ }
138
+ }
139
+
140
+ class TimeoutError extends FolikoError {
141
+ constructor(message, options = {}) {
142
+ super(message, {
143
+ code: 'TIMEOUT_ERROR',
144
+ context: { timeout: options.timeout, operation: options.operation, ...options.context },
145
+ });
146
+ }
147
+ }
148
+
149
+ class FileSystemError extends FolikoError {
150
+ constructor(message, options = {}) {
151
+ super(message, {
152
+ code: 'FS_ERROR',
153
+ context: { path: options.path, operation: options.operation, ...options.context },
154
+ });
155
+ }
156
+ }
157
+
158
+ function isErrorOfType(error, ErrorClass) {
159
+ return error instanceof ErrorClass;
160
+ }
161
+
162
+ function safeErrorInfo(error) {
163
+ if (error instanceof FolikoError) {
164
+ return error.toJSON();
165
+ }
166
+ return {
167
+ name: error.name || 'Error',
168
+ message: error.message,
169
+ code: 'UNKNOWN_ERROR',
170
+ context: {},
171
+ stack: error.stack,
172
+ };
173
+ }
174
+
175
+ function createError(type, message, options = {}) {
176
+ const errorClasses = {
177
+ plugin: PluginError, tool: ToolError, ai: AIError, session: SessionError,
178
+ config: ConfigError, workflow: WorkflowError, validation: ValidationError,
179
+ network: NetworkError, auth: AuthError, rateLimit: RateLimitError,
180
+ timeout: TimeoutError, fs: FileSystemError,
181
+ };
182
+ const ErrorClass = errorClasses[type] || FolikoError;
183
+ return new ErrorClass(message, options);
184
+ }
185
+
186
+ function isRetryable(error) {
187
+ if (error instanceof RateLimitError) return true;
188
+ if (error instanceof TimeoutError) return true;
189
+ if (error instanceof NetworkError) return true;
190
+ if (error instanceof FolikoError) {
191
+ return ['RATE_LIMIT_ERROR', 'TIMEOUT_ERROR', 'NETWORK_ERROR'].includes(error.code);
192
+ }
193
+ return false;
194
+ }
195
+
196
+ function getFriendlyMessage(error) {
197
+ if (error instanceof FolikoError) {
198
+ const messages = {
199
+ PLUGIN_NOT_FOUND: '插件未找到,请检查配置',
200
+ TOOL_NOT_FOUND: '工具未找到,请检查工具名称',
201
+ CONFIG_ERROR: '配置错误,请检查配置文件',
202
+ VALIDATION_ERROR: '输入验证失败,请检查输入数据',
203
+ RATE_LIMIT_ERROR: '请求过于频繁,请稍后再试',
204
+ TIMEOUT_ERROR: '请求超时,请检查网络连接',
205
+ NETWORK_ERROR: '网络错误,请检查网络连接',
206
+ AUTH_ERROR: '认证失败,请检查凭据是否正确',
207
+ };
208
+ return messages[error.code] || error.message;
209
+ }
210
+ return error.message || '发生未知错误';
211
+ }
212
+
213
+ // ============================================
214
+ // 错误边界
215
+ // ============================================
216
+
217
+ const Severity = {
218
+ LOW: 'low',
219
+ MEDIUM: 'medium',
220
+ HIGH: 'high',
221
+ CRITICAL: 'critical',
222
+ };
223
+
224
+ const RecoveryAction = {
225
+ RETRY: 'retry',
226
+ SKIP: 'skip',
227
+ FALLBACK: 'fallback',
228
+ ABORT: 'abort',
229
+ IGNORE: 'ignore',
230
+ };
231
+
232
+ class ErrorBoundary extends EventEmitter {
233
+ constructor(config = {}) {
234
+ super();
235
+ this.name = config.name || 'AnonymousBoundary';
236
+ this._onError = config.onError || null;
237
+ this._onRecovery = config.onRecovery || null;
238
+ this._fallback = config.fallback || null;
239
+ this._logErrors = config.logErrors !== false;
240
+ this._propagateErrors = config.propagateErrors || false;
241
+ this._logger = logger.child('ErrorBoundary');
242
+ this._errorCount = 0;
243
+ this._lastError = null;
244
+ this._errorHistory = [];
245
+ this._maxHistorySize = config.maxHistorySize || 50;
246
+ }
247
+
248
+ async execute(operation, context = {}) {
249
+ try {
250
+ const result = await (typeof operation === 'function' ? operation() : operation);
251
+ return { success: true, result };
252
+ } catch (error) {
253
+ this._errorCount++;
254
+ this._lastError = error;
255
+ this._addToHistory(error, context);
256
+ if (this._logErrors) this._logError(error, context);
257
+ this.emit('error', { error, context, boundary: this.name });
258
+
259
+ let recoveryAction = RecoveryAction.ABORT;
260
+ if (this._onError) {
261
+ try {
262
+ recoveryAction = await Promise.resolve(this._onError(error, context, this));
263
+ } catch (handlerError) {
264
+ this._logger.warn(`Error in onError handler: ${handlerError.message}`);
265
+ }
266
+ }
267
+
268
+ const { action, fallbackResult } = await this._handleRecovery(error, context, recoveryAction);
269
+
270
+ if (action !== RecoveryAction.ABORT) {
271
+ this.emit('recovery', { error, context, action, boundary: this.name });
272
+ if (this._onRecovery) {
273
+ try { await this._onRecovery(error, context, action, this); } catch (recoveryError) {
274
+ logger.warn(`Error in onRecovery handler: ${recoveryError.message}`);
275
+ }
276
+ }
277
+ }
278
+
279
+ if (this._propagateErrors || (action === RecoveryAction.ABORT && !fallbackResult)) {
280
+ throw error;
281
+ }
282
+
283
+ return { success: false, error, recovered: action !== RecoveryAction.ABORT, fallbackResult, action };
284
+ }
285
+ }
286
+
287
+ async executeWithRetry(operation, retryConfig = {}, context = {}) {
288
+ const { maxAttempts = 3, baseDelay = 1000, shouldRetry = () => true } = retryConfig;
289
+ let lastError;
290
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
291
+ try {
292
+ return await this.execute(operation, { ...context, attempt });
293
+ } catch (error) {
294
+ lastError = error;
295
+ if (attempt >= maxAttempts || !shouldRetry(error, attempt)) throw error;
296
+ const delay = baseDelay * Math.pow(2, attempt - 1);
297
+ logger.debug(`Retry attempt ${attempt}/${maxAttempts} after ${delay}ms...`);
298
+ await new Promise((resolve) => setTimeout(resolve, delay));
299
+ }
300
+ }
301
+ throw lastError;
302
+ }
303
+
304
+ wrap(fn, defaultContext = {}) {
305
+ return async (...args) => {
306
+ const result = await this.execute(() => fn(...args), defaultContext);
307
+ return result.success ? result.result : result.fallbackResult;
308
+ };
309
+ }
310
+
311
+ async _handleRecovery(error, context, recoveryAction) {
312
+ let fallbackResult = null;
313
+ let action = recoveryAction;
314
+ switch (recoveryAction) {
315
+ case RecoveryAction.FALLBACK:
316
+ if (this._fallback) {
317
+ try {
318
+ fallbackResult = await this._fallback(error, context, this);
319
+ action = RecoveryAction.RETRY;
320
+ } catch (fallbackError) {
321
+ logger.warn(`Fallback also failed: ${fallbackError.message}`);
322
+ action = RecoveryAction.ABORT;
323
+ }
324
+ }
325
+ break;
326
+ case RecoveryAction.SKIP:
327
+ fallbackResult = null;
328
+ break;
329
+ case RecoveryAction.IGNORE:
330
+ fallbackResult = undefined;
331
+ break;
332
+ default:
333
+ action = RecoveryAction.ABORT;
334
+ }
335
+ return { action, fallbackResult };
336
+ }
337
+
338
+ _logError(error, context) {
339
+ const errorInfo = safeErrorInfo(error);
340
+ const severity = this._determineSeverity(error);
341
+ const logMsg = `[${this.name}] Error in ${context.operation || 'unknown'}: ${error.message}`;
342
+ switch (severity) {
343
+ case Severity.LOW: this._logger.debug(logMsg); break;
344
+ case Severity.MEDIUM: this._logger.warn(logMsg); break;
345
+ case Severity.HIGH: this._logger.error(logMsg); break;
346
+ case Severity.CRITICAL: this._logger.error(`CRITICAL: ${logMsg}`, { stack: error.stack }); break;
347
+ }
348
+ }
349
+
350
+ _determineSeverity(error) {
351
+ if (isErrorOfType(error, FolikoError)) {
352
+ return error.context?.severity || Severity.MEDIUM;
353
+ }
354
+ const msg = (error.message || '').toLowerCase();
355
+ if (msg.includes('timeout') || msg.includes('network')) return Severity.MEDIUM;
356
+ if (msg.includes('memory') || msg.includes('fatal')) return Severity.CRITICAL;
357
+ return Severity.HIGH;
358
+ }
359
+
360
+ _addToHistory(error, context) {
361
+ this._errorHistory.push({ timestamp: new Date().toISOString(), error: safeErrorInfo(error), context });
362
+ if (this._errorHistory.length > this._maxHistorySize) this._errorHistory.shift();
363
+ }
364
+
365
+ getStats() {
366
+ return {
367
+ totalErrors: this._errorCount,
368
+ lastError: this._lastError ? safeErrorInfo(this._lastError) : null,
369
+ historySize: this._errorHistory.length,
370
+ recentErrors: this._errorHistory.slice(-10),
371
+ };
372
+ }
373
+
374
+ clearHistory() { this._errorHistory = []; this._lastError = null; }
375
+ }
376
+
377
+ function createErrorBoundary(config = {}) {
378
+ return new ErrorBoundary(config);
379
+ }
380
+
381
+ function combineBoundaries(boundaries) {
382
+ const combined = new ErrorBoundary({ name: 'CombinedBoundary' });
383
+ for (const boundary of boundaries) {
384
+ boundary.on('error', (data) => combined.emit('error', data));
385
+ boundary.on('recovery', (data) => combined.emit('recovery', data));
386
+ }
387
+ return combined;
388
+ }
389
+
390
+ // ============================================
391
+ // Exports
392
+ // ============================================
393
+
394
+ module.exports = {
395
+ // Error types
396
+ FolikoError, PluginError, ToolError, AIError, SessionError, ConfigError,
397
+ WorkflowError, ValidationError, ToolNotFoundError, PluginNotFoundError,
398
+ NetworkError, AuthError, RateLimitError, TimeoutError, FileSystemError,
399
+ isErrorOfType, safeErrorInfo, createError, isRetryable, getFriendlyMessage,
400
+ // Error boundary
401
+ ErrorBoundary, Severity, RecoveryAction, createErrorBoundary, combineBoundaries,
402
+ };
@@ -196,6 +196,38 @@ function createGlobalLogger() {
196
196
  return new Logger({ namespace: 'foliko' });
197
197
  }
198
198
 
199
+ /**
200
+ * Create a framework-bound logger that forwards log events to the framework
201
+ * @param {Object} framework - Framework instance (EventEmitter)
202
+ * @returns {Logger}
203
+ */
204
+ function createFrameworkLogger(framework) {
205
+ const logger = new Logger({ namespace: 'foliko' });
206
+ const state = { silent: false };
207
+
208
+ // Forward log events to framework
209
+ const onLog = (data) => {
210
+ if (!state.silent) framework.emit('log', data);
211
+ };
212
+ logger.on('log', onLog);
213
+
214
+ // Override methods to emit per-level events
215
+ const LEVEL_METHODS = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
216
+ for (const method of LEVEL_METHODS) {
217
+ const original = logger[method].bind(logger);
218
+ logger[method] = (...args) => {
219
+ if (!state.silent) original(...args);
220
+ };
221
+ }
222
+
223
+ logger.setSilent = (v) => { state.silent = v; };
224
+ logger.setGlobalLogLevel = (level) => {
225
+ if (rootLogger) rootLogger.level = level;
226
+ };
227
+
228
+ return logger;
229
+ }
230
+
199
231
  // 导出
200
232
  module.exports = {
201
233
  Logger,
@@ -216,6 +248,7 @@ module.exports = {
216
248
  },
217
249
  getLogFilePath: () => currentLogFile,
218
250
  initLogger,
251
+ createFrameworkLogger,
219
252
  LOG_LEVELS: {
220
253
  TRACE: 'trace',
221
254
  DEBUG: 'debug',
@@ -1,5 +1,5 @@
1
- const { EventEmitter } = require('./event-emitter');
2
- const { cleanResponse } = require('./index');
1
+ const { EventEmitter } = require('./events');
2
+ const { cleanResponse } = require('../utils');
3
3
  const { logger } = require('./logger');
4
4
  const { isNetworkError, calculateDelay, PRESETS } = require('./retry');
5
5
  const log = logger.child('ChatQueue');
@@ -0,0 +1,50 @@
1
+ /**
2
+ * 插件管理器公共配置
3
+ */
4
+
5
+ // 默认插件仓库
6
+ const DEFAULT_REPO = 'https://github.com/chnak/foliko-plugins.git';
7
+
8
+ // 发布时忽略的文件和目录
9
+ const IGNORE_PATTERNS = [
10
+ 'node_modules',
11
+ '.git',
12
+ '.env',
13
+ '.DS_Store',
14
+ 'Thumbs.db',
15
+ '*.log',
16
+ '*.lock',
17
+ '*.bak',
18
+ '.claude',
19
+ '.foliko',
20
+ 'examples',
21
+ 'dist',
22
+ 'build',
23
+ 'coverage',
24
+ 'tests',
25
+ '__tests__',
26
+ '*.test.js',
27
+ '*.spec.js',
28
+ 'package-lock.json',
29
+ 'yarn.lock',
30
+ 'pnpm-lock.yaml',
31
+ ];
32
+
33
+ /**
34
+ * 检查文件/目录是否应该被忽略
35
+ */
36
+ function shouldIgnore(name) {
37
+ return IGNORE_PATTERNS.some((pattern) => {
38
+ if (pattern.startsWith('*.')) {
39
+ const ext = pattern.slice(1);
40
+ return name.endsWith(ext);
41
+ }
42
+ return name === pattern;
43
+ });
44
+ }
45
+
46
+ module.exports = {
47
+ DEFAULT_REPO,
48
+ IGNORE_PATTERNS,
49
+ shouldIgnore,
50
+ };
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * AgentContext - Agent 级上下文
5
+ * 管理 Agent 运行时的状态
6
+ */
7
+
8
+ class AgentContext {
9
+ constructor(options = {}) {
10
+ this.agentId = options.agentId;
11
+ this.name = options.name || 'Agent';
12
+ this.parentContext = options.parentContext || null;
13
+ this._data = new Map();
14
+ this.status = 'idle';
15
+ this.startedAt = Date.now();
16
+ }
17
+
18
+ set(key, value) { this._data.set(key, value); }
19
+ get(key) { return this._data.get(key); }
20
+ has(key) { return this._data.has(key); }
21
+
22
+ toJSON() {
23
+ return {
24
+ agentId: this.agentId,
25
+ name: this.name,
26
+ status: this.status,
27
+ startedAt: this.startedAt,
28
+ };
29
+ }
30
+ }
31
+
32
+ module.exports = { AgentContext };
@@ -0,0 +1,170 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Compaction and branch summarization prompt templates
5
+ */
6
+
7
+ const SUMMARIZATION_SYSTEM_PROMPT = `You are a context summarization assistant. Your task is to read a conversation between a user and an AI coding assistant, then produce a structured summary following the exact format specified.
8
+
9
+ Do NOT continue the conversation. Do NOT respond to any questions in the conversation. ONLY output the structured summary.`;
10
+
11
+ const SUMMARIZATION_PROMPT = `The messages above are a conversation to summarize. Create a structured context checkpoint summary that another LLM will use to continue the work.
12
+
13
+ Use this EXACT format:
14
+
15
+ ## Goal
16
+ [What is the user trying to accomplish? Can be multiple items if the session covers different tasks.]
17
+
18
+ ## Constraints & Preferences
19
+ - [Any constraints, preferences, or requirements mentioned by user]
20
+ - [Or "(none)" if none were mentioned]
21
+
22
+ ## Progress
23
+ ### Done
24
+ - [x] [Completed tasks/changes]
25
+
26
+ ### In Progress
27
+ - [ ] [Current work]
28
+
29
+ ### Blocked
30
+ - [Issues preventing progress, if any]
31
+
32
+ ## Key Decisions
33
+ - **[Decision]**: [Brief rationale]
34
+
35
+ ## Next Steps
36
+ 1. [Ordered list of what should happen next]
37
+
38
+ ## Critical Context
39
+ - [Any data, examples, or references needed to continue]
40
+ - [Or "(none)" if not applicable]
41
+
42
+ Keep each section concise. Preserve exact file paths, function names, and error messages.`;
43
+
44
+ const UPDATE_SUMMARIZATION_PROMPT = `The messages above are NEW conversation messages to incorporate into the existing summary provided in <previous-summary> tags.
45
+
46
+ Update the existing structured summary with new information. RULES:
47
+ - PRESERVE all existing information from the previous summary
48
+ - ADD new progress, decisions, and context from the new messages
49
+ - UPDATE the Progress section: move items from "In Progress" to "Done" when completed
50
+ - UPDATE "Next Steps" based on what was accomplished
51
+ - PRESERVE exact file paths, function names, and error messages
52
+ - If something is no longer relevant, you may remove it
53
+
54
+ Use this EXACT format:
55
+
56
+ ## Goal
57
+ [Preserve existing goals, add new ones if the task expanded]
58
+
59
+ ## Constraints & Preferences
60
+ - [Preserve existing, add new ones discovered]
61
+
62
+ ## Progress
63
+ ### Done
64
+ - [x] [Include previously done items AND newly completed items]
65
+
66
+ ### In Progress
67
+ - [ ] [Current work - update based on progress]
68
+
69
+ ### Blocked
70
+ - [Current blockers - remove if resolved]
71
+
72
+ ## Key Decisions
73
+ - **[Decision]**: [Brief rationale] (preserve all previous, add new)
74
+
75
+ ## Next Steps
76
+ 1. [Update based on current state]
77
+
78
+ ## Critical Context
79
+ - [Preserve important context, add new if needed]
80
+
81
+ Keep each section concise. Preserve exact file paths, function names, and error messages.`;
82
+
83
+ const TURN_PREFIX_SUMMARIZATION_PROMPT = `This is the PREFIX of a turn that was too large to keep. The SUFFIX (recent work) is retained.
84
+
85
+ Summarize the prefix to provide context for the retained suffix:
86
+
87
+ ## Original Request
88
+ [What did the user ask for in this turn?]
89
+
90
+ ## Early Progress
91
+ - [Key decisions and work done in the prefix]
92
+
93
+ ## Context for Suffix
94
+ - [Information needed to understand the retained recent work]
95
+
96
+ Be concise. Focus on what's needed to understand the kept suffix.`;
97
+
98
+ const BRANCH_SUMMARY_PREAMBLE = `The user explored a different conversation branch before returning here.
99
+ Summary of that exploration:
100
+
101
+ `;
102
+
103
+ const BRANCH_SUMMARY_PROMPT = `Create a structured summary of this conversation branch for context when returning later.
104
+
105
+ Use this EXACT format:
106
+
107
+ ## Goal
108
+ [What was the user trying to accomplish in this branch?]
109
+
110
+ ## Constraints & Preferences
111
+ - [Any constraints, preferences, or requirements mentioned]
112
+ - [Or "(none)" if none were mentioned]
113
+
114
+ ## Progress
115
+ ### Done
116
+ - [x] [Completed tasks/changes]
117
+
118
+ ### In Progress
119
+ - [ ] [Work that was started but not finished]
120
+
121
+ ### Blocked
122
+ - [Issues preventing progress, if any]
123
+
124
+ ## Key Decisions
125
+ - **[Decision]**: [Brief rationale]
126
+
127
+ ## Next Steps
128
+ 1. [What should happen next to continue this work]
129
+
130
+ Keep each section concise. Preserve exact file paths, function names, and error messages.`;
131
+
132
+ const DEFAULT_COMPACTION_SETTINGS = {
133
+ enabled: true,
134
+ reserveTokens: 16384,
135
+ keepRecentTokens: 20000,
136
+ };
137
+
138
+ const MODEL_CONTEXT_LIMITS = {
139
+ 'deepseek-chat': 100000,
140
+ 'deepseek-coder': 100000,
141
+ 'deepseek-v4-pro': 800000,
142
+ 'deepseek-v4-flash': 800000,
143
+ 'deepseek-reasoner': 100000,
144
+ 'MiniMax-M2.7': 100000,
145
+ 'gpt-4': 100000,
146
+ 'gpt-4o': 100000,
147
+ 'gpt-4o-mini': 100000,
148
+ 'gpt-4-turbo': 100000,
149
+ 'claude-3-5-sonnet': 110000,
150
+ 'claude-3-opus': 110000,
151
+ 'claude-3-sonnet': 110000,
152
+ 'claude-3-haiku': 110000,
153
+ 'claude-4-sonnet': 110000,
154
+ 'claude-4-opus': 110000,
155
+ 'glm-5.1': 110000,
156
+ };
157
+
158
+ const COMPRESSION_TIMEOUT_MS = 120000;
159
+
160
+ module.exports = {
161
+ SUMMARIZATION_SYSTEM_PROMPT,
162
+ SUMMARIZATION_PROMPT,
163
+ UPDATE_SUMMARIZATION_PROMPT,
164
+ TURN_PREFIX_SUMMARIZATION_PROMPT,
165
+ BRANCH_SUMMARY_PREAMBLE,
166
+ BRANCH_SUMMARY_PROMPT,
167
+ DEFAULT_COMPACTION_SETTINGS,
168
+ MODEL_CONTEXT_LIMITS,
169
+ COMPRESSION_TIMEOUT_MS,
170
+ };