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.
- package/.agent/data/email/processed-emails.json +1 -0
- package/.agent/data/plugins-state.json +5 -1
- package/.agent/data/web/web-config.json +5 -0
- package/.agent/memory/feedback/mnt7jrlt-d67qs7.md +15 -0
- package/.agent/memory/feedback/mnt88ja3-al4fuy.md +9 -0
- package/.agent/plugins/test-plugin.py +108 -0
- package/.agent/sessions/cli_default.json +514 -5298
- package/.claude/settings.local.json +2 -1
- package/SPEC.md +735 -696
- package/output/zen_silence.png +0 -0
- package/package.json +2 -2
- package/plugins/ambient-agent/EventWatcher.js +33 -37
- package/plugins/ambient-agent/ExplorerLoop.js +338 -36
- package/plugins/ambient-agent/GoalManager.js +7 -3
- package/plugins/ambient-agent/StateStore.js +30 -1
- package/plugins/ambient-agent/constants.js +15 -1
- package/plugins/ambient-agent/index.js +26 -33
- package/plugins/coordinator-plugin.js +3 -3
- package/plugins/default-plugins.js +2 -2
- package/plugins/email/index.js +150 -36
- package/plugins/email/monitor.js +79 -5
- package/plugins/email/reply.js +15 -25
- package/plugins/extension-executor-plugin.js +160 -31
- package/plugins/file-system-plugin.js +57 -24
- package/plugins/memory-plugin.js +176 -64
- package/plugins/python-plugin-loader.js +79 -9
- package/plugins/scheduler-plugin.js +64 -24
- package/plugins/think-plugin.js +7 -2
- package/plugins/web-plugin.js +263 -4
- package/skills/ambient-agent/SKILL.md +342 -314
- package/src/core/agent-chat.js +64 -9
- package/src/core/agent.js +118 -59
- package/src/core/tool-registry.js +5 -5
- package/src/executors/mcp-executor.js +188 -26
- package/src/utils/id.js +5 -0
- package/system.md +3480 -0
- package/.agent/data/ambient/goals.json +0 -50
- package/.agent/data/ambient/memories.json +0 -7
- package/.agent/memory/core.md +0 -1
- package/.agent/memory/feedback/mnrsiuoc-e1ru74.md +0 -9
- package/.agent/memory/feedback/mnrt2mmz-98az6n.md +0 -9
- package/.agent/memory/feedback/mnrtqrhm-kxsicz.md +0 -9
- package/.agent/memory/feedback/mnrts8vg-i0ngzp.md +0 -15
- package/.agent/memory/feedback/mnrtt7jt-c0trb2.md +0 -9
- package/.agent/memory/feedback/mnruc2f0-5s52la.md +0 -16
- package/.agent/memory/feedback/mnrumbmx-63sa0v.md +0 -9
- package/.agent/memory/project/mnn93ogy-ypjn27.md +0 -9
- package/.agent/memory/project/mnn98fqy-5nhc1u.md +0 -25
- package/.agent/memory/project/mnrp7p5n-8enm2a.md +0 -31
- package/.agent/memory/project/mnrp9ifb-yynks0.md +0 -40
- package/.agent/memory/project/mnrpb3b8-f617s4.md +0 -25
- package/.agent/memory/project/mnrrmqgg-focprv.md +0 -9
- package/.agent/memory/project/mnrtykbh-6atsor.md +0 -9
- package/.agent/memory/project/mnru9jiu-kgau16.md +0 -35
- package/.agent/memory/reference/mnq3oenw-46haj6.md +0 -63
- package/.agent/memory/reference/mnq5qxm2-mjoooh.md +0 -116
- package/.agent/memory/reference/mnrnvpwo-rcqv9m.md +0 -52
- package/.agent/memory/reference/mnrovxvz-zy9xqm.md +0 -25
- package/.agent/memory/reference/mnroxabj-1b3930.md +0 -68
- package/.agent/memory/reference/mnrpjtlp-mnb9od.md +0 -35
- package/.agent/memory/reference/mnrps1x3-6b8xfm.md +0 -28
- package/.agent/memory/reference/mnrpt9ov-15er5w.md +0 -22
- package/.agent/memory/reference/mnrq82dn-y9tv9e.md +0 -50
- package/.agent/memory/reference/mnrqnr5v-v75drf.md +0 -34
- package/.agent/memory/reference/mnrrfzys-urudaf.md +0 -31
- package/.agent/memory/reference/mnrrocha-t0027n.md +0 -21
- package/.agent/memory/reference/mnrukklc-bxndsb.md +0 -35
- package/.agent/memory/user/mnm67t9m-x8rekk.md +0 -9
- package/.agent/memory/user/mnn5mmqh-w6aktx.md +0 -11
- package/.agent/memory/user/mnnbfhhn-dk1bd1.md +0 -22
- package/.agent/memory/user/mnrt39t8-8eosy0.md +0 -9
- package/foliko-cloud-rising.png +0 -0
- package/foliko-dawn-of-ai.png +0 -0
- package/foliko-mindful-observation.png +0 -0
- package/foliko-stellar-dreams.png +0 -0
- package/foliko-zen-jing.png +0 -0
- package/foliko-zen-kong.png +0 -0
- package/foliko-zen-wu.png +0 -0
- package/zen_karesansui.png +0 -0
package/plugins/memory-plugin.js
CHANGED
|
@@ -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
|
-
//
|
|
815
|
-
framework.on('agent:message',
|
|
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
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
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
|
-
//
|
|
841
|
+
// 启动定时提取器
|
|
842
|
+
this._startPeriodicExtraction()
|
|
843
|
+
|
|
844
|
+
// 监听所有工具调用错误 - 收集到队列,定时处理
|
|
830
845
|
framework.on('tool:error', async (data) => {
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
}
|
|
839
|
-
|
|
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
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
}
|
|
853
|
-
|
|
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
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
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
|
-
|
|
875
|
-
|
|
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
|
-
|
|
890
|
-
|
|
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
|
-
|
|
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
|
|
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 +=
|
|
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
|
|
112
|
-
|
|
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 += '
|
|
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:
|
|
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 ${
|
|
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
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
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
|
-
//
|
|
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('
|
|
596
|
-
|
|
597
|
-
|
|
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)
|
package/plugins/think-plugin.js
CHANGED
|
@@ -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',
|
|
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 {
|