foliko 2.0.2 → 2.0.3

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 (78) hide show
  1. package/README.md +6 -6
  2. package/docs/public-api.md +91 -0
  3. package/docs/system-prompt.md +219 -0
  4. package/docs/usage.md +6 -6
  5. package/package.json +1 -1
  6. package/plugins/ambient/ExplorerLoop.js +1 -0
  7. package/plugins/ambient/index.js +1 -0
  8. package/plugins/core/coordinator/index.js +10 -5
  9. package/plugins/core/default/bootstrap.js +21 -3
  10. package/plugins/core/default/config.js +12 -3
  11. package/plugins/core/python-loader/index.js +3 -3
  12. package/plugins/core/scheduler/index.js +26 -2
  13. package/plugins/core/skill-manager/index.js +198 -151
  14. package/plugins/core/sub-agent/index.js +34 -15
  15. package/plugins/core/think/index.js +1 -0
  16. package/plugins/core/workflow/index.js +5 -4
  17. package/plugins/executors/data-splitter/index.js +14 -1
  18. package/plugins/executors/extension/index.js +51 -37
  19. package/plugins/executors/python/index.js +3 -3
  20. package/plugins/executors/shell/index.js +6 -4
  21. package/plugins/io/web/index.js +2 -1
  22. package/plugins/memory/index.js +29 -74
  23. package/plugins/messaging/email/handlers.js +1 -19
  24. package/plugins/messaging/email/monitor.js +2 -17
  25. package/plugins/messaging/email/reply.js +2 -1
  26. package/plugins/messaging/email/utils.js +20 -1
  27. package/plugins/messaging/feishu/index.js +1 -1
  28. package/plugins/messaging/qq/index.js +1 -1
  29. package/plugins/messaging/telegram/index.js +1 -1
  30. package/plugins/messaging/weixin/index.js +1 -1
  31. package/plugins/plugin-manager/index.js +1 -33
  32. package/plugins/tools/index.js +14 -1
  33. package/skills/plugins/SKILL.md +316 -0
  34. package/skills/skill-guide/SKILL.md +5 -5
  35. package/skills/{subagent-guide → subagents}/SKILL.md +1 -1
  36. package/skills/{workflow-guide → workflows}/SKILL.md +3 -3
  37. package/skills/{workflow-troubleshooting → workflows/workflow-troubleshooting}/DEBUGGING.md +197 -197
  38. package/skills/{workflow-troubleshooting → workflows/workflow-troubleshooting}/SKILL.md +391 -391
  39. package/src/agent/base.js +5 -20
  40. package/src/agent/index.js +20 -8
  41. package/src/agent/main.js +100 -23
  42. package/src/agent/prompt-registry.js +296 -0
  43. package/src/agent/sub-config.js +1 -78
  44. package/src/agent/sub.js +19 -24
  45. package/src/cli/commands/plugin.js +1 -60
  46. package/src/cli/ui/chat-ui-old.js +1 -1
  47. package/src/cli/ui/chat-ui.js +1 -1
  48. package/src/cli/ui/components/agent-mention-provider.js +1 -1
  49. package/src/common/constants.js +42 -0
  50. package/src/common/id.js +13 -0
  51. package/src/common/queue.js +2 -2
  52. package/src/context/compressor.js +17 -9
  53. package/src/framework/framework.js +185 -0
  54. package/src/framework/index.js +1 -2
  55. package/src/framework/lifecycle.js +102 -1
  56. package/src/framework/loader.js +1 -55
  57. package/src/index.js +11 -2
  58. package/src/plugin/base.js +69 -55
  59. package/src/plugin/loader.js +1 -57
  60. package/src/session/entry.js +1 -11
  61. package/src/storage/manager.js +1 -12
  62. package/src/tool/executor.js +2 -1
  63. package/src/tool/router.js +5 -5
  64. package/src/utils/data-splitter.js +2 -1
  65. package/src/utils/index.js +150 -0
  66. package/src/utils/message-validator.js +21 -17
  67. package/src/utils/plugin-helpers.js +19 -5
  68. package/subagent.md +2 -2
  69. package/tests/core/plugin-prompts.test.js +219 -0
  70. package/tests/core/prompt-registry.test.js +209 -0
  71. package/src/cli/utils/plugin-config.js +0 -50
  72. package/src/config/plugin-config.js +0 -50
  73. /package/skills/{ambient-agent → ambient}/SKILL.md +0 -0
  74. /package/skills/{foliko-dev → foliko}/AGENTS.md +0 -0
  75. /package/skills/{foliko-dev → foliko}/SKILL.md +0 -0
  76. /package/skills/{mcp-usage → mcp}/SKILL.md +0 -0
  77. /package/skills/{plugin-guide → plugins-guide}/SKILL.md +0 -0
  78. /package/skills/{python-plugin-dev → python}/SKILL.md +0 -0
