foliko 2.0.21 → 2.0.23

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 (42) hide show
  1. package/.editorconfig +56 -56
  2. package/.lintstagedrc +7 -7
  3. package/.prettierignore +29 -29
  4. package/.prettierrc +11 -11
  5. package/Dockerfile +63 -63
  6. package/install.ps1 +129 -129
  7. package/install.sh +121 -121
  8. package/package.json +3 -5
  9. package/plugins/core/skill-manager/index.js +34 -23
  10. package/plugins/core/sub-agent/index.js +103 -14
  11. package/plugins/core/workflow/context.js +941 -941
  12. package/plugins/core/workflow/engine.js +66 -66
  13. package/plugins/core/workflow/js-runner.js +318 -318
  14. package/plugins/core/workflow/json-runner.js +323 -323
  15. package/plugins/core/workflow/stages/choice.js +74 -74
  16. package/plugins/core/workflow/stages/each.js +123 -123
  17. package/plugins/core/workflow/stages/parallel.js +69 -69
  18. package/plugins/executors/extension/extension-registry.js +72 -1
  19. package/plugins/executors/extension/index.js +68 -9
  20. package/plugins/io/file-system/index.js +377 -153
  21. package/skills/find-skills/SKILL.md +133 -133
  22. package/src/agent/chat.js +207 -222
  23. package/src/agent/sub.js +29 -26
  24. package/src/agent/tool-loop.js +648 -0
  25. package/src/llm/provider.js +12 -28
  26. package/src/plugin/base.js +17 -14
  27. package/src/plugin/manager.js +19 -0
  28. package/src/tool/router.js +2 -2
  29. package/src/utils/message-validator.js +186 -0
  30. package/tests/core/chat-tool.test.js +187 -0
  31. package/tests/core/disable-thinking.test.js +64 -0
  32. package/tests/core/edit-file.test.js +194 -0
  33. package/tests/core/ext-call-empty-args.test.js +136 -0
  34. package/tests/core/reasoning-content.test.js +129 -0
  35. package/tests/core/sanitize-for-llm.test.js +152 -0
  36. package/tests/core/search.test.js +212 -0
  37. package/tests/core/skill-input-schema.test.js +150 -0
  38. package/tests/core/strip-stale-tool-calls.test.js +154 -0
  39. package/tests/core/sub-agent-parse.test.js +247 -0
  40. package/tests/core/sub-agent-skills.test.js +197 -0
  41. package/tests/core/tool-loop.test.js +208 -0
  42. package/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.jsonl +0 -76
package/src/agent/sub.js CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  const { BaseAgent } = require('./base');
10
10
  const { cleanResponse } = require('../utils');
11
- const { generateText, tool, stepCountIs, ToolLoopAgent } = require('ai');
11
+ const { ToolLoop } = require('./tool-loop');
12
12
  const { z } = require('zod');
13
13
  const { logger } = require('../common/logger');
14
14
  const { validateAll } = require('../utils/message-validator');
