foliko 1.0.75 → 1.0.76

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 (88) hide show
  1. package/.claude/settings.local.json +159 -157
  2. package/cli/bin/foliko.js +12 -12
  3. package/cli/src/commands/chat.js +143 -143
  4. package/cli/src/commands/list.js +93 -93
  5. package/cli/src/index.js +75 -75
  6. package/cli/src/ui/chat-ui.js +201 -201
  7. package/cli/src/utils/ansi.js +40 -40
  8. package/cli/src/utils/markdown.js +292 -292
  9. package/examples/ambient-example.js +194 -194
  10. package/examples/basic.js +115 -115
  11. package/examples/bootstrap.js +121 -121
  12. package/examples/mcp-example.js +56 -56
  13. package/examples/skill-example.js +49 -49
  14. package/examples/test-chat.js +137 -137
  15. package/examples/test-mcp.js +85 -85
  16. package/examples/test-reload.js +59 -59
  17. package/examples/test-telegram.js +50 -50
  18. package/examples/test-tg-bot.js +45 -45
  19. package/examples/test-tg-simple.js +47 -47
  20. package/examples/test-tg.js +62 -62
  21. package/examples/test-think.js +43 -43
  22. package/examples/test-web-plugin.js +103 -103
  23. package/examples/test-weixin-feishu.js +103 -103
  24. package/examples/workflow.js +158 -158
  25. package/package.json +1 -1
  26. package/plugins/ai-plugin.js +102 -102
  27. package/plugins/ambient-agent/EventWatcher.js +113 -113
  28. package/plugins/ambient-agent/ExplorerLoop.js +640 -640
  29. package/plugins/ambient-agent/GoalManager.js +197 -197
  30. package/plugins/ambient-agent/Reflector.js +95 -95
  31. package/plugins/ambient-agent/StateStore.js +90 -90
  32. package/plugins/ambient-agent/constants.js +101 -101
  33. package/plugins/ambient-agent/index.js +579 -579
  34. package/plugins/audit-plugin.js +187 -187
  35. package/plugins/default-plugins.js +662 -662
  36. package/plugins/email/constants.js +64 -64
  37. package/plugins/email/handlers.js +461 -461
  38. package/plugins/email/index.js +278 -278
  39. package/plugins/email/monitor.js +269 -269
  40. package/plugins/email/parser.js +138 -138
  41. package/plugins/email/reply.js +151 -151
  42. package/plugins/email/utils.js +124 -124
  43. package/plugins/feishu-plugin.js +481 -481
  44. package/plugins/file-system-plugin.js +826 -826
  45. package/plugins/install-plugin.js +199 -199
  46. package/plugins/python-executor-plugin.js +367 -367
  47. package/plugins/python-plugin-loader.js +481 -481
  48. package/plugins/rules-plugin.js +294 -294
  49. package/plugins/scheduler-plugin.js +691 -691
  50. package/plugins/session-plugin.js +369 -369
  51. package/plugins/shell-executor-plugin.js +197 -197
  52. package/plugins/storage-plugin.js +240 -240
  53. package/plugins/subagent-plugin.js +845 -845
  54. package/plugins/telegram-plugin.js +482 -482
  55. package/plugins/think-plugin.js +345 -345
  56. package/plugins/tools-plugin.js +196 -196
  57. package/plugins/web-plugin.js +606 -606
  58. package/plugins/weixin-plugin.js +545 -545
  59. package/src/capabilities/index.js +11 -11
  60. package/src/capabilities/skill-manager.js +609 -609
  61. package/src/capabilities/workflow-engine.js +1109 -1109
  62. package/src/core/agent-chat.js +882 -882
  63. package/src/core/agent.js +892 -892
  64. package/src/core/framework.js +465 -465
  65. package/src/core/index.js +19 -19
  66. package/src/core/plugin-base.js +219 -219
  67. package/src/core/plugin-manager.js +863 -863
  68. package/src/core/provider.js +114 -114
  69. package/src/core/sub-agent-config.js +264 -264
  70. package/src/core/system-prompt-builder.js +120 -120
  71. package/src/core/tool-registry.js +517 -517
  72. package/src/core/tool-router.js +297 -297
  73. package/src/executors/executor-base.js +58 -58
  74. package/src/executors/mcp-executor.js +741 -741
  75. package/src/index.js +25 -25
  76. package/src/utils/circuit-breaker.js +301 -301
  77. package/src/utils/error-boundary.js +363 -363
  78. package/src/utils/error.js +374 -374
  79. package/src/utils/event-emitter.js +97 -97
  80. package/src/utils/id.js +133 -133
  81. package/src/utils/index.js +217 -217
  82. package/src/utils/logger.js +181 -181
  83. package/src/utils/plugin-helpers.js +90 -90
  84. package/src/utils/retry.js +122 -122
  85. package/src/utils/sandbox.js +292 -292
  86. package/test/tool-registry-validation.test.js +218 -218
  87. package/website/script.js +136 -136
  88. package/foliko-1.0.75.tgz +0 -0
