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.
- 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/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/{core → common}/constants.js +0 -0
- /package/src/{utils/edit-diff.js → common/diff.js} +0 -0
- /package/src/{utils/event-emitter.js → common/events.js} +0 -0
- /package/src/{utils → common}/id.js +0 -0
- /package/src/{utils → common}/retry.js +0 -0
- /package/src/{core/notification-manager.js → notification/manager.js} +0 -0
- /package/src/{core/session-entry.js → session/entry.js} +0 -0
- /package/src/{core/storage-manager.js → storage/manager.js} +0 -0
|
@@ -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('./
|
|
2
|
-
const { cleanResponse } = require('
|
|
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
|
+
};
|