foliko 1.1.6 → 1.1.7

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 (79) hide show
  1. package/.agent/data/email/processed-emails.json +1 -0
  2. package/.agent/data/plugins-state.json +5 -1
  3. package/.agent/data/web/web-config.json +5 -0
  4. package/.agent/memory/feedback/mnt7jrlt-d67qs7.md +15 -0
  5. package/.agent/memory/feedback/mnt88ja3-al4fuy.md +9 -0
  6. package/.agent/plugins/test-plugin.py +108 -0
  7. package/.agent/sessions/cli_default.json +514 -5298
  8. package/.claude/settings.local.json +2 -1
  9. package/SPEC.md +735 -696
  10. package/output/zen_silence.png +0 -0
  11. package/package.json +2 -2
  12. package/plugins/ambient-agent/EventWatcher.js +33 -37
  13. package/plugins/ambient-agent/ExplorerLoop.js +338 -36
  14. package/plugins/ambient-agent/GoalManager.js +7 -3
  15. package/plugins/ambient-agent/StateStore.js +30 -1
  16. package/plugins/ambient-agent/constants.js +15 -1
  17. package/plugins/ambient-agent/index.js +26 -33
  18. package/plugins/coordinator-plugin.js +3 -3
  19. package/plugins/default-plugins.js +2 -2
  20. package/plugins/email/index.js +150 -36
  21. package/plugins/email/monitor.js +79 -5
  22. package/plugins/email/reply.js +15 -25
  23. package/plugins/extension-executor-plugin.js +160 -31
  24. package/plugins/file-system-plugin.js +57 -24
  25. package/plugins/memory-plugin.js +176 -64
  26. package/plugins/python-plugin-loader.js +79 -9
  27. package/plugins/scheduler-plugin.js +64 -24
  28. package/plugins/think-plugin.js +7 -2
  29. package/plugins/web-plugin.js +263 -4
  30. package/skills/ambient-agent/SKILL.md +342 -314
  31. package/src/core/agent-chat.js +64 -9
  32. package/src/core/agent.js +118 -59
  33. package/src/core/tool-registry.js +5 -5
  34. package/src/executors/mcp-executor.js +188 -26
  35. package/src/utils/id.js +5 -0
  36. package/system.md +3480 -0
  37. package/.agent/data/ambient/goals.json +0 -50
  38. package/.agent/data/ambient/memories.json +0 -7
  39. package/.agent/memory/core.md +0 -1
  40. package/.agent/memory/feedback/mnrsiuoc-e1ru74.md +0 -9
  41. package/.agent/memory/feedback/mnrt2mmz-98az6n.md +0 -9
  42. package/.agent/memory/feedback/mnrtqrhm-kxsicz.md +0 -9
  43. package/.agent/memory/feedback/mnrts8vg-i0ngzp.md +0 -15
  44. package/.agent/memory/feedback/mnrtt7jt-c0trb2.md +0 -9
  45. package/.agent/memory/feedback/mnruc2f0-5s52la.md +0 -16
  46. package/.agent/memory/feedback/mnrumbmx-63sa0v.md +0 -9
  47. package/.agent/memory/project/mnn93ogy-ypjn27.md +0 -9
  48. package/.agent/memory/project/mnn98fqy-5nhc1u.md +0 -25
  49. package/.agent/memory/project/mnrp7p5n-8enm2a.md +0 -31
  50. package/.agent/memory/project/mnrp9ifb-yynks0.md +0 -40
  51. package/.agent/memory/project/mnrpb3b8-f617s4.md +0 -25
  52. package/.agent/memory/project/mnrrmqgg-focprv.md +0 -9
  53. package/.agent/memory/project/mnrtykbh-6atsor.md +0 -9
  54. package/.agent/memory/project/mnru9jiu-kgau16.md +0 -35
  55. package/.agent/memory/reference/mnq3oenw-46haj6.md +0 -63
  56. package/.agent/memory/reference/mnq5qxm2-mjoooh.md +0 -116
  57. package/.agent/memory/reference/mnrnvpwo-rcqv9m.md +0 -52
  58. package/.agent/memory/reference/mnrovxvz-zy9xqm.md +0 -25
  59. package/.agent/memory/reference/mnroxabj-1b3930.md +0 -68
  60. package/.agent/memory/reference/mnrpjtlp-mnb9od.md +0 -35
  61. package/.agent/memory/reference/mnrps1x3-6b8xfm.md +0 -28
  62. package/.agent/memory/reference/mnrpt9ov-15er5w.md +0 -22
  63. package/.agent/memory/reference/mnrq82dn-y9tv9e.md +0 -50
  64. package/.agent/memory/reference/mnrqnr5v-v75drf.md +0 -34
  65. package/.agent/memory/reference/mnrrfzys-urudaf.md +0 -31
  66. package/.agent/memory/reference/mnrrocha-t0027n.md +0 -21
  67. package/.agent/memory/reference/mnrukklc-bxndsb.md +0 -35
  68. package/.agent/memory/user/mnm67t9m-x8rekk.md +0 -9
  69. package/.agent/memory/user/mnn5mmqh-w6aktx.md +0 -11
  70. package/.agent/memory/user/mnnbfhhn-dk1bd1.md +0 -22
  71. package/.agent/memory/user/mnrt39t8-8eosy0.md +0 -9
  72. package/foliko-cloud-rising.png +0 -0
  73. package/foliko-dawn-of-ai.png +0 -0
  74. package/foliko-mindful-observation.png +0 -0
  75. package/foliko-stellar-dreams.png +0 -0
  76. package/foliko-zen-jing.png +0 -0
  77. package/foliko-zen-kong.png +0 -0
  78. package/foliko-zen-wu.png +0 -0
  79. package/zen_karesansui.png +0 -0
