foliko 1.0.75 → 1.0.77

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 (89) hide show
  1. package/.claude/settings.local.json +159 -157
  2. package/.env.example +3 -1
  3. package/cli/bin/foliko.js +12 -12
  4. package/cli/src/commands/chat.js +143 -143
  5. package/cli/src/commands/list.js +93 -93
  6. package/cli/src/index.js +75 -75
  7. package/cli/src/ui/chat-ui.js +201 -201
  8. package/cli/src/utils/ansi.js +40 -40
  9. package/cli/src/utils/markdown.js +292 -292
  10. package/examples/ambient-example.js +194 -194
  11. package/examples/basic.js +115 -115
  12. package/examples/bootstrap.js +121 -121
  13. package/examples/mcp-example.js +56 -56
  14. package/examples/skill-example.js +49 -49
  15. package/examples/test-chat.js +137 -137
  16. package/examples/test-mcp.js +85 -85
  17. package/examples/test-reload.js +59 -59
  18. package/examples/test-telegram.js +50 -50
  19. package/examples/test-tg-bot.js +45 -45
  20. package/examples/test-tg-simple.js +47 -47
  21. package/examples/test-tg.js +62 -62
  22. package/examples/test-think.js +43 -43
  23. package/examples/test-web-plugin.js +103 -103
  24. package/examples/test-weixin-feishu.js +103 -103
  25. package/examples/workflow.js +158 -158
  26. package/package.json +81 -81
  27. package/plugins/ai-plugin.js +102 -102
  28. package/plugins/ambient-agent/EventWatcher.js +113 -113
  29. package/plugins/ambient-agent/ExplorerLoop.js +640 -640
  30. package/plugins/ambient-agent/GoalManager.js +197 -197
  31. package/plugins/ambient-agent/Reflector.js +95 -95
  32. package/plugins/ambient-agent/StateStore.js +90 -90
  33. package/plugins/ambient-agent/constants.js +101 -101
  34. package/plugins/ambient-agent/index.js +579 -579
  35. package/plugins/audit-plugin.js +187 -187
  36. package/plugins/default-plugins.js +662 -662
  37. package/plugins/email/constants.js +64 -64
  38. package/plugins/email/handlers.js +461 -461
  39. package/plugins/email/index.js +278 -278
  40. package/plugins/email/monitor.js +269 -269
  41. package/plugins/email/parser.js +138 -138
  42. package/plugins/email/reply.js +151 -151
  43. package/plugins/email/utils.js +124 -124
  44. package/plugins/feishu-plugin.js +481 -481
  45. package/plugins/file-system-plugin.js +826 -826
  46. package/plugins/install-plugin.js +199 -199
  47. package/plugins/python-executor-plugin.js +367 -367
  48. package/plugins/python-plugin-loader.js +481 -481
  49. package/plugins/rules-plugin.js +294 -294
  50. package/plugins/scheduler-plugin.js +691 -691
  51. package/plugins/session-plugin.js +369 -369
  52. package/plugins/shell-executor-plugin.js +197 -197
  53. package/plugins/storage-plugin.js +240 -240
  54. package/plugins/subagent-plugin.js +845 -845
  55. package/plugins/telegram-plugin.js +482 -482
  56. package/plugins/think-plugin.js +345 -345
  57. package/plugins/tools-plugin.js +196 -196
  58. package/plugins/web-plugin.js +606 -606
  59. package/plugins/weixin-plugin.js +545 -545
  60. package/src/capabilities/index.js +11 -11
  61. package/src/capabilities/skill-manager.js +609 -609
  62. package/src/capabilities/workflow-engine.js +1109 -1109
  63. package/src/core/agent-chat.js +882 -882
  64. package/src/core/agent.js +892 -892
  65. package/src/core/framework.js +465 -465
  66. package/src/core/index.js +19 -19
  67. package/src/core/plugin-base.js +219 -219
  68. package/src/core/plugin-manager.js +863 -863
  69. package/src/core/provider.js +114 -114
  70. package/src/core/sub-agent-config.js +264 -264
  71. package/src/core/system-prompt-builder.js +120 -120
  72. package/src/core/tool-registry.js +517 -517
  73. package/src/core/tool-router.js +297 -297
  74. package/src/executors/executor-base.js +58 -58
  75. package/src/executors/mcp-executor.js +741 -741
  76. package/src/index.js +25 -25
  77. package/src/utils/circuit-breaker.js +301 -301
  78. package/src/utils/error-boundary.js +363 -363
  79. package/src/utils/error.js +374 -374
  80. package/src/utils/event-emitter.js +97 -97
  81. package/src/utils/id.js +133 -133
  82. package/src/utils/index.js +217 -217
  83. package/src/utils/logger.js +181 -181
  84. package/src/utils/plugin-helpers.js +90 -90
  85. package/src/utils/retry.js +122 -122
  86. package/src/utils/sandbox.js +292 -292
  87. package/test/tool-registry-validation.test.js +218 -218
  88. package/website/script.js +136 -136
  89. 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
+ }