@@ -116,6 +116,7 @@ class ThinkPlugin extends Plugin {
116
116
  - 思考要深入、全面
117
117
  - 直接给出结论和见解`,
118
118
  disableTools: false,
119
+ hidden: true, // 隐藏子Agent,不在指令系统中显示
119
120
  })
120
121
  }
121
122
 
@@ -173,7 +173,7 @@ class StepExecutor {
173
173
  // 解析工具参数,支持变量引用
174
174
  let resolvedArgs = this._resolveArgs(step.args || {}, context);
175
175
  // 兼容旧格式:处理 step.input 字段(${stepId.output} 引用)
176
- // 将 input 注入到 args.input_data,供 python-execute 使用
176
+ // 将 input 注入到 args.input_data,供 py_execute 使用
177
177
  if (step.input) {
178
178
  const resolvedInput = this._resolveArgs(step.input, context);
179
179
  resolvedArgs.input_data = resolvedInput;
@@ -344,6 +344,7 @@ class StepExecutor {
344
344
  const messageAgent = this.framework.createSubAgent({
345
345
  name: 'workflow_message',
346
346
  role: '工作流任务执行助手,专注于处理工作流中的消息任务',
347
+ hidden: true
347
348
  });
348
349
  let content = step.content || '';
349
350
  // 支持变量引用
@@ -863,9 +864,9 @@ class WorkflowPlugin extends Plugin {
863
864
  start(framework) {
864
865
  this._loadWorkflows();
865
866
  this._registerWorkflowTools();
866
- // 注册 reloadWorkflows 工具
867
+ // 注册 workflow_reload 工具
867
868
  framework.registerTool({
868
- name: 'reloadWorkflows',
869
+ name: 'workflow_reload',
869
870
  description: '重载所有工作流,当用户添加或修改工作流后调用此工具',
870
871
  inputSchema: z.object({}),
871
872
  execute: async () => {
@@ -880,7 +881,7 @@ class WorkflowPlugin extends Plugin {
880
881
 
881
882
  // 注册列出工作流的工具
882
883
  framework.registerTool({
883
- name: 'listWorkflows',
884
+ name: 'workflow_list',
884
885
  description: '列出所有已加载的工作流及其描述信息',
885
886
  inputSchema: z.object({}),
886
887
  execute: async () => {
@@ -39,9 +39,22 @@ class DataSplitterPlugin extends Plugin {
39
39
  return this;
40
40
  }
41
41
 
42
+ // 声明式 prompt part
43
+ prompts = [
44
+ {
45
+ name: 'data-splitter-rules',
46
+ slot: 'BEHAVIOR',
47
+ description: '大工具结果自动分拆规则',
48
+ provider: function () {
49
+ return this._getPromptRules();
50
+ },
51
+ },
52
+ ];
53
+
42
54
  start(framework) {
55
+ super.start(framework);
43
56
  this._registerSplitTools();
44
- this.registerPromptPart('data-splitter-rules', 85, () => this._getPromptRules());
57
+ // prompt part 由基类 _autoRegisterPrompts 自动处理
45
58
  this._registerAutoSplitHook();
46
59
  }
47
60
 
@@ -12,6 +12,18 @@ const { zodSchemaToMarkdown, zodSchemaToTable } = require('@chnak/zod-to-markdow
12
12
  const log = logger.child('ExtensionExecutor');
13
13
 
14
14
  class ExtensionExecutorPlugin extends Plugin {
15
+ // 声明式 prompt parts
16
+ prompts = [
17
+ {
18
+ name: 'extension-tools',
19
+ slot: 'CAPABILITY',
20
+ description: '所有扩展工具的说明和使用方式',
21
+ provider: function () {
22
+ return this._buildExtensionsDescription();
23
+ },
24
+ },
25
+ ];
26
+
15
27
  constructor(config = {}) {
16
28
  super();
17
29
  this.name = 'extension-executor';
@@ -35,6 +47,8 @@ class ExtensionExecutorPlugin extends Plugin {
35
47
  this._rescanPluginTools(plugin);
36
48
  });
37
49
 
50
+ // 不监听 tool:registered — 直接 registerTool 注册的工具通过 AI SDK 暴露给 LLM,无需出现在 Extensions 中
51
+
38
52
  framework.on('framework:ready', () => {
39
53
  log.debug('Framework ready, rescanning all plugin tools...');
40
54
  const plugins = framework.pluginManager.getAll();
@@ -44,7 +58,7 @@ class ExtensionExecutorPlugin extends Plugin {
44
58
  }
45
59
  this._scanPluginTools(plugin);
46
60
  }
47
- this._refreshAllAgentsExtPrompt(framework);
61
+ this._invalidatePromptCaches(framework);
48
62
  });
49
63
 
50
64
  return this;
@@ -80,7 +94,7 @@ class ExtensionExecutorPlugin extends Plugin {
80
94
  log.debug(` Scanned ${Object.keys(plugin.tools).length} tools from plugin '${pluginName}' (as '${useExtName}')`);
81
95
 
82
96
  if (this._framework && this._framework._ready) {
83
- this._refreshAllAgentsExtPrompt(this._framework);
97
+ this._invalidatePromptCaches(this._framework);
84
98
  }
85
99
  }
86
100
 
@@ -111,6 +125,7 @@ class ExtensionExecutorPlugin extends Plugin {
111
125
  }
112
126
 
113
127
  async start(framework) {
128
+ super.start(framework);
114
129
  const plugins = framework.pluginManager.getAll();
115
130
  for (const { instance: plugin } of plugins) {
116
131
  if (plugin.name === 'skill-manager') {
@@ -236,25 +251,21 @@ class ExtensionExecutorPlugin extends Plugin {
236
251
  execute: async (args) => {
237
252
  const { plugin } = args;
238
253
  if (plugin === 'skill') {
239
- return "请使用 `loadSkill` 获取skill的使用详情"
254
+ return "请使用 `skill_load` 获取skill的使用详情"
240
255
  }
241
256
  return this.bindAllToolSkill(plugin)
242
257
  },
243
258
  });
244
259
 
245
260
  framework.on('agent:created', (agent) => {
246
- this._refreshAgentExtPrompt(agent);
261
+ // Extension tool 描述已通过 prompts 声明式注册到 framework.prompts,
262
+ // 并由 _syncPromptParts 同步到子 Agent,无需再注入 _originalPrompt
247
263
  });
248
264
 
249
- if (framework._ready) {
250
- this._refreshAllAgentsExtPrompt(framework);
251
- } else {
252
- framework.once('framework:ready', () => {
253
- this._refreshAllAgentsExtPrompt(framework);
254
- });
255
- }
265
+ // Extension tool 描述已通过 prompts 声明式注册到 framework.prompts,
266
+ // 不再需要 _refreshAllAgentsExtPrompt 注入 _originalPrompt
256
267
 
257
- this.registerPromptPart('extension-tools', PROMPT_PRIORITY.EXTENSION_TOOLS, () => this._buildExtensionsDescription());
268
+ // prompt part 由基类 _autoRegisterPrompts 自动处理
258
269
 
259
270
  return this;
260
271
  }
@@ -294,7 +305,7 @@ class ExtensionExecutorPlugin extends Plugin {
294
305
  log.debug(` Registered tool '${toolDef.name}' for extension '${pluginName}'`);
295
306
 
296
307
  if (this._framework && this._framework._ready) {
297
- this._refreshAllAgentsExtPrompt(this._framework);
308
+ this._invalidatePromptCaches(this._framework);
298
309
  }
299
310
  }
300
311
 
@@ -339,32 +350,17 @@ class ExtensionExecutorPlugin extends Plugin {
339
350
  return sections.join('\n')
340
351
  }
341
352
 
342
- _refreshAllAgentsExtPrompt(framework) {
343
- const visited = new Set();
344
- const traverse = (agent) => {
345
- if (!agent || visited.has(agent)) return;
346
- visited.add(agent);
347
- this._refreshAgentExtPrompt(agent);
348
- const subAgents = agent.getSubAgents?.() || agent._subAgents || new Map();
349
- for (const [, subAgentInfo] of subAgents) {
350
- traverse(subAgentInfo.agent);
351
- }
352
- };
353
- for (const agent of framework._agents || []) {
354
- traverse(agent);
353
+ /**
354
+ * 失效所有 Agent 的延展工具 prompt 缓存
355
+ * 不再注入 _originalPrompt,仅失效缓存让下次 build 时自动读取最新 _buildExtensionsDescription()
356
+ */
357
+ _invalidatePromptCaches(framework) {
358
+ framework?.prompts?.invalidate(this.name, 'extension-tools');
359
+ for (const agent of framework?._agents || []) {
360
+ agent._invalidateSystemPromptCache?.();
355
361
  }
356
362
  }
357
363
 
358
- _refreshAgentExtPrompt(agent) {
359
- const extDesc = this._buildExtensionsDescription();
360
- if (!extDesc) return;
361
-
362
- let existingPrompt = agent._originalPrompt || '';
363
-
364
- const extRegex = /## 【Extensions】[\s\S]*?(?=\n## 【|$)/;
365
- existingPrompt = existingPrompt.replace(extRegex, '').trim();
366
- }
367
-
368
364
  _buildExtensionsDescription() {
369
365
  let desc = '';
370
366
 
@@ -497,6 +493,24 @@ class ExtensionExecutorPlugin extends Plugin {
497
493
  }));
498
494
  }
499
495
 
496
+ // 获取指定插件的所有工具
497
+ getExtensionTools(pluginName) {
498
+ const ext = this._extensions.get(pluginName);
499
+ return ext ? ext.tools : [];
500
+ }
501
+
502
+ // 获取指定插件的指定工具
503
+ getExtensionTool(pluginName, toolName) {
504
+ const ext = this._extensions.get(pluginName);
505
+ if (!ext) return null;
506
+ return ext.tools.find(t => t.name === toolName) || null;
507
+ }
508
+
509
+ // 获取所有插件名称
510
+ getExtensionNames() {
511
+ return Array.from(this._extensions.keys());
512
+ }
513
+
500
514
  getSkillCommands() {
501
515
  const ext = this._extensions.get('skill');
502
516
  if (!ext) return [];
@@ -547,7 +561,7 @@ class ExtensionExecutorPlugin extends Plugin {
547
561
  this._rescanPluginTools(plugin);
548
562
  }
549
563
  this._mcpExecutor = framework.pluginManager?.get('mcp') || null;
550
- this._refreshAllAgentsExtPrompt(framework);
564
+ this._invalidatePromptCaches(framework);
551
565
 
552
566
  await this._reloadMCPConfig(framework);
553
567
  }
@@ -46,7 +46,7 @@ class PythonExecutorPlugin extends Plugin {
46
46
 
47
47
  start(framework) {
48
48
  framework.registerTool({
49
- name: 'python-execute',
49
+ name: 'py_execute',
50
50
  description: '执行 Python 代码,禁止传入无用的emoji',
51
51
  inputSchema: z.object({
52
52
  code: z.string().describe('Python 代码'),
@@ -59,7 +59,7 @@ class PythonExecutorPlugin extends Plugin {
59
59
  })
60
60
 
61
61
  framework.registerTool({
62
- name: 'python_script',
62
+ name: 'py_script',
63
63
  description: '执行 Python 脚本文件',
64
64
  inputSchema: z.object({
65
65
  scriptPath: z.string().describe('Python 脚本文件路径'),
@@ -72,7 +72,7 @@ class PythonExecutorPlugin extends Plugin {
72
72
  })
73
73
 
74
74
  framework.registerTool({
75
- name: 'pip_install',
75
+ name: 'py_pip_install',
76
76
  description: '安装 Python 包',
77
77
  inputSchema: z.object({
78
78
  package: z.string().describe('包名 (例如: numpy, pandas>=1.0)'),
@@ -34,9 +34,8 @@ class ShellExecutorPlugin extends Plugin {
34
34
  }
35
35
 
36
36
  start(framework) {
37
- // 注册 shell 工具
38
- framework.registerTool({
39
- name: 'shell',
37
+ // 注册 shell 工具(同时注册新旧两个名字以兼容旧代码)
38
+ const shellExecHandler = {
40
39
  description: '执行 Shell 命令',
41
40
  inputSchema: z.object({
42
41
  command: z.string().describe('要执行的命令'),
@@ -108,7 +107,10 @@ class ShellExecutorPlugin extends Plugin {
108
107
  }, timeout)
109
108
  })
110
109
  }
111
- })
110
+ };
111
+
112
+ framework.registerTool({ name: 'shell_exec', ...shellExecHandler });
113
+ framework.registerTool({ name: 'shell', ...shellExecHandler });
112
114
 
113
115
  if (process.platform === 'win32') {
114
116
  framework.registerTool({
@@ -455,7 +455,8 @@ class WebPlugin extends Plugin {
455
455
 
456
456
  const webhookAgent = this._framework.createSubAgent({
457
457
  name: 'webhook_handler',
458
- role: 'Webhook处理助手,专注于处理webhook数据并生成适当响应'
458
+ role: 'Webhook处理助手,专注于处理webhook数据并生成适当响应',
459
+ hidden: true
459
460
  })
460
461
 
461
462
  if (!webhook.awaitResponse) {
@@ -10,7 +10,7 @@ const { z } = require('zod')
10
10
  const fs = require('fs')
11
11
  const path = require('path')
12
12
  const { EventEmitter } = require('events')
13
- const {cleanResponse} =require('../../src/utils')
13
+ const {cleanResponse, parseFrontmatter} =require('../../src/utils')
14
14
  /**
15
15
  * 记忆类型常量
16
16
  */
@@ -26,64 +26,6 @@ const MEMORY_TYPES = {
26
26
  */
27
27
  const MEMORY_BASE_DIR = '.foliko/memory'
28
28
 
29
- /**
30
- * 解析 YAML frontmatter(从 skill-manager 复用)
31
- */
32
- function parseFrontmatter(content) {
33
- const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/)
34
- if (!match) return null
35
-
36
- const frontmatter = {}
37
- const lines = match[1].split('\n')
38
- let currentKey = null
39
-
40
- for (const line of lines) {
41
- if (currentKey === 'metadata') {
42
- if (line.match(/^ {2}[a-zA-Z]/)) {
43
- const colonIndex = line.indexOf(':')
44
- if (colonIndex > 0) {
45
- const key = line.substring(2, colonIndex).trim()
46
- const value = line.substring(colonIndex + 1).trim().replace(/^["']|["']$/g, '')
47
- frontmatter[currentKey][key] = value
48
- continue
49
- }
50
- } else if (line.match(/^[a-zA-Z]/)) {
51
- currentKey = null
52
- continue
53
- }
54
- }
55
-
56
- const colonIndex = line.indexOf(':')
57
- if (colonIndex === -1) continue
58
-
59
- const key = line.substring(0, colonIndex).trim()
60
- let value = line.substring(colonIndex + 1).trim()
61
-
62
- if (
63
- (value.startsWith('"') && value.endsWith('"')) ||
64
- (value.startsWith("'") && value.endsWith("'"))
65
- ) {
66
- value = value.slice(1, -1)
67
- }
68
-
69
- if (key === 'metadata') {
70
- currentKey = 'metadata'
71
- frontmatter.metadata = {}
72
- } else if (key === 'tags') {
73
- // 去除首尾空白和方括号
74
- value = value.trim().replace(/^\[|\]$/g, '')
75
- frontmatter[key] = value
76
- .split(',')
77
- .map(t => t.trim().replace(/^["']|["']$/g, ''))
78
- .filter(t => t)
79
- } else {
80
- frontmatter[key] = value
81
- }
82
- }
83
-
84
- return frontmatter
85
- }
86
-
87
29
  /**
88
30
  * 序列化 frontmatter
89
31
  */
@@ -575,13 +517,14 @@ class MemoryPlugin extends Plugin {
575
517
  // 初始化存储
576
518
  this._store = new MemoryStore(this.config.memoryDir, this._framework?.getCwd?.())
577
519
 
578
- // 创建复用的记忆提取子 Agent
520
+ // 创建复用的记忆提取子 Agent(隐藏,不在指令系统中显示)
579
521
  this._memoryAgent = framework.createSubAgent({
580
522
  name: 'memory-extractor',
581
523
  role: '记忆提取专家',
582
524
  description: '专门负责从对话和错误中提取记忆的子 Agent',
583
525
  tools: {}, // 不需要额外工具,隔离主 Agent 工具
584
- parentTools: [] // 不继承任何工具,纯粹做提取
526
+ parentTools: [], // 不继承任何工具,纯粹做提取
527
+ hidden: true, // 隐藏子Agent,不在指令系统中显示
585
528
  })
586
529
 
587
530
  // 注册工具
@@ -1166,25 +1109,28 @@ ${prompt}`
1166
1109
 
1167
1110
  /**
1168
1111
  * 注册 memory-context 到系统提示
1112
+ * 使用 framework.prompts 新 API,slot=CONTEXT
1169
1113
  */
1170
1114
  _registerMemoryContext(framework) {
1171
- const mainAgent = framework._mainAgent
1172
- if (mainAgent) {
1173
- mainAgent.registerPromptPart('memory-context', 350, () => {
1174
- return this._buildMemoryContext()
1115
+ const reg = framework?.prompts
1116
+ const doRegister = () => {
1117
+ if (!reg) return
1118
+ reg.register(this.name, 'memory-context', () => this._buildMemoryContext(), {
1119
+ slot: 'CONTEXT',
1120
+ description: '长期记忆上下文摘要',
1175
1121
  })
1176
- mainAgent._refreshContext()
1177
- }
1178
-
1179
- framework.on('framework:ready', () => {
1180
1122
  const agent = framework._mainAgent
1181
- if (agent) {
1182
- agent.registerPromptPart('memory-context', 350, () => {
1183
- return this._buildMemoryContext()
1184
- })
1123
+ if (agent && typeof agent._invalidateSystemPromptCache === 'function') {
1124
+ agent._invalidateSystemPromptCache()
1185
1125
  agent._refreshContext()
1186
1126
  }
1187
- })
1127
+ }
1128
+
1129
+ if (framework._ready) {
1130
+ doRegister()
1131
+ } else {
1132
+ framework.on('framework:ready', doRegister)
1133
+ }
1188
1134
  }
1189
1135
 
1190
1136
  /**
@@ -1225,14 +1171,23 @@ ${prompt}`
1225
1171
 
1226
1172
  if (userMemories.length > 0) {
1227
1173
  parts.push('### 【用户偏好】')
1174
+ const seen = new Set()
1228
1175
  for (const m of userMemories) {
1176
+ // 去重:跳过前 30 字相似的内容
1177
+ const key = (m.body || '').trim().slice(0, 30)
1178
+ if (seen.has(key)) continue
1179
+ seen.add(key)
1229
1180
  parts.push(`- ${m.name}: ${m.body.substring(0, 150)}${m.body.length > 150 ? '...' : ''}`)
1230
1181
  }
1231
1182
  }
1232
1183
 
1233
1184
  if (projectMemories.length > 0) {
1234
1185
  parts.push('### 【项目上下文】')
1186
+ const seen = new Set()
1235
1187
  for (const m of projectMemories) {
1188
+ const key = (m.body || '').trim().slice(0, 30)
1189
+ if (seen.has(key)) continue
1190
+ seen.add(key)
1236
1191
  const projectTag = m.project ? `[${m.project}] ` : ''
1237
1192
  parts.push(`- ${projectTag}${m.name}: ${m.body.substring(0, 100)}${m.body.length > 100 ? '...' : ''}`)
1238
1193
  }
@@ -4,25 +4,7 @@
4
4
 
5
5
  const Imap = require('imap-mkl')
6
6
  const { simpleParser } = require('mailparser')
7
- const { getConfig, processAttachment } = require('./utils')
8
- const { DEFAULT_IMAP, IMAP_CLIENT_INFO } = require('./constants')
9
-
10
- /**
11
- * 创建 IMAP 配置
12
- * @param {Object} config - 用户配置
13
- * @returns {Object} IMAP 配置
14
- */
15
- function createImapConfig(config = {}) {
16
- const cfg = getConfig(config)
17
- return {
18
- ...DEFAULT_IMAP,
19
- user: cfg.user,
20
- password: cfg.password,
21
- host: cfg.host,
22
- port: cfg.port,
23
- id: IMAP_CLIENT_INFO
24
- }
25
- }
7
+ const { getConfig, processAttachment, createImapConfig } = require('./utils')
26
8
 
27
9
  /**
28
10
  * 发送邮件
@@ -4,23 +4,8 @@
4
4
 
5
5
  const Imap = require('imap-mkl')
6
6
  const { simpleParser } = require('mailparser')
7
- const { DEFAULT_IMAP, IMAP_CLIENT_INFO, EMAIL_DEFAULTS } = require('./constants')
8
- const { getConfig, generateCallId } = require('./utils')
9
-
10
- /**
11
- * 创建 IMAP 配置
12
- */
13
- function createImapConfig(config = {}) {
14
- const cfg = getConfig(config)
15
- return {
16
- ...DEFAULT_IMAP,
17
- user: cfg.user,
18
- password: cfg.password,
19
- host: cfg.host,
20
- port: cfg.port,
21
- id: IMAP_CLIENT_INFO
22
- }
23
- }
7
+ const { EMAIL_DEFAULTS } = require('./constants')
8
+ const { getConfig, generateCallId, createImapConfig } = require('./utils')
24
9
 
25
10
  /**
26
11
  * 标记所有现有未读邮件为已读(启动监控前调用)
@@ -39,7 +39,8 @@ async function handleAutoReply(emailPlugin, args) {
39
39
  const replyAgent = emailPlugin._framework.createSubAgent({
40
40
  name: 'email_replier',
41
41
  role: '邮件自动回复助手,专注于生成专业、礼貌、简洁的邮件回复',
42
- parentTools: []
42
+ parentTools: [],
43
+ hidden: true
43
44
  })
44
45
 
45
46
  // 构建提示让 LLM 生成回复
@@ -114,11 +114,30 @@ function generateCallId() {
114
114
  return `${Date.now()}_${Math.random().toString(36).substr(2, 5)}`
115
115
  }
116
116
 
117
+ /**
118
+ * 创建 IMAP 配置
119
+ * @param {Object} config - 配置对象
120
+ * @returns {Object} IMAP 配置
121
+ */
122
+ function createImapConfig(config = {}) {
123
+ const cfg = getConfig(config)
124
+ const { DEFAULT_IMAP, IMAP_CLIENT_INFO } = require('./constants')
125
+ return {
126
+ ...DEFAULT_IMAP,
127
+ user: cfg.user,
128
+ password: cfg.password,
129
+ host: cfg.host,
130
+ port: cfg.port,
131
+ id: IMAP_CLIENT_INFO
132
+ }
133
+ }
134
+
117
135
  module.exports = {
118
136
  isValidEmail,
119
137
  parseEmailAddress,
120
138
  getConfig,
121
139
  fetchUrl,
122
140
  processAttachment,
123
- generateCallId
141
+ generateCallId,
142
+ createImapConfig
124
143
  }
@@ -212,7 +212,7 @@ class FeishuPlugin extends Plugin {
212
212
  break
213
213
  case 'reload':
214
214
  try {
215
- await this._framework.executeTool('reloadSkills', {})
215
+ await this._framework.executeTool('skill_reload', {})
216
216
  await this._sendMessage(openId, '✅ 技能已重载', originalMsg)
217
217
  } catch (err) {
218
218
  await this._sendMessage(openId, '❌ 重载失败: ' + err.message, originalMsg)
@@ -695,7 +695,7 @@ class QQPlugin extends Plugin {
695
695
  break
696
696
  case 'reload':
697
697
  try {
698
- await this._framework.executeTool('reloadSkills', {})
698
+ await this._framework.executeTool('skill_reload', {})
699
699
  await this._sendMessage(openid, '✅ 技能已重载')
700
700
  } catch (err) {
701
701
  await this._sendMessage(openid, '❌ 重载失败: ' + err.message)
@@ -328,7 +328,7 @@ class TelegramPlugin extends Plugin {
328
328
  break
329
329
  case 'reload':
330
330
  try {
331
- await this._framework.executeTool('reloadSkills', {})
331
+ await this._framework.executeTool('skill_reload', {})
332
332
  await this._sendMessage(chatId, '✅ 技能已重载', msg.message_id)
333
333
  } catch (err) {
334
334
  await this._sendMessage(chatId, '❌ 重载失败: ' + err.message, msg.message_id)
@@ -641,7 +641,7 @@ class WeixinPlugin extends Plugin {
641
641
  break
642
642
  case 'reload':
643
643
  try {
644
- await this._framework.executeTool('reloadSkills', {})
644
+ await this._framework.executeTool('skill_reload', {})
645
645
  await this._sendMessageBatch(originalMsg, userId, '✅ 技能已重载', true)
646
646
  } catch (err) {
647
647
  await this._sendMessageBatch(originalMsg, userId, '❌ 重载失败: ' + err.message, true)
@@ -5,45 +5,13 @@
5
5
 
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
- const { execSync } = require('child_process');
9
8
  const { Plugin } = require('../../src/plugin/base');
10
9
  const { logger } = require('../../src/common/logger');
11
10
  const { z } = require('zod');
12
- const { DEFAULT_REPO, shouldIgnore } = require('../../src/cli/utils/plugin-config');
11
+ const { DEFAULT_REPO, shouldIgnore, copyDirRecursive, parseGitUrl, gitCommand } = require('../../src/utils');
13
12
 
14
13
  const log = logger.child('PluginManagerPlugin');
15
14
 
16
- function copyDirRecursive(src, dest) {
17
- if (!fs.existsSync(src)) return;
18
- fs.mkdirSync(dest, { recursive: true });
19
-
20
- const entries = fs.readdirSync(src, { withFileTypes: true });
21
- for (const entry of entries) {
22
- const srcPath = path.join(src, entry.name);
23
- const destPath = path.join(dest, entry.name);
24
- if (shouldIgnore(entry.name)) { log.debug(`Ignoring: ${entry.name}`); continue; }
25
- if (entry.isDirectory()) { copyDirRecursive(srcPath, destPath); }
26
- else { fs.copyFileSync(srcPath, destPath); }
27
- }
28
- }
29
-
30
- function gitCommand(args, cwd) {
31
- try { return execSync(`git ${args}`, { cwd, encoding: 'utf-8', stdio: 'pipe' }); }
32
- catch (err) { return err.stdout || err.stderr || ''; }
33
- }
34
-
35
- function parseGitUrl(url) {
36
- const patterns = [
37
- /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/,
38
- /^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/,
39
- ];
40
- for (const pattern of patterns) {
41
- const match = url.match(pattern);
42
- if (match) return { owner: match[1], repo: match[2] };
43
- }
44
- return null;
45
- }
46
-
47
15
  class PluginManagerPlugin extends Plugin {
48
16
  constructor(config = {}) {
49
17
  super();
@@ -19,6 +19,18 @@ class ToolsPlugin extends Plugin {
19
19
  this._framework = null;
20
20
  }
21
21
 
22
+ // 声明式 prompt parts(替代原 start() 中的 registerPromptPart 调用)
23
+ prompts = [
24
+ {
25
+ name: 'tool-core-rules',
26
+ slot: 'BEHAVIOR',
27
+ description: '工具调用核心规则 + 响应规范 + 禁止事项',
28
+ provider: function () {
29
+ return this._getToolCoreRules();
30
+ },
31
+ },
32
+ ];
33
+
22
34
  install(framework) {
23
35
  this._framework = framework;
24
36
  this._registerBuiltinTools();
@@ -26,7 +38,8 @@ class ToolsPlugin extends Plugin {
26
38
  }
27
39
 
28
40
  start(framework) {
29
- this.registerPromptPart('tool-core-rules', 95, () => this._getToolCoreRules());
41
+ super.start(framework);
42
+ // prompt parts 由基类 _autoRegisterPrompts 自动处理
30
43
  }
31
44
 
32
45
  _getToolCoreRules() {