@@ -70,6 +70,8 @@ function parseFrontmatter(content) {
70
70
  currentKey = 'metadata'
71
71
  frontmatter.metadata = {}
72
72
  } else if (key === 'tags') {
73
+ // 去除首尾空白和方括号
74
+ value = value.trim().replace(/^\[|\]$/g, '')
73
75
  frontmatter[key] = value
74
76
  .split(',')
75
77
  .map(t => t.trim().replace(/^["']|["']$/g, ''))
@@ -558,6 +560,10 @@ class MemoryPlugin extends Plugin {
558
560
  this._store = null
559
561
  this._memoryAgent = null // 用于记忆提取的子 agent
560
562
  this._isExtracting = false // 防止递归调用的标志
563
+ this._extractInterval = config.extractInterval || 10 * 60 * 1000 // 默认 5 分钟
564
+ this._extractTimer = null // 定时提取定时器
565
+ this._pendingConversations = [] // 待处理的对话队列
566
+ this._pendingErrors = [] // 待处理的错误队列
561
567
  }
562
568
 
563
569
  install(framework) {
@@ -811,93 +817,91 @@ class MemoryPlugin extends Plugin {
811
817
  * 设置自动记忆提取
812
818
  */
813
819
  _setupAutoExtract(framework) {
814
- // 监听 agent 消息完成事件,在对话轮次结束时提取记忆
815
- framework.on('agent:message', async ({ userMessage, assistantResponse }) => {
820
+ // 收集对话到待处理队列,定时提取
821
+ framework.on('agent:message', ({ userMessage, assistantResponse }) => {
816
822
  // 防止递归调用:如果正在提取中,跳过此次事件
817
823
  if (this._isExtracting) {
818
824
  log.debug('Skipping agent:message event during memory extraction')
819
825
  return
820
826
  }
821
827
 
822
- try {
823
- await this._extractMemoryFromConversation(userMessage, assistantResponse)
824
- } catch (err) {
825
- log.warn('Auto memory extraction failed:', err.message)
828
+ // 添加到待处理队列
829
+ this._pendingConversations.push({
830
+ userMessage,
831
+ assistantResponse,
832
+ timestamp: Date.now()
833
+ })
834
+
835
+ // 限制队列大小,避免内存溢出
836
+ if (this._pendingConversations.length > 50) {
837
+ this._pendingConversations.shift()
826
838
  }
827
839
  })
828
840
 
829
- // 监听所有工具调用错误(包括 ext_call, MCP, 原生工具等)
841
+ // 启动定时提取器
842
+ this._startPeriodicExtraction()
843
+
844
+ // 监听所有工具调用错误 - 收集到队列,定时处理
830
845
  framework.on('tool:error', async (data) => {
831
- try {
832
- await this._extractErrorLesson({
833
- name: data.name || data.tool || 'unknown-tool',
834
- args: data.args || {},
835
- error: data.error || data.message || String(data),
836
- source: data.source || 'tool'
837
- })
838
- } catch (err) {
839
- log.warn('Auto tool error lesson extraction failed:', err.message)
846
+ // 添加到待处理队列
847
+ this._pendingErrors.push({
848
+ name: data.name || data.tool || 'unknown-tool',
849
+ args: data.args || {},
850
+ error: data.error || data.message || String(data),
851
+ source: data.source || 'tool',
852
+ timestamp: Date.now()
853
+ })
854
+
855
+ // 限制队列大小
856
+ if (this._pendingErrors.length > 50) {
857
+ this._pendingErrors.shift()
840
858
  }
841
859
  })
842
860
 
843
861
  // 监听 tool-call 别名(AI SDK 6.x 格式)
844
862
  framework.on('tool-error', async (data) => {
845
- try {
846
- await this._extractErrorLesson({
847
- name: data.name || data.tool || 'unknown-tool',
848
- args: data.args || {},
849
- error: data.error || data.message || String(data),
850
- source: data.source || 'tool'
851
- })
852
- } catch (err) {
853
- log.warn('Auto tool-error lesson extraction failed:', err.message)
863
+ // 添加到待处理队列
864
+ this._pendingErrors.push({
865
+ name: data.name || data.tool || 'unknown-tool',
866
+ args: data.args || {},
867
+ error: data.error || data.message || String(data),
868
+ source: data.source || 'tool',
869
+ timestamp: Date.now()
870
+ })
871
+
872
+ // 限制队列大小
873
+ if (this._pendingErrors.length > 50) {
874
+ this._pendingErrors.shift()
854
875
  }
855
876
  })
856
877
 
857
- // 监听工具重试事件(重试可能意味着配置或参数问题)
878
+ // 监听工具重试事件
858
879
  framework.on('tool:retry', async (data) => {
859
- try {
860
- const { name, args, error, attempt } = data
861
- await this._extractErrorLesson({
862
- name: name || 'unknown-tool',
863
- args: args || {},
864
- error: `Retry attempt ${attempt} failed: ${error || data.message || 'unknown'}`,
865
- source: 'tool-retry'
866
- })
867
- } catch (err) {
868
- log.warn('Auto tool retry lesson extraction failed:', err.message)
880
+ // 添加到待处理队列
881
+ this._pendingErrors.push({
882
+ name: data.name || 'unknown-tool',
883
+ args: data.args || {},
884
+ error: `Retry attempt ${data.attempt} failed: ${data.error || data.message || 'unknown'}`,
885
+ source: 'tool-retry',
886
+ timestamp: Date.now()
887
+ })
888
+
889
+ // 限制队列大小
890
+ if (this._pendingErrors.length > 50) {
891
+ this._pendingErrors.shift()
869
892
  }
870
893
  })
871
894
 
872
895
  // 监听 agent 对话过程中的错误
873
896
  framework.on('error', async (data) => {
874
- try {
875
- const errorObj = typeof data === 'object' ? data : { error: String(data) }
876
- await this._extractErrorLesson({
877
- name: errorObj.context || 'agent-error',
878
- args: errorObj.data || {},
879
- error: errorObj.error || errorObj.message || String(data),
880
- source: 'agent'
881
- })
882
- } catch (err) {
883
- log.warn('Auto agent error lesson extraction failed:', err.message)
884
- }
897
+ // agent 错误通常不需要提取记忆,避免循环
898
+ log.debug('Received agent error event, skipping memory extraction')
885
899
  })
886
900
 
887
901
  // 监听 agent status 错误
888
902
  framework.on('status', async (data) => {
889
- if (data.status === 'error' && data.error) {
890
- try {
891
- await this._extractErrorLesson({
892
- name: 'agent-status-error',
893
- args: {},
894
- error: data.error,
895
- source: 'agent-status'
896
- })
897
- } catch (err) {
898
- log.warn('Auto status error lesson extraction failed:', err.message)
899
- }
900
- }
903
+ // status 错误已经在 tool:error 中处理,这里跳过避免重复
904
+ log.debug('Received status event, skipping memory extraction')
901
905
  })
902
906
 
903
907
  // 监听会话上下文错误
@@ -918,12 +922,113 @@ class MemoryPlugin extends Plugin {
918
922
  })
919
923
  }
920
924
 
925
+ /**
926
+ * 启动定时记忆提取
927
+ */
928
+ _startPeriodicExtraction() {
929
+ // 如果间隔为 0,则禁用定时提取
930
+ if (this._extractInterval <= 0) {
931
+ log.info('Periodic memory extraction disabled')
932
+ return
933
+ }
934
+
935
+ // 清除已有的定时器
936
+ if (this._extractTimer) {
937
+ clearInterval(this._extractTimer)
938
+ }
939
+
940
+ log.info(`Starting periodic memory extraction (interval: ${this._extractInterval / 1000}s)`)
941
+
942
+ this._extractTimer = setInterval(async () => {
943
+ await this._processPendingConversations()
944
+ }, this._extractInterval)
945
+ }
946
+
947
+ /**
948
+ * 处理待处理的对话队列和错误队列
949
+ */
950
+ async _processPendingConversations() {
951
+ if (this._isExtracting) {
952
+ log.debug('Skipping periodic extraction - already in progress')
953
+ return
954
+ }
955
+
956
+ // 如果两个队列都为空,直接返回
957
+ if (this._pendingConversations.length === 0 && this._pendingErrors.length === 0) {
958
+ return
959
+ }
960
+
961
+ // 防止递归调用
962
+ this._isExtracting = true
963
+
964
+ try {
965
+ // 批量处理对话
966
+ if (this._pendingConversations.length > 0) {
967
+ const conversations = this._pendingConversations.splice(0, 10) // 每次最多处理 10 条
968
+ log.debug(`Processing ${conversations.length} pending conversations`)
969
+
970
+ for (const { userMessage, assistantResponse } of conversations) {
971
+ try {
972
+ await this._extractMemoryFromConversation(userMessage, assistantResponse)
973
+ } catch (err) {
974
+ log.warn('Failed to extract memory from conversation:', err.message)
975
+ }
976
+ }
977
+ }
978
+
979
+ // 批量处理错误
980
+ if (this._pendingErrors.length > 0) {
981
+ const errors = this._pendingErrors.splice(0, 10) // 每次最多处理 10 条
982
+ log.debug(`Processing ${errors.length} pending errors`)
983
+
984
+ for (const errorData of errors) {
985
+ try {
986
+ await this._extractErrorLesson(errorData)
987
+ } catch (err) {
988
+ log.warn('Failed to extract error lesson:', err.message)
989
+ }
990
+ }
991
+ }
992
+ } finally {
993
+ this._isExtracting = false
994
+ }
995
+ }
996
+
921
997
  /**
922
998
  * 从错误中提取经验教训并保存
923
999
  */
924
1000
  async _extractErrorLesson(errorData) {
925
1001
  const { name, args, error, source } = errorData
926
1002
 
1003
+ // 将 error 转为字符串
1004
+ const errorStr = typeof error === 'string' ? error : String(error)
1005
+
1006
+ // 检查是否需要跳过 AI 提取(AI 服务过载或返回非 JSON 时避免加重负担)
1007
+ const shouldSkipAI = errorStr && (
1008
+ errorStr.includes('负载较高') ||
1009
+ errorStr.includes('retry') ||
1010
+ errorStr.includes('timeout') ||
1011
+ errorStr.includes('Failed after') ||
1012
+ errorStr.includes('Invalid JSON response')
1013
+ )
1014
+
1015
+ if (shouldSkipAI) {
1016
+ // AI 服务过载时,直接保存基本信息,不调用 AI
1017
+ try {
1018
+ this._store.add({
1019
+ name: `【避免指南】${name} 调用失败`,
1020
+ type: MEMORY_TYPES.FEEDBACK,
1021
+ project: null,
1022
+ tags: ['error', 'avoid', name],
1023
+ body: `【下次这样做】调用 ${name} 时遇到错误: ${error}。来源: ${source}`
1024
+ })
1025
+ log.debug('[Memory] Saved error lesson without AI extraction')
1026
+ } catch (saveErr) {
1027
+ // 忽略保存失败
1028
+ }
1029
+ return
1030
+ }
1031
+
927
1032
  // 构建提取提示 - 强调"如何下次避免"
928
1033
  const extractPrompt = `你是一个经验总结专家。请分析以下错误,总结如何在未来避免类似错误。
929
1034
 
@@ -961,7 +1066,9 @@ class MemoryPlugin extends Plugin {
961
1066
  }
962
1067
  }
963
1068
  } catch (err) {
964
- log.warn('[Memory] Error lesson extraction failed:', err.message)
1069
+ // 忽略所有提取错误,包括 AI 服务过载错误
1070
+ // 这些错误不应该影响主对话流程
1071
+ log.debug('[Memory] Error lesson extraction skipped:', err.message)
965
1072
  }
966
1073
  }
967
1074
 
@@ -1123,25 +1230,25 @@ ${prompt}`
1123
1230
  return null
1124
1231
  }
1125
1232
 
1126
- const parts = ['【记忆上下文】']
1233
+ const parts = ['## 【记忆上下文】']
1127
1234
 
1128
1235
  // 错误避免指南(优先显示,放在最前面)
1129
1236
  if (feedbackMemories.length > 0) {
1130
- parts.push('【错误避免指南 - 必须遵守】')
1237
+ parts.push('### 【错误避免指南 - 必须遵守】')
1131
1238
  for (const m of feedbackMemories) {
1132
1239
  parts.push(`- ${m.body}`)
1133
1240
  }
1134
1241
  }
1135
1242
 
1136
1243
  if (userMemories.length > 0) {
1137
- parts.push('【用户偏好】')
1244
+ parts.push('### 【用户偏好】')
1138
1245
  for (const m of userMemories) {
1139
1246
  parts.push(`- ${m.name}: ${m.body.substring(0, 150)}${m.body.length > 150 ? '...' : ''}`)
1140
1247
  }
1141
1248
  }
1142
1249
 
1143
1250
  if (projectMemories.length > 0) {
1144
- parts.push('【项目上下文】')
1251
+ parts.push('### 【项目上下文】')
1145
1252
  for (const m of projectMemories) {
1146
1253
  const projectTag = m.project ? `[${m.project}] ` : ''
1147
1254
  parts.push(`- ${projectTag}${m.name}: ${m.body.substring(0, 100)}${m.body.length > 100 ? '...' : ''}`)
@@ -1176,6 +1283,11 @@ ${prompt}`
1176
1283
  }
1177
1284
 
1178
1285
  uninstall(framework) {
1286
+ // 清除定时器
1287
+ if (this._extractTimer) {
1288
+ clearInterval(this._extractTimer)
1289
+ this._extractTimer = null
1290
+ }
1179
1291
  this._store = null
1180
1292
  this._framework = null
1181
1293
  }
@@ -9,6 +9,7 @@ const { Plugin } = require('../src/core/plugin-base')
9
9
  const { logger } = require('../src/utils/logger')
10
10
  const log = logger.child('PythonPluginLoader')
11
11
  const { z } = require('zod')
12
+ const { zodSchemaToMarkdown } = require('@chnak/zod-to-markdown')
12
13
 
13
14
  // 将 JSON Schema 转换为 Zod Schema
14
15
  function jsonSchemaToZod(jsonSchema) {
@@ -101,21 +102,25 @@ class PythonPluginLoader extends Plugin {
101
102
  return ''
102
103
  }
103
104
 
104
- let desc = '【Python 插件】\n'
105
- desc += 'Python 插件通过 ext_call 调用:\n\n'
105
+ let desc = '## 【Python 插件】\n'
106
+ desc += 'Python 插件工具已注册为 `插件名_工具名` 格式,通过 ext_call 调用:\n\n'
106
107
 
107
108
  for (const [name, plugin] of this._pythonPlugins) {
108
- desc += `插件: ${plugin.info.name} - ${plugin.info.description || ''}\n`
109
+ desc += `### ${plugin.info.name}\n`
110
+ desc += `${plugin.info.description || ''}\n\n`
109
111
  if (plugin.info.tools && Array.isArray(plugin.info.tools)) {
110
112
  for (const tool of plugin.info.tools) {
111
- const paramsStr = Object.keys(tool.params || {}).join(', ') || '无参数'
112
- desc += ` - ${tool.name}(${paramsStr}): ${tool.description || '无描述'}\n`
113
+ const fullName = `${name}_${tool.name}`
114
+ const paramsStr = this._formatParams(tool.params || {})
115
+ desc += `- **${fullName}**(${paramsStr}): ${tool.description || '无描述'}\n`
113
116
  }
114
117
  }
115
118
  desc += '\n'
116
119
  }
117
120
 
118
- desc += '调用格式: ext_call({ plugin: "插件名", tool: "工具名", args: {...} })\n'
121
+ desc += '**调用格式:**\n'
122
+ desc += '```\next_call({ plugin: "python", tool: "插件名_工具名", args: {...} })\n'
123
+ desc += '```\n'
119
124
  return desc.trim()
120
125
  }
121
126
 
@@ -202,14 +207,18 @@ class PythonPluginLoader extends Plugin {
202
207
 
203
208
  /**
204
209
  * 注册 Python 插件的工具
210
+ * 工具名格式:插件名_工具名(如 stock_get_price)
205
211
  */
206
212
  _registerPythonTool(pluginName, tool) {
207
213
  if (!tool.name) return
208
214
 
215
+ // 格式:pluginname_toolname
216
+ const fullToolName = `${pluginName}_${tool.name}`
217
+
209
218
  try {
210
219
  this.registerTool({
211
- name: tool.name,
212
- description: tool.description || '',
220
+ name: fullToolName,
221
+ description: `[${pluginName}] ${tool.description || ''}`,
213
222
  pluginName: pluginName,
214
223
  inputSchema: this._parseToolParams(tool.params || {}),
215
224
  execute: async (args) => {
@@ -217,7 +226,7 @@ class PythonPluginLoader extends Plugin {
217
226
  }
218
227
  })
219
228
  } catch (err) {
220
- log.error(` Failed to register tool ${tool.name}:`, err.message)
229
+ log.error(` Failed to register tool ${fullToolName}:`, err.message)
221
230
  }
222
231
  }
223
232
 
@@ -253,6 +262,67 @@ class PythonPluginLoader extends Plugin {
253
262
  return jsonSchemaToZod(jsonSchema)
254
263
  }
255
264
 
265
+ /**
266
+ * 格式化参数描述(使用 zodSchemaToMarkdown)
267
+ */
268
+ _formatParams(params) {
269
+ if (!params || Object.keys(params).length === 0) {
270
+ return '无参数'
271
+ }
272
+
273
+ try {
274
+ // 构建 JSON Schema 格式
275
+ const properties = {}
276
+ const required = []
277
+
278
+ for (const [key, value] of Object.entries(params)) {
279
+ let type = 'string'
280
+ if (typeof value === 'boolean') type = 'boolean'
281
+ else if (typeof value === 'number') type = 'number'
282
+ else if (Array.isArray(value)) type = 'array'
283
+ else if (typeof value === 'object' && value !== null) type = 'object'
284
+
285
+ properties[key] = {
286
+ type,
287
+ description: ''
288
+ }
289
+ required.push(key)
290
+ }
291
+
292
+ const jsonSchema = {
293
+ type: 'object',
294
+ properties,
295
+ required
296
+ }
297
+
298
+ // 转换为 Zod Schema
299
+ const zodSchema = jsonSchemaToZod(jsonSchema)
300
+
301
+ // 使用 zodSchemaToMarkdown 生成表格
302
+ const table = zodSchemaToMarkdown(zodSchema)
303
+
304
+ // 提取表格中的参数行,格式化为 "param: type" 形式
305
+ const lines = table.split('\n')
306
+ const paramLines = []
307
+
308
+ for (const line of lines) {
309
+ if (line.startsWith('| ') && !line.includes('字段') && !line.includes('---')) {
310
+ const parts = line.split('|').map(s => s.trim())
311
+ if (parts.length >= 3 && parts[1]) {
312
+ const paramName = parts[1]
313
+ const type = parts[2]
314
+ paramLines.push(`${paramName}: ${type}`)
315
+ }
316
+ }
317
+ }
318
+
319
+ return paramLines.join(', ') || '无参数'
320
+ } catch (err) {
321
+ // fallback 到简单的参数名列表
322
+ return Object.keys(params).join(', ') || '无参数'
323
+ }
324
+ }
325
+
256
326
  /**
257
327
  * 加载单个 Python 插件的元信息
258
328
  * 支持 PLUGIN 和 TOOLS 格式
@@ -55,7 +55,8 @@ class TaskStore {
55
55
  sessionId: t.sessionId,
56
56
  llm: t.llm,
57
57
  persistDelay: t.persistDelay, // 相对时间的延迟毫秒数
58
- persistNextRun: t.persistNextRun // 下次执行时间
58
+ persistNextRun: t.persistNextRun, // 下次执行时间
59
+ notify: t.notify // 是否发送通知
59
60
  }))
60
61
  fs.writeFileSync(this._getTasksPath(), JSON.stringify(serializable, null, 2))
61
62
  } catch (err) {
@@ -180,7 +181,8 @@ class SchedulerPlugin extends Plugin {
180
181
  repeat: z.boolean().optional().describe('是否重复执行 (默认 false)'),
181
182
  cronExpression: z.string().optional().describe('Cron 表达式 (当 repeat 为 true 时使用)'),
182
183
  sessionId: z.string().optional().describe('会话 ID(提醒将发送到该会话,不填则使用默认会话)'),
183
- llm: z.boolean().optional().describe('是否需要 LLM 处理(自动检测,可手动覆盖)')
184
+ llm: z.boolean().optional().describe('是否需要 LLM 处理(自动检测,可手动覆盖)'),
185
+ notify: z.boolean().optional().describe('任务完成后是否发送通知(默认 true)。设为 false 时,任务将在后台静默执行,不发送任何通知')
184
186
  }),
185
187
  execute: async (args) => {
186
188
  try {
@@ -254,7 +256,8 @@ class SchedulerPlugin extends Plugin {
254
256
  timer: null,
255
257
  cronTask: null,
256
258
  sessionId: targetSessionId || null,
257
- llm: llmMode
259
+ llm: llmMode,
260
+ notify: args.notify !== false // 默认发送通知
258
261
  }
259
262
 
260
263
  // 使用 node-cron 调度
@@ -276,7 +279,8 @@ class SchedulerPlugin extends Plugin {
276
279
  runCount: 0,
277
280
  timer: null,
278
281
  sessionId: targetSessionId || null,
279
- llm: llmMode
282
+ llm: llmMode,
283
+ notify: args.notify !== false
280
284
  }
281
285
  task.timer = setTimeout(async () => {
282
286
  await this._executeTask(task)
@@ -301,7 +305,8 @@ class SchedulerPlugin extends Plugin {
301
305
  timer: null,
302
306
  sessionId: targetSessionId || null,
303
307
  llm: llmMode,
304
- persistDelay: delayMs // 保存延迟毫秒数用于持久化
308
+ persistDelay: delayMs, // 保存延迟毫秒数用于持久化
309
+ notify: args.notify !== false
305
310
  }
306
311
  task.timer = setTimeout(async () => {
307
312
  await this._executeTask(task)
@@ -330,7 +335,8 @@ class SchedulerPlugin extends Plugin {
330
335
  cronExpression: task.cronExpression,
331
336
  message: repeat ? '定时任务已创建 (重复执行)' : '提醒已设置',
332
337
  sessionId: sessionId || 'default',
333
- llm: llmMode
338
+ llm: llmMode,
339
+ notify: task.notify
334
340
  }
335
341
  } catch (err) {
336
342
  return { success: false, error: err.message }
@@ -354,6 +360,7 @@ class SchedulerPlugin extends Plugin {
354
360
  lastRun: t.lastRun,
355
361
  cronExpression: t.cronExpression,
356
362
  llm: t.llm,
363
+ notify: t.notify,
357
364
  sessionId: t.sessionId
358
365
  }))
359
366
 
@@ -578,29 +585,56 @@ class SchedulerPlugin extends Plugin {
578
585
  // console.log(`\n🔔 [定时提醒] ${responseText}\n`)
579
586
  // }
580
587
 
581
- // 发送统一的通知事件
582
- this._framework.emit('notification', {
583
- title: task.name,
584
- message: responseText || task.message,
585
- source: 'scheduler',
586
- level: 'info',
587
- sessionId: task.sessionId,
588
- timestamp: new Date()
588
+ // 根据 notify 参数决定是否发送通知
589
+ if (task.notify !== false) {
590
+ // 发送统一的通知事件
591
+ this._framework.emit('notification', {
592
+ title: task.name,
593
+ message: responseText || task.message,
594
+ source: 'scheduler',
595
+ level: 'info',
596
+ sessionId: task.sessionId,
597
+ timestamp: new Date()
598
+ })
599
+ // 发送 scheduler:reminder 事件
600
+ this._framework.emit('scheduler:reminder', {
601
+ taskId: task.id,
602
+ message: task.message,
603
+ time: task.runAt || task.nextRun
604
+ })
605
+ }
606
+
607
+ // 发送任务完成事件
608
+ this._framework.emit('scheduler:task_completed', {
609
+ taskId: task.id,
610
+ result: responseText || task.message
589
611
  })
590
612
  } else {
591
613
  // 直接显示模式:只显示提醒,不发 LLM
592
- //console.log(`\n🔔 [定时提醒] ${task.message}\n`)
614
+ // 根据 notify 参数决定是否发送通知
615
+ if (task.notify !== false) {
616
+ // 发送统一的通知事件
617
+ this._framework.emit('notification', {
618
+ title: task.name,
619
+ message: task.message,
620
+ source: 'scheduler',
621
+ level: 'info',
622
+ sessionId: task.sessionId,
623
+ timestamp: new Date()
624
+ })
625
+ // 发送 scheduler:reminder 事件
626
+ this._framework.emit('scheduler:reminder', {
627
+ taskId: task.id,
628
+ message: task.message,
629
+ time: task.runAt || task.nextRun
630
+ })
631
+ }
593
632
 
594
- // 发送统一的通知事件
595
- this._framework.emit('notification', {
596
- title: task.name,
597
- message: task.message,
598
- source: 'scheduler',
599
- level: 'info',
600
- sessionId: task.sessionId,
601
- timestamp: new Date()
633
+ // 发送任务完成事件
634
+ this._framework.emit('scheduler:task_completed', {
635
+ taskId: task.id,
636
+ result: task.message
602
637
  })
603
-
604
638
  this._taskStats.completed++
605
639
  }
606
640
 
@@ -624,6 +658,12 @@ class SchedulerPlugin extends Plugin {
624
658
  timestamp: new Date()
625
659
  })
626
660
 
661
+ // 发送任务失败事件
662
+ this._framework.emit('scheduler:task_failed', {
663
+ taskId: task.id,
664
+ error: err.message
665
+ })
666
+
627
667
  // 一次性任务失败后也清理
628
668
  if (task.type === 'once' && !task.cronTask) {
629
669
  this._cleanupTask(task.id)
@@ -131,9 +131,14 @@ class ThinkPlugin extends Plugin {
131
131
  this._thoughts = this._thoughts.slice(-100)
132
132
  }
133
133
 
134
- // 触发思考完成事件
134
+ // 触发思考完成事件(使用文档规定的字段结构)
135
135
  if (this._framework) {
136
- this._framework.emit('think:thought_completed', thought)
136
+ this._framework.emit('think:thought_completed', {
137
+ mode: thought.mode,
138
+ topic: thought.topic,
139
+ thought: thought.result,
140
+ depth: thought.depth
141
+ })
137
142
  }
138
143
 
139
144
  return {