foliko 1.1.92 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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/{core → common}/constants.js +3 -0
  80. package/src/common/errors.js +402 -0
  81. package/src/{utils → common}/logger.js +33 -0
  82. package/src/{utils/chat-queue.js → common/queue.js} +2 -2
  83. package/src/config/plugin-config.js +50 -0
  84. package/src/context/agent.js +32 -0
  85. package/src/context/compaction-prompts.js +170 -0
  86. package/src/context/compaction-utils.js +191 -0
  87. package/src/context/compressor.js +413 -0
  88. package/src/context/index.js +9 -0
  89. package/src/{core/context-manager.js → context/manager.js} +1 -1
  90. package/src/context/request.js +50 -0
  91. package/src/context/session.js +33 -0
  92. package/src/context/storage.js +30 -0
  93. package/src/executors/mcp-client.js +153 -0
  94. package/src/executors/mcp-desc.js +236 -0
  95. package/src/executors/mcp-executor.js +91 -956
  96. package/src/{core → framework}/command-registry.js +1 -1
  97. package/src/framework/framework.js +300 -0
  98. package/src/framework/index.js +18 -0
  99. package/src/framework/lifecycle.js +203 -0
  100. package/src/framework/loader.js +78 -0
  101. package/src/framework/registry.js +86 -0
  102. package/src/{core/ui-extension-context.js → framework/ui-extension.js} +1 -1
  103. package/src/index.js +130 -15
  104. package/src/llm/index.js +26 -0
  105. package/src/llm/provider.js +212 -0
  106. package/src/llm/registry.js +11 -0
  107. package/src/{core/token-counter.js → llm/tokens.js} +4 -37
  108. package/src/{core/plugin-base.js → plugin/base.js} +10 -136
  109. package/src/plugin/index.js +14 -0
  110. package/src/plugin/loader.js +101 -0
  111. package/src/plugin/manager.js +261 -0
  112. package/src/{core → session}/branch-summary-auto.js +2 -2
  113. package/src/{core/chat-session.js → session/chat.js} +2 -2
  114. package/src/session/index.js +7 -0
  115. package/src/{core/session-manager.js → session/session.js} +2 -2
  116. package/src/session/ttl.js +92 -0
  117. package/src/{core/jsonl-storage.js → storage/jsonl.js} +1 -1
  118. package/src/tool/executor.js +85 -0
  119. package/src/tool/index.js +15 -0
  120. package/src/tool/registry.js +143 -0
  121. package/src/{core/tool-router.js → tool/router.js} +17 -124
  122. package/src/tool/schema.js +108 -0
  123. package/src/utils/data-splitter.js +1 -1
  124. package/src/utils/download.js +1 -1
  125. package/src/utils/index.js +6 -6
  126. package/src/utils/message-validator.js +1 -1
  127. package/tests/core/context-storage.test.js +46 -0
  128. package/tests/core/llm.test.js +54 -0
  129. package/tests/core/plugin.test.js +42 -0
  130. package/tests/core/tool.test.js +60 -0
  131. package/tests/setup.js +10 -0
  132. package/tests/smoke.test.js +58 -0
  133. package/vitest.config.js +9 -0
  134. package/cli/src/daemon.js +0 -149
  135. package/docs/CONTEXT_DESIGN.md +0 -1596
  136. package/docs/ai-sdk-optimization.md +0 -655
  137. package/docs/features.md +0 -120
  138. package/docs/qq-bot.md +0 -976
  139. package/docs/quick-reference.md +0 -160
  140. package/docs/user-manual.md +0 -1391
  141. package/images/geometric_shapes.jpg +0 -0
  142. package/images/sunset_mountain_lake.jpg +0 -0
  143. package/skills/poster-guide/SKILL.md +0 -792
  144. package/src/capabilities/index.js +0 -11
  145. package/src/core/agent.js +0 -808
  146. package/src/core/context-compressor.js +0 -959
  147. package/src/core/enhanced-context-compressor.js +0 -210
  148. package/src/core/framework.js +0 -1422
  149. package/src/core/index.js +0 -30
  150. package/src/core/plugin-manager.js +0 -961
  151. package/src/core/provider-registry.js +0 -159
  152. package/src/core/provider.js +0 -156
  153. package/src/core/request-context.js +0 -98
  154. package/src/core/subagent.js +0 -442
  155. package/src/core/system-prompt-builder.js +0 -120
  156. package/src/core/tool-executor.js +0 -202
  157. package/src/core/tool-registry.js +0 -517
  158. package/src/core/worker-agent.js +0 -192
  159. package/src/executors/executor-base.js +0 -58
  160. package/src/utils/error-boundary.js +0 -363
  161. package/src/utils/error.js +0 -374
  162. package/system.md +0 -1645
  163. package/website_v2/README.md +0 -57
  164. package/website_v2/SPEC.md +0 -1
  165. package/website_v2/docs/api.html +0 -128
  166. package/website_v2/docs/configuration.html +0 -147
  167. package/website_v2/docs/plugin-development.html +0 -129
  168. package/website_v2/docs/project-structure.html +0 -89
  169. package/website_v2/docs/skill-development.html +0 -85
  170. package/website_v2/index.html +0 -489
  171. package/website_v2/scripts/main.js +0 -93
  172. package/website_v2/styles/animations.css +0 -8
  173. package/website_v2/styles/docs.css +0 -83
  174. package/website_v2/styles/main.css +0 -417
  175. package/xhs_auth.json +0 -268
  176. package//346/265/267/346/212/245/346/217/222/344/273/266.md +0 -621
  177. /package/plugins/{ambient-agent → ambient}/constants.js +0 -0
  178. /package/plugins/{email → messaging/email}/constants.js +0 -0
  179. /package/plugins/{email → messaging/email}/handlers.js +0 -0
  180. /package/plugins/{email → messaging/email}/monitor.js +0 -0
  181. /package/plugins/{email → messaging/email}/parser.js +0 -0
  182. /package/plugins/{email → messaging/email}/reply.js +0 -0
  183. /package/plugins/{email → messaging/email}/utils.js +0 -0
  184. /package/{examples → sandbox}/test-chat.js +0 -0
  185. /package/{examples → sandbox}/test-mcp.js +0 -0
  186. /package/{examples → sandbox}/test-reload.js +0 -0
  187. /package/{examples → sandbox}/test-telegram.js +0 -0
  188. /package/{examples → sandbox}/test-tg-bot.js +0 -0
  189. /package/{examples → sandbox}/test-tg-simple.js +0 -0
  190. /package/{examples → sandbox}/test-tg.js +0 -0
  191. /package/{examples → sandbox}/test-think.js +0 -0
  192. /package/src/{core/sub-agent-config.js → agent/sub-config.js} +0 -0
  193. /package/{cli/src → src/cli}/commands/daemon.js +0 -0
  194. /package/{cli/src → src/cli}/commands/list.js +0 -0
  195. /package/{cli/src → src/cli}/commands/plugin.js +0 -0
  196. /package/{cli/src → src/cli}/ui/components/agent-mention-provider.js +0 -0
  197. /package/{cli/src → src/cli}/ui/components/chained-autocomplete-provider.js +0 -0
  198. /package/{cli/src → src/cli}/ui/components/message-bubble.js +0 -0
  199. /package/{cli/src → src/cli}/ui/components/status-bar.js +0 -0
  200. /package/{cli/src → src/cli}/utils/ansi.js +0 -0
  201. /package/{cli/src → src/cli}/utils/config.js +0 -0
  202. /package/{cli/src → src/cli}/utils/markdown.js +0 -0
  203. /package/{cli/src → src/cli}/utils/plugin-config.js +0 -0
  204. /package/{cli/src → src/cli}/utils/render-diff.js +0 -0
  205. /package/src/{utils/circuit-breaker.js → common/circuit.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
@@ -3,7 +3,7 @@
3
3
  * 允许插件扩展 TUI 界面
4
4
  */
5
5
 
6
- const { logger } = require('../utils/logger');
6
+ const { logger } = require('../common/logger');
7
7
 
8
8
  class ExtensionUIContext {
9
9
  /**
package/src/index.js CHANGED
@@ -1,25 +1,140 @@
1
1
  /**
2
2
  * Foliko Framework
3
3
  * 简约的插件化 Agent 框架
4
+ *
5
+ * Public API Facade
6
+ * 提供简洁的入口函数,覆盖 80% 使用场景
4
7
  */
5
8
 
6
- const { Framework, Agent, Plugin, PluginManager, ToolRegistry, EventEmitter } = require('./core');
9
+ 'use strict';
7
10
 
8
- const { SkillManagerPlugin, WorkflowPlugin } = require('./capabilities');
11
+ // 加载工作目录 .env 配置(优先于系统环境变量)
12
+ require('dotenv').config({ override: true });
9
13
 
10
- const { MCPExecutorPlugin } = require('./executors/mcp-executor');
14
+ // ============================================================
15
+ // Framework
16
+ // ============================================================
17
+ const { Framework } = require('./framework/framework');
18
+ const { bootstrap, ready, destroy } = require('./framework/lifecycle');
11
19
 
12
- module.exports = {
13
- // 核心
14
- Framework,
15
- Agent,
16
- Plugin,
17
- PluginManager,
18
- ToolRegistry,
19
- EventEmitter,
20
+ // ============================================================
21
+ // Agent
22
+ // ============================================================
23
+ const { createAgent, MainAgent, SubAgent, WorkerAgent } = require('./agent');
20
24
 
21
- // 能力插件
22
- SkillManagerPlugin,
23
- WorkflowPlugin,
24
- MCPExecutorPlugin,
25
+ // ============================================================
26
+ // Plugin
27
+ // ============================================================
28
+ const { Plugin, PluginManager } = require('./plugin');
29
+
30
+ // ============================================================
31
+ // Tool
32
+ // ============================================================
33
+ const { ToolRegistry, ToolExecutor } = require('./tool');
34
+
35
+ // ============================================================
36
+ // LLM
37
+ // ============================================================
38
+ const llm = require('./llm');
39
+
40
+ // ============================================================
41
+ // Common
42
+ // ============================================================
43
+ const { EventEmitter } = require('./common/events');
44
+ const { Logger } = require('./common/logger');
45
+
46
+ // ============================================================
47
+ // Public API
48
+ // ============================================================
49
+
50
+ // --- 入门三件套 ---
51
+
52
+ /** 加载 .foliko 配置并启动框架 */
53
+ exports.bootstrap = async (config = {}) => {
54
+ const fw = new Framework(config);
55
+ await bootstrap(fw, config);
56
+ return fw;
57
+ };
58
+
59
+ /** 构造一个未启动的 Framework(支持链式 .use()) */
60
+ exports.createApp = (config = {}) => {
61
+ const fw = new Framework(config);
62
+ fw.use = (plugin) => { fw.loadPlugin(plugin); return fw; };
63
+ return fw;
64
+ };
65
+
66
+ /** 显式构造 Agent */
67
+ exports.createAgent = createAgent;
68
+
69
+ // --- 框架生命周期 ---
70
+
71
+ exports.ready = ready;
72
+ exports.destroy = destroy;
73
+
74
+ // --- 上下文管理 ---
75
+
76
+ exports.runInSession = (framework, sessionId, options, fn) =>
77
+ framework.runInSession(sessionId, options, fn);
78
+
79
+ exports.runInRequest = (framework, context, fn) =>
80
+ framework.runWithRequestContext(context, fn);
81
+
82
+ // --- 快捷工具注册 ---
83
+
84
+ exports.registerTool = (framework, toolDef) => {
85
+ framework.registerTool(toolDef);
86
+ return framework;
87
+ };
88
+
89
+ exports.use = (app, plugin) => {
90
+ if (app?.loadPlugin) return app.loadPlugin(plugin);
91
+ throw new Error('use() requires a Framework instance as first argument');
92
+ };
93
+
94
+ // --- 快捷插件工厂 ---
95
+
96
+ exports.withAI = (config) => {
97
+ const AIPlugin = require('../plugins/core/ai');
98
+ return new AIPlugin(config);
99
+ };
100
+
101
+ exports.withShell = (config = {}) => {
102
+ const ShellPlugin = require('../plugins/executors/shell');
103
+ return new ShellPlugin(config);
25
104
  };
105
+
106
+ exports.withPython = (config = {}) => {
107
+ const PythonPlugin = require('../plugins/executors/python');
108
+ return new PythonPlugin(config);
109
+ };
110
+
111
+ // --- 类型/常量 ---
112
+
113
+ exports.z = require('zod');
114
+ exports.LLM = llm;
115
+
116
+ // --- 框架核心类(用于扩展) ---
117
+
118
+ exports.Framework = Framework;
119
+ exports.MainAgent = MainAgent;
120
+ exports.SubAgent = SubAgent;
121
+ exports.WorkerAgent = WorkerAgent;
122
+ exports.ToolRegistry = ToolRegistry;
123
+ exports.ToolExecutor = ToolExecutor;
124
+ exports.Plugin = Plugin;
125
+ exports.PluginManager = PluginManager;
126
+ exports.EventEmitter = EventEmitter;
127
+ exports.Logger = Logger;
128
+
129
+ // --- 向后兼容(旧路径转发) ---
130
+
131
+ const { SkillManagerPlugin, Skill, SkillMetadata } = require('../plugins/core/skill-manager');
132
+ const { WorkflowPlugin, WorkflowEngine, StepExecutor } = require('../plugins/core/workflow');
133
+ exports.SkillManagerPlugin = SkillManagerPlugin;
134
+ exports.Skill = Skill;
135
+ exports.SkillMetadata = SkillMetadata;
136
+ exports.WorkflowPlugin = WorkflowPlugin;
137
+ exports.WorkflowEngine = WorkflowEngine;
138
+ exports.StepExecutor = StepExecutor;
139
+ exports.Agent = MainAgent;
140
+ exports.MCPExecutorPlugin = require('./executors/mcp-executor');
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ createAI, createModel, getAvailableProviders,
5
+ DEFAULT_PROVIDERS, isThinkingModel,
6
+ LLMProvider, ProviderRegistry, getProviderRegistry,
7
+ } = require('./provider');
8
+ const { TokenCounter, encode, estimateTokens, estimateContextTokens, shouldCompact } = require('./tokens');
9
+
10
+ module.exports = {
11
+ // Provider
12
+ LLMProvider,
13
+ ProviderRegistry,
14
+ getProviderRegistry,
15
+ createAI,
16
+ createModel,
17
+ getAvailableProviders,
18
+ DEFAULT_PROVIDERS,
19
+ isThinkingModel,
20
+ // Token
21
+ TokenCounter,
22
+ encode,
23
+ estimateTokens,
24
+ estimateContextTokens,
25
+ shouldCompact,
26
+ };
@@ -0,0 +1,212 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * LLM Provider 系统
5
+ * 统一管理 Provider 注册、模型创建、thinking mode 判断
6
+ * 合并自 provider.js + registry.js
7
+ * 目标 ~300 行
8
+ */
9
+
10
+ const { createOpenAICompatible } = require('@ai-sdk/openai-compatible');
11
+
12
+ // ==================== Provider 默认配置 ====================
13
+
14
+ const DEFAULT_PROVIDERS = {
15
+ openai: {
16
+ name: 'OpenAI',
17
+ baseURL: 'https://api.openai.com/v1',
18
+ defaultModel: 'gpt-4o',
19
+ },
20
+ ollama: {
21
+ name: 'Ollama',
22
+ baseURL: 'http://localhost:11434/v1',
23
+ defaultModel: 'llama3',
24
+ },
25
+ lmstudio: {
26
+ name: 'LM Studio',
27
+ baseURL: 'http://localhost:1234/v1',
28
+ defaultModel: 'local-model',
29
+ },
30
+ deepseek: {
31
+ name: 'DeepSeek',
32
+ baseURL: 'https://api.deepseek.com/v1',
33
+ defaultModel: 'deepseek-chat',
34
+ },
35
+ anthropic: {
36
+ name: 'Anthropic',
37
+ baseURL: 'https://api.anthropic.com/v1',
38
+ defaultModel: 'claude-sonnet-4-20250514',
39
+ },
40
+ minimax: {
41
+ name: 'MiniMax',
42
+ baseURL: 'https://api.minimaxi.com/v1',
43
+ defaultModel: 'MiniMax-M2.7',
44
+ },
45
+ };
46
+
47
+ const THINKING_MODELS = [
48
+ 'deepseek-v4-pro',
49
+ 'deepseek-v4-flash',
50
+ 'deepseek-reasoner',
51
+ ];
52
+
53
+ function isThinkingModel(model) {
54
+ if (!model) return false;
55
+ const lower = model.toLowerCase();
56
+ return THINKING_MODELS.some(tm => lower.includes(tm) || lower === tm);
57
+ }
58
+
59
+ // ==================== LLMProvider ====================
60
+
61
+ /**
62
+ * LLMProvider — Provider 配置封装
63
+ * 将 provider/model/apiKey/baseURL 配置封装为对象,提供 createModel() 工厂
64
+ */
65
+ class LLMProvider {
66
+ constructor(config = {}) {
67
+ this.provider = config.provider || 'deepseek';
68
+ this.model = config.model || 'deepseek-chat';
69
+ this.apiKey = config.apiKey;
70
+ this.baseURL = config.baseURL;
71
+ this.options = { ...config.options };
72
+ }
73
+
74
+ /** 创建 AI SDK model 实例 */
75
+ createModel() {
76
+ return _createAIClient(this.provider, this.model, this.apiKey, this.baseURL)(this.model);
77
+ }
78
+
79
+ /** 当前模型是否 thinking mode */
80
+ isThinking() { return isThinkingModel(this.model); }
81
+
82
+ getConfig() {
83
+ return {
84
+ provider: this.provider,
85
+ model: this.model,
86
+ apiKey: this.apiKey,
87
+ baseURL: this.baseURL,
88
+ options: { ...this.options },
89
+ };
90
+ }
91
+ }
92
+
93
+ // ==================== ProviderRegistry ====================
94
+
95
+ class ProviderRegistry {
96
+ constructor() {
97
+ this._providers = new Map();
98
+ this._models = new Map();
99
+ }
100
+
101
+ registerProvider(name, config) {
102
+ this._providers.set(name, {
103
+ name: config.name || name,
104
+ baseURL: config.baseURL,
105
+ apiKey: config.apiKey || '',
106
+ defaultModel: config.defaultModel || '',
107
+ headers: config.headers || {},
108
+ });
109
+ if (config.defaultModel) this.registerModel(config.defaultModel, name);
110
+ return this;
111
+ }
112
+
113
+ registerModel(modelId, provider, options = {}) {
114
+ this._models.set(modelId, { provider, ...options });
115
+ return this;
116
+ }
117
+
118
+ getProvider(name) { return this._providers.get(name); }
119
+ getModel(modelId) { return this._models.get(modelId); }
120
+ getProviderNames() { return Array.from(this._providers.keys()); }
121
+ hasProvider(name) { return this._providers.has(name); }
122
+
123
+ /** 创建 AI SDK model 实例(自动 fallback 到注册的默认值) */
124
+ createClient(providerName, model, apiKey, baseURL) {
125
+ const provider = this._providers.get(providerName);
126
+ if (!provider) throw new Error(`Provider '${providerName}' not registered.`);
127
+ const finalApiKey = apiKey || provider.apiKey;
128
+ const finalBaseURL = baseURL || provider.baseURL;
129
+ const finalModel = model || provider.defaultModel;
130
+ if (!finalApiKey) throw new Error(`No API key for provider '${providerName}'`);
131
+ return _createAIClient(providerName, finalModel, finalApiKey, finalBaseURL)(finalModel);
132
+ }
133
+
134
+ unregisterProvider(name) {
135
+ this._providers.delete(name);
136
+ for (const [mid, mc] of this._models) {
137
+ if (mc.provider === name) this._models.delete(mid);
138
+ }
139
+ return this;
140
+ }
141
+ }
142
+
143
+ // ==================== 模块级单例 ====================
144
+
145
+ const _registry = new ProviderRegistry();
146
+
147
+ function getProviderRegistry() { return _registry; }
148
+
149
+ function _registerDefaultProviders() {
150
+ for (const [key, cfg] of Object.entries(DEFAULT_PROVIDERS)) {
151
+ _registry.registerProvider(key, {
152
+ name: cfg.name, baseURL: cfg.baseURL, defaultModel: cfg.defaultModel,
153
+ });
154
+ }
155
+ }
156
+ _registerDefaultProviders();
157
+
158
+ // ==================== 内部 AI SDK 客户端工厂 ====================
159
+
160
+ function _createAIClient(provider, model, apiKey, baseURL) {
161
+ const providerName = (provider || 'deepseek').toLowerCase();
162
+ const pcfg = DEFAULT_PROVIDERS[providerName];
163
+
164
+ if (pcfg) {
165
+ const streamOptions = { includeUsage: true };
166
+ if (providerName === 'deepseek') streamOptions.includeReasoningContent = true;
167
+ return createOpenAICompatible({
168
+ name: pcfg.name,
169
+ baseURL: baseURL || pcfg.baseURL,
170
+ apiKey: apiKey || 'dummy-key',
171
+ headers: provider === 'anthropic' ? { 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' } : undefined,
172
+ models: { default: { id: model || 'deepseek-chat', streamOptions } },
173
+ });
174
+ }
175
+
176
+ return createOpenAICompatible({
177
+ name: providerName || 'Custom',
178
+ baseURL,
179
+ apiKey: apiKey || 'dummy-key',
180
+ models: { default: { id: model || 'gpt-4' } },
181
+ });
182
+ }
183
+
184
+ // ==================== 向后兼容 API ====================
185
+
186
+ function createAI(config) {
187
+ return _createAIClient(config.provider, config.model, config.apiKey, config.baseURL);
188
+ }
189
+
190
+ function createModel(model, client) { return client(model); }
191
+
192
+ function getAvailableProviders() {
193
+ return Object.entries(DEFAULT_PROVIDERS).map(([k, v]) => ({ id: k, name: v.name, baseURL: v.baseURL }));
194
+ }
195
+
196
+ function registerProvider(name, config) { _registry.registerProvider(name, config); }
197
+
198
+ module.exports = {
199
+ // Class API
200
+ LLMProvider,
201
+ ProviderRegistry,
202
+ getProviderRegistry,
203
+ // 常量 & 工具函数
204
+ DEFAULT_PROVIDERS,
205
+ THINKING_MODELS,
206
+ isThinkingModel,
207
+ // 向后兼容
208
+ createAI,
209
+ createModel,
210
+ getAvailableProviders,
211
+ registerProvider,
212
+ };
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * ProviderRegistry — 向后兼容转发
5
+ * 实际实现在 provider.js
6
+ * @deprecated 直接引用 src/llm/provider
7
+ */
8
+
9
+ const { ProviderRegistry, getProviderRegistry, registerProvider } = require('./provider');
10
+
11
+ module.exports = { ProviderRegistry, getProviderRegistry, registerProvider };
@@ -1,33 +1,22 @@
1
+ 'use strict';
2
+
1
3
  /**
2
4
  * TokenCounter - Token 计算工具
3
5
  * Ported from pi's compaction/compaction.ts with enhancements
4
6
  */
5
7
 
6
- /**
7
- * 简单的中英文混合 tokenizer
8
- * 粗略估计:中文每个字符 2 字节,英文每个单词约 1.5 字节
9
- * @param {string} text - 文本
10
- * @param {number} bytesPerToken - 每 token 字节数,默认 4
11
- * @returns {number} token 数量
12
- */
13
8
  function encode(text, bytesPerToken = 4) {
14
9
  if (!text) return 0;
15
10
  const bytes = Buffer.byteLength(String(text), 'utf8');
16
11
  return Math.ceil(bytes / bytesPerToken);
17
12
  }
18
13
 
19
- /**
20
- * 计算 JSON 字符串的 token 数量
21
- */
22
14
  function encodeForJSON(text) {
23
15
  if (!text) return 0;
24
16
  const encoded = encode(text);
25
17
  return encoded + Math.ceil(Buffer.byteLength(JSON.stringify(text), 'utf8') / 100);
26
18
  }
27
19
 
28
- /**
29
- * Safe JSON stringify that handles unserializable values
30
- */
31
20
  function safeJsonStringify(value) {
32
21
  try {
33
22
  return JSON.stringify(value) ?? 'undefined';
@@ -36,10 +25,6 @@ function safeJsonStringify(value) {
36
25
  }
37
26
  }
38
27
 
39
- /**
40
- * Estimate token count for one message using a conservative character heuristic
41
- * Ported from pi
42
- */
43
28
  function estimateTokens(message) {
44
29
  let chars = 0;
45
30
 
@@ -100,16 +85,10 @@ function estimateTokens(message) {
100
85
  return 0;
101
86
  }
102
87
 
103
- /**
104
- * Calculate total context tokens from provider usage
105
- */
106
88
  function calculateContextTokens(usage) {
107
89
  return usage.totalTokens || (usage.input || 0) + (usage.output || 0) + (usage.cacheRead || 0) + (usage.cacheWrite || 0);
108
90
  }
109
91
 
110
- /**
111
- * Get assistant usage from message
112
- */
113
92
  function getAssistantUsage(msg) {
114
93
  if (msg.role === 'assistant' && msg.usage) {
115
94
  if (msg.stopReason !== 'aborted' && msg.stopReason !== 'error' && msg.usage) {
@@ -119,9 +98,6 @@ function getAssistantUsage(msg) {
119
98
  return undefined;
120
99
  }
121
100
 
122
- /**
123
- * Return usage from the last successful assistant message in session entries
124
- */
125
101
  function getLastAssistantUsage(entries) {
126
102
  for (let i = entries.length - 1; i >= 0; i--) {
127
103
  const entry = entries[i];
@@ -133,9 +109,6 @@ function getLastAssistantUsage(entries) {
133
109
  return undefined;
134
110
  }
135
111
 
136
- /**
137
- * Get last assistant usage info from messages
138
- */
139
112
  function getLastAssistantUsageInfo(messages) {
140
113
  for (let i = messages.length - 1; i >= 0; i--) {
141
114
  const usage = getAssistantUsage(messages[i]);
@@ -144,9 +117,6 @@ function getLastAssistantUsageInfo(messages) {
144
117
  return undefined;
145
118
  }
146
119
 
147
- /**
148
- * Estimate context tokens for messages using provider usage when available
149
- */
150
120
  function estimateContextTokens(messages) {
151
121
  const usageInfo = getLastAssistantUsageInfo(messages);
152
122
 
@@ -177,9 +147,6 @@ function estimateContextTokens(messages) {
177
147
  };
178
148
  }
179
149
 
180
- /**
181
- * Return whether context usage exceeds the configured compaction threshold
182
- */
183
150
  function shouldCompact(contextTokens, contextWindow, settings) {
184
151
  if (!settings.enabled) return false;
185
152
  return contextTokens > contextWindow - settings.reserveTokens;
@@ -305,5 +272,5 @@ module.exports = {
305
272
  getLastAssistantUsage,
306
273
  estimateContextTokens,
307
274
  shouldCompact,
308
- safeJsonStringify
309
- };
275
+ safeJsonStringify,
276
+ };