@@ -30,7 +30,7 @@ class SubAgent extends BaseAgent {
30
30
  this.defaulTools = ['ext_skill', 'skill_load', 'ext_call', 'subagent_call'];
31
31
  this.bindTools = {
32
32
  read: 'read_file', write: 'write_file', edit: 'modify_file',
33
- glob: 'read_directory', grep: 'search_file', bash: 'shell_exec',
33
+ glob: 'read_directory', grep: 'search', bash: 'shell_exec',
34
34
  };
35
35
 
36
36
  this._customSystemPrompt = config.systemPrompt || null;
@@ -61,36 +61,40 @@ class SubAgent extends BaseAgent {
61
61
  addTool(toolDef) { this.tools[toolDef.name] = toolDef; return this; }
62
62
  getTools() { return Object.values(this.tools); }
63
63
 
64
- _getAIProvider() {
65
- const { createAI } = require('../llm/provider');
66
- // 优先从 framework 的 ai 插件拿最新配置(避免构造时拿到 stale apiKey)
64
+ _getOpenAIClient() {
65
+ const { LLMProvider } = require('../llm/provider');
66
+ // 优先从 framework 的 ai 插件拿最新配置
67
67
  const aiPlugin = this.framework?.pluginManager?.get('ai');
68
68
  const aiConfig = aiPlugin ? aiPlugin.getConfig() : {};
69
- return createAI({
69
+ const provider = new LLMProvider({
70
70
  provider: aiConfig.provider || this.provider,
71
71
  model: aiConfig.model || this.model,
72
72
  apiKey: aiConfig.apiKey || this.apiKey,
73
73
  baseURL: aiConfig.baseURL || this.baseURL,
74
74
  });
75
+ return provider.createModel();
75
76
  }
76
77
 
77
- _buildAITools() {
78
- const tools = {};
79
- const all_tools = this.framework?.getTools() || [];
80
- let parentTools = [];
78
+ _buildToolMap() {
79
+ const map = {};
80
+ const allTools = this.framework?.getTools() || [];
81
81
  if (Array.isArray(this.parentTools)) {
82
- parentTools = this.parentTools.map(key => this.bindTools[key.toLocaleLowerCase()] || key);
83
- for (const toolName of parentTools) {
84
- const toolDef = all_tools.find(t => t.name === toolName);
85
- if (toolDef) tools[toolDef.name] = toolDef;
86
- }
87
- for (const tool of all_tools.filter(a => this.defaulTools.includes(a.name))) {
88
- tools[tool.name] = tool;
82
+ const parentToolNames = this.parentTools.map(key => this.bindTools[key.toLocaleLowerCase()] || key);
83
+ // 父工具 + 默认工具
84
+ const names = new Set([...parentToolNames, ...this.defaulTools]);
85
+ for (const tool of allTools) {
86
+ if (names.has(tool.name)) {
87
+ map[tool.name] = tool;
88
+ }
89
89
  }
90
90
  } else {
91
- all_tools.forEach(item => { tools[item.name] = item; });
91
+ allTools.forEach(t => { map[t.name] = t; });
92
+ }
93
+ // 覆盖 / 合并插件自身的工具
94
+ for (const [name, toolDef] of Object.entries(this.tools)) {
95
+ map[name] = toolDef;
92
96
  }
93
- return { ...tools, ...this.tools };
97
+ return map;
94
98
  }
95
99
 
96
100
  _buildSystemPrompt() {
@@ -130,26 +134,25 @@ class SubAgent extends BaseAgent {
130
134
  const maxSteps = options?.maxSteps || 30;
131
135
  const maxRetries = options?.maxRetries ?? this.maxRetries;
132
136
  const retryDelay = options?.retryDelay ?? this.retryDelay;
133
- const aiProvider = this._getAIProvider();
137
+ const client = this._getOpenAIClient();
134
138
 
135
139
  let messages = Array.isArray(taskOrMessages) ? [...taskOrMessages] : [{ role: 'user', content: taskOrMessages }];
136
140
 
137
141
  let lastError;
138
142
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
139
143
  try {
140
- const tools = this.disableTools ? {} : this._buildAITools();
144
+ const tools = this.disableTools ? {} : this._buildToolMap();
141
145
  const systemPrompt = this.disableTools ? this._customSystemPrompt : this._buildSystemPrompt();
142
146
  const validated = this._validateMessagesPairing(messages);
143
147
  if (validated.length !== messages.length) { messages.length = 0; messages.push(...validated); }
144
148
 
145
- const agent = new ToolLoopAgent({
146
- model: aiProvider(this.model),
147
- instructions: systemPrompt,
149
+ const loop = new ToolLoop({
148
150
  tools,
149
- stopWhen: stepCountIs(maxSteps),
151
+ maxSteps,
152
+ prepareStep: this._prepareStep ? this._prepareStep.bind(this) : undefined,
150
153
  });
151
154
 
152
- const result = await agent.generate({ messages, ...this.providerOptions, abortSignal: options.signal });
155
+ const result = await loop.generate({ messages, systemPrompt, model: this.model, client, ...this.providerOptions });
153
156
  messages.push(...result.response.messages);
154
157
  const full_text = cleanResponse(result.text);
155
158
  this.emit('complete', { message: full_text, steps: result.steps?.length || 0 });