@@ -1,138 +1,138 @@
1
- /**
2
- * Email 插件 - 邮件解析器
3
- */
4
-
5
- const { simpleParser } = require('mailparser')
6
-
7
- /**
8
- * 解析邮件内容
9
- * @param {Stream} stream - 邮件流
10
- * @returns {Promise<Object>} 解析后的邮件对象
11
- */
12
- async function parseEmail(stream) {
13
- return await simpleParser(stream)
14
- }
15
-
16
- /**
17
- * 将邮件对象标准化
18
- * @param {Object} mail - mailparser 解析结果
19
- * @param {number} defaultUid - 默认 UID
20
- * @returns {Object} 标准化的邮件对象
21
- */
22
- function normalizeEmail(mail, defaultUid) {
23
- return {
24
- uid: mail.uid || defaultUid,
25
- messageId: mail.messageId,
26
- subject: mail.subject,
27
- from: mail.from?.text || '',
28
- fromName: mail.from?.value?.[0]?.name || '',
29
- fromAddress: mail.from?.value?.[0]?.address || '',
30
- to: mail.to?.text || '',
31
- toName: mail.to?.value?.[0]?.name || '',
32
- toAddress: mail.to?.value?.[0]?.address || '',
33
- date: mail.date?.toISOString() || '',
34
- text: mail.text || mail.textAsHtml || '',
35
- html: mail.html,
36
- attachments: parseAttachments(mail.attachments),
37
- headers: parseHeaders(mail.headers)
38
- }
39
- }
40
-
41
- /**
42
- * 解析附件列表
43
- * @param {Array} attachments - 附件数组
44
- * @returns {Array} 标准化的附件列表
45
- */
46
- function parseAttachments(attachments) {
47
- if (!attachments || !Array.isArray(attachments)) {
48
- return []
49
- }
50
-
51
- return attachments.map(att => ({
52
- filename: att.filename,
53
- contentType: att.contentType,
54
- contentId: att.contentId,
55
- size: att.size,
56
- headers: att.headers
57
- }))
58
- }
59
-
60
- /**
61
- * 解析邮件头
62
- * @param {Map|Object} headers - 邮件头
63
- * @returns {Object} 标准化的头信息
64
- */
65
- function parseHeaders(headers) {
66
- if (!headers) return {}
67
-
68
- const result = {}
69
- if (headers instanceof Map) {
70
- for (const [key, value] of headers) {
71
- result[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : value
72
- }
73
- } else {
74
- for (const [key, value] of Object.entries(headers)) {
75
- result[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : value
76
- }
77
- }
78
-
79
- return result
80
- }
81
-
82
- /**
83
- * 检测邮件编码
84
- * @param {string} charset - 字符集
85
- * @returns {string} 标准化的字符集名称
86
- */
87
- function normalizeCharset(charset) {
88
- if (!charset) return 'utf-8'
89
-
90
- const charsetMap = {
91
- 'gb2312': 'gb18030',
92
- 'gbk': 'gb18030',
93
- 'windows-1252': 'cp1252'
94
- }
95
-
96
- return charsetMap[charset.toLowerCase()] || charset.toLowerCase()
97
- }
98
-
99
- /**
100
- * 清理邮件文本内容
101
- * @param {string} text - 原始文本
102
- * @returns {string} 清理后的文本
103
- */
104
- function cleanText(text) {
105
- if (!text) return ''
106
-
107
- return text
108
- .replace(/\r\n/g, '\n') // 统一换行符
109
- .replace(/\t/g, ' ') // Tab 转为空格
110
- .replace(/ +/g, ' ') // 多个空格合并为一个
111
- .replace(/\n{3,}/g, '\n\n') // 超过两个连续换行合并为两个
112
- .trim()
113
- }
114
-
115
- /**
116
- * 从文本中提取邮箱地址
117
- * @param {string} text - 文本内容
118
- * @returns {Array<string>} 邮箱地址列表
119
- */
120
- function extractEmails(text) {
121
- if (!text) return []
122
-
123
- const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g
124
- const matches = text.match(emailRegex) || []
125
-
126
- // 去重
127
- return [...new Set(matches)]
128
- }
129
-
130
- module.exports = {
131
- parseEmail,
132
- normalizeEmail,
133
- parseAttachments,
134
- parseHeaders,
135
- normalizeCharset,
136
- cleanText,
137
- extractEmails
138
- }
1
+ /**
2
+ * Email 插件 - 邮件解析器
3
+ */
4
+
5
+ const { simpleParser } = require('mailparser')
6
+
7
+ /**
8
+ * 解析邮件内容
9
+ * @param {Stream} stream - 邮件流
10
+ * @returns {Promise<Object>} 解析后的邮件对象
11
+ */
12
+ async function parseEmail(stream) {
13
+ return await simpleParser(stream)
14
+ }
15
+
16
+ /**
17
+ * 将邮件对象标准化
18
+ * @param {Object} mail - mailparser 解析结果
19
+ * @param {number} defaultUid - 默认 UID
20
+ * @returns {Object} 标准化的邮件对象
21
+ */
22
+ function normalizeEmail(mail, defaultUid) {
23
+ return {
24
+ uid: mail.uid || defaultUid,
25
+ messageId: mail.messageId,
26
+ subject: mail.subject,
27
+ from: mail.from?.text || '',
28
+ fromName: mail.from?.value?.[0]?.name || '',
29
+ fromAddress: mail.from?.value?.[0]?.address || '',
30
+ to: mail.to?.text || '',
31
+ toName: mail.to?.value?.[0]?.name || '',
32
+ toAddress: mail.to?.value?.[0]?.address || '',
33
+ date: mail.date?.toISOString() || '',
34
+ text: mail.text || mail.textAsHtml || '',
35
+ html: mail.html,
36
+ attachments: parseAttachments(mail.attachments),
37
+ headers: parseHeaders(mail.headers)
38
+ }
39
+ }
40
+
41
+ /**
42
+ * 解析附件列表
43
+ * @param {Array} attachments - 附件数组
44
+ * @returns {Array} 标准化的附件列表
45
+ */
46
+ function parseAttachments(attachments) {
47
+ if (!attachments || !Array.isArray(attachments)) {
48
+ return []
49
+ }
50
+
51
+ return attachments.map(att => ({
52
+ filename: att.filename,
53
+ contentType: att.contentType,
54
+ contentId: att.contentId,
55
+ size: att.size,
56
+ headers: att.headers
57
+ }))
58
+ }
59
+
60
+ /**
61
+ * 解析邮件头
62
+ * @param {Map|Object} headers - 邮件头
63
+ * @returns {Object} 标准化的头信息
64
+ */
65
+ function parseHeaders(headers) {
66
+ if (!headers) return {}
67
+
68
+ const result = {}
69
+ if (headers instanceof Map) {
70
+ for (const [key, value] of headers) {
71
+ result[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : value
72
+ }
73
+ } else {
74
+ for (const [key, value] of Object.entries(headers)) {
75
+ result[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : value
76
+ }
77
+ }
78
+
79
+ return result
80
+ }
81
+
82
+ /**
83
+ * 检测邮件编码
84
+ * @param {string} charset - 字符集
85
+ * @returns {string} 标准化的字符集名称
86
+ */
87
+ function normalizeCharset(charset) {
88
+ if (!charset) return 'utf-8'
89
+
90
+ const charsetMap = {
91
+ 'gb2312': 'gb18030',
92
+ 'gbk': 'gb18030',
93
+ 'windows-1252': 'cp1252'
94
+ }
95
+
96
+ return charsetMap[charset.toLowerCase()] || charset.toLowerCase()
97
+ }
98
+
99
+ /**
100
+ * 清理邮件文本内容
101
+ * @param {string} text - 原始文本
102
+ * @returns {string} 清理后的文本
103
+ */
104
+ function cleanText(text) {
105
+ if (!text) return ''
106
+
107
+ return text
108
+ .replace(/\r\n/g, '\n') // 统一换行符
109
+ .replace(/\t/g, ' ') // Tab 转为空格
110
+ .replace(/ +/g, ' ') // 多个空格合并为一个
111
+ .replace(/\n{3,}/g, '\n\n') // 超过两个连续换行合并为两个
112
+ .trim()
113
+ }
114
+
115
+ /**
116
+ * 从文本中提取邮箱地址
117
+ * @param {string} text - 文本内容
118
+ * @returns {Array<string>} 邮箱地址列表
119
+ */
120
+ function extractEmails(text) {
121
+ if (!text) return []
122
+
123
+ const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g
124
+ const matches = text.match(emailRegex) || []
125
+
126
+ // 去重
127
+ return [...new Set(matches)]
128
+ }
129
+
130
+ module.exports = {
131
+ parseEmail,
132
+ normalizeEmail,
133
+ parseAttachments,
134
+ parseHeaders,
135
+ normalizeCharset,
136
+ cleanText,
137
+ extractEmails
138
+ }
@@ -1,151 +1,151 @@
1
- /**
2
- * Email 插件 - 自动回复
3
- */
4
-
5
- const { EMAIL_DEFAULTS } = require('./constants')
6
-
7
- /**
8
- * 处理自动回复
9
- * @param {Object} emailPlugin - EmailPlugin 实例
10
- * @param {Object} args - 回复参数
11
- * @returns {Promise<Object>} 回复结果
12
- */
13
- async function handleAutoReply(emailPlugin, args) {
14
- let { to, subject, body, from, _event, prompt, messageId, inReplyTo } = args
15
-
16
- // 如果没有直接参数,尝试从 _event 提取
17
- // 支持多种事件结构:{ email, timestamp } 或 { data: { email } } 或 { data: { ... } }
18
- if (!to && !subject && !body && _event) {
19
- // 兼容多种事件结构
20
- const email = _event.email || _event.data?.email || _event.data || {}
21
- from = email.from?.text || email.from || ''
22
- to = email.to?.text || email.to || ''
23
- subject = email.subject || ''
24
- body = email.text || email.body || ''
25
- // 优先使用 _event 中的 uid/messageId
26
- if (!messageId) {
27
- messageId = email.messageId || email.uid || _event.data?.messageId || _event.data?.uid
28
- }
29
- }
30
-
31
- // 如果 _event 没有有效数据,不允许自动读取所有邮件
32
- if (!_event || (!_event.email && !_event.data)) {
33
- return {
34
- success: false,
35
- error: '缺少邮件数据:_event 中没有有效的邮件信息,无法自动回复。请确认是否在收到新邮件事件时调用此工具。'
36
- }
37
- }
38
-
39
- // 优先使用传入的 inReplyTo,否则用 messageId
40
- const replyTo = inReplyTo || messageId
41
-
42
- // 检查必要参数
43
- if (!from && !to) {
44
- return { success: false, error: '缺少收件人地址' }
45
- }
46
- if (!body) {
47
- return { success: false, error: '缺少邮件内容' }
48
- }
49
-
50
- try {
51
- // 使用子 Agent 生成回复
52
- const replyAgent = emailPlugin._framework.createSubAgent({
53
- name: 'email_replier',
54
- role: '邮件自动回复助手,专注于生成专业、礼貌、简洁的邮件回复',
55
- parentTools: []
56
- })
57
-
58
- // 构建提示让 LLM 生成回复
59
- const finalPrompt = prompt || `你是一封邮件自动回复助手。请根据以下邮件内容,生成一封专业的回复邮件。
60
-
61
- 【原始邮件】
62
- 发件人: ${from || to}
63
- 主题: ${subject}
64
- 内容:
65
- ${body}
66
-
67
- 【要求】
68
- 1. 回复内容要针对邮件中的问题或内容进行回复
69
- 2. 语言要专业、礼貌、简洁
70
- 3. 只输出邮件正文内容,不要额外解释
71
- 4. 回复语言应与原邮件一致(如果原邮件是中文,则用中文回复)`
72
-
73
- // 等待 Agent 生成回复(带超时保护)
74
- const timeoutMs = args.timeout || EMAIL_DEFAULTS.timeout
75
- const timeoutPromise = new Promise((_, reject) => {
76
- setTimeout(() => reject(new Error(`AI回复生成超时(${timeoutMs / 1000}秒)`)), timeoutMs)
77
- })
78
-
79
- const replyPromise = replyAgent.chat(finalPrompt, { maxSteps: 3 })
80
- const replyResult = await Promise.race([replyPromise, timeoutPromise])
81
-
82
- // 提取回复内容
83
- let replyContent = ''
84
- if (typeof replyResult === 'string') {
85
- replyContent = replyResult.trim()
86
- } else if (replyResult && replyResult.content) {
87
- replyContent = replyResult.content.trim()
88
- } else if (replyResult && replyResult.message) {
89
- replyContent = replyResult.message.trim()
90
- } else {
91
- replyContent = JSON.stringify(replyResult).trim()
92
- }
93
-
94
- // 去掉思考过程标签
95
- replyContent = replyContent.replace(/<think>[\s\S]*?<\/think>/g, '').trim()
96
-
97
- if (!replyContent || replyContent.length < 5) {
98
- return { success: false, error: 'AI未能生成有效的回复内容' }
99
- }
100
-
101
- // 发送回复邮件
102
- const { sendEmail } = require('./handlers')
103
- const sendResult = await sendEmail(emailPlugin, {
104
- to: from || to,
105
- subject: `Re: ${subject}`,
106
- body: replyContent,
107
- inReplyTo: replyTo,
108
- references: replyTo ? [replyTo] : undefined
109
- })
110
-
111
- if (sendResult.success) {
112
- // 发送成功后,标记为已处理
113
- const emailData = _event?.email || _event?.data?.email || _event?.data || {}
114
- const msgId = messageId || emailData.messageId || emailData.uid
115
- if (msgId) {
116
- emailPlugin._processedEmails.add(msgId)
117
- emailPlugin._log.info(`邮件已处理: ${msgId}`)
118
-
119
- // 24小时后从已处理列表中移除
120
- setTimeout(() => {
121
- emailPlugin._processedEmails.delete(msgId)
122
- }, EMAIL_DEFAULTS.processedEmailTTL)
123
- }
124
-
125
- // 自动标记原邮件为已读
126
- const uid = emailData.uid || msgId
127
- if (uid) {
128
- try {
129
- const { markAsRead } = require('./handlers')
130
- await markAsRead(emailPlugin, { uid: uid }) // 使用 uid 而非 messageId
131
- } catch (err) {
132
- emailPlugin._log.warn(`自动标记已读失败: ${err.message}`)
133
- }
134
- }
135
-
136
- return {
137
- success: true,
138
- message: `自动回复已发送至 ${from || to}`,
139
- replyContent
140
- }
141
- } else {
142
- return { success: false, error: sendResult.error || '发送失败' }
143
- }
144
- } catch (err) {
145
- return { success: false, error: err.message }
146
- }
147
- }
148
-
149
- module.exports = {
150
- handleAutoReply
151
- }
1
+ /**
2
+ * Email 插件 - 自动回复
3
+ */
4
+
5
+ const { EMAIL_DEFAULTS } = require('./constants')
6
+
7
+ /**
8
+ * 处理自动回复
9
+ * @param {Object} emailPlugin - EmailPlugin 实例
10
+ * @param {Object} args - 回复参数
11
+ * @returns {Promise<Object>} 回复结果
12
+ */
13
+ async function handleAutoReply(emailPlugin, args) {
14
+ let { to, subject, body, from, _event, prompt, messageId, inReplyTo } = args
15
+
16
+ // 如果没有直接参数,尝试从 _event 提取
17
+ // 支持多种事件结构:{ email, timestamp } 或 { data: { email } } 或 { data: { ... } }
18
+ if (!to && !subject && !body && _event) {
19
+ // 兼容多种事件结构
20
+ const email = _event.email || _event.data?.email || _event.data || {}
21
+ from = email.from?.text || email.from || ''
22
+ to = email.to?.text || email.to || ''
23
+ subject = email.subject || ''
24
+ body = email.text || email.body || ''
25
+ // 优先使用 _event 中的 uid/messageId
26
+ if (!messageId) {
27
+ messageId = email.messageId || email.uid || _event.data?.messageId || _event.data?.uid
28
+ }
29
+ }
30
+
31
+ // 如果 _event 没有有效数据,不允许自动读取所有邮件
32
+ if (!_event || (!_event.email && !_event.data)) {
33
+ return {
34
+ success: false,
35
+ error: '缺少邮件数据:_event 中没有有效的邮件信息,无法自动回复。请确认是否在收到新邮件事件时调用此工具。'
36
+ }
37
+ }
38
+
39
+ // 优先使用传入的 inReplyTo,否则用 messageId
40
+ const replyTo = inReplyTo || messageId
41
+
42
+ // 检查必要参数
43
+ if (!from && !to) {
44
+ return { success: false, error: '缺少收件人地址' }
45
+ }
46
+ if (!body) {
47
+ return { success: false, error: '缺少邮件内容' }
48
+ }
49
+
50
+ try {
51
+ // 使用子 Agent 生成回复
52
+ const replyAgent = emailPlugin._framework.createSubAgent({
53
+ name: 'email_replier',
54
+ role: '邮件自动回复助手,专注于生成专业、礼貌、简洁的邮件回复',
55
+ parentTools: []
56
+ })
57
+
58
+ // 构建提示让 LLM 生成回复
59
+ const finalPrompt = prompt || `你是一封邮件自动回复助手。请根据以下邮件内容,生成一封专业的回复邮件。
60
+
61
+ 【原始邮件】
62
+ 发件人: ${from || to}
63
+ 主题: ${subject}
64
+ 内容:
65
+ ${body}
66
+
67
+ 【要求】
68
+ 1. 回复内容要针对邮件中的问题或内容进行回复
69
+ 2. 语言要专业、礼貌、简洁
70
+ 3. 只输出邮件正文内容,不要额外解释
71
+ 4. 回复语言应与原邮件一致(如果原邮件是中文,则用中文回复)`
72
+
73
+ // 等待 Agent 生成回复(带超时保护)
74
+ const timeoutMs = args.timeout || EMAIL_DEFAULTS.timeout
75
+ const timeoutPromise = new Promise((_, reject) => {
76
+ setTimeout(() => reject(new Error(`AI回复生成超时(${timeoutMs / 1000}秒)`)), timeoutMs)
77
+ })
78
+
79
+ const replyPromise = replyAgent.chat(finalPrompt, { maxSteps: 3 })
80
+ const replyResult = await Promise.race([replyPromise, timeoutPromise])
81
+
82
+ // 提取回复内容
83
+ let replyContent = ''
84
+ if (typeof replyResult === 'string') {
85
+ replyContent = replyResult.trim()
86
+ } else if (replyResult && replyResult.content) {
87
+ replyContent = replyResult.content.trim()
88
+ } else if (replyResult && replyResult.message) {
89
+ replyContent = replyResult.message.trim()
90
+ } else {
91
+ replyContent = JSON.stringify(replyResult).trim()
92
+ }
93
+
94
+ // 去掉思考过程标签
95
+ replyContent = replyContent.replace(/<think>[\s\S]*?<\/think>/g, '').trim()
96
+
97
+ if (!replyContent || replyContent.length < 5) {
98
+ return { success: false, error: 'AI未能生成有效的回复内容' }
99
+ }
100
+
101
+ // 发送回复邮件
102
+ const { sendEmail } = require('./handlers')
103
+ const sendResult = await sendEmail(emailPlugin, {
104
+ to: from || to,
105
+ subject: `Re: ${subject}`,
106
+ body: replyContent,
107
+ inReplyTo: replyTo,
108
+ references: replyTo ? [replyTo] : undefined
109
+ })
110
+
111
+ if (sendResult.success) {
112
+ // 发送成功后,标记为已处理
113
+ const emailData = _event?.email || _event?.data?.email || _event?.data || {}
114
+ const msgId = messageId || emailData.messageId || emailData.uid
115
+ if (msgId) {
116
+ emailPlugin._processedEmails.add(msgId)
117
+ emailPlugin._log.info(`邮件已处理: ${msgId}`)
118
+
119
+ // 24小时后从已处理列表中移除
120
+ setTimeout(() => {
121
+ emailPlugin._processedEmails.delete(msgId)
122
+ }, EMAIL_DEFAULTS.processedEmailTTL)
123
+ }
124
+
125
+ // 自动标记原邮件为已读
126
+ const uid = emailData.uid || msgId
127
+ if (uid) {
128
+ try {
129
+ const { markAsRead } = require('./handlers')
130
+ await markAsRead(emailPlugin, { uid: uid }) // 使用 uid 而非 messageId
131
+ } catch (err) {
132
+ emailPlugin._log.warn(`自动标记已读失败: ${err.message}`)
133
+ }
134
+ }
135
+
136
+ return {
137
+ success: true,
138
+ message: `自动回复已发送至 ${from || to}`,
139
+ replyContent
140
+ }
141
+ } else {
142
+ return { success: false, error: sendResult.error || '发送失败' }
143
+ }
144
+ } catch (err) {
145
+ return { success: false, error: err.message }
146
+ }
147
+ }
148
+
149
+ module.exports = {
150
+ handleAutoReply
151
+ }