foliko 1.0.74 → 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 (238) hide show
  1. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +26 -0
  2. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
  3. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +101 -0
  4. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
  5. package/.agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
  6. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
  7. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
  8. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  9. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  10. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  11. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  12. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  13. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  14. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  15. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
  16. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  17. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  18. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  19. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  20. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
  21. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
  22. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  23. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  24. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
  25. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  26. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  27. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +258 -0
  28. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +1067 -0
  29. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +106 -0
  30. package/.agent/ARCHITECTURE.md +288 -0
  31. package/.agent/agents/ambient-agent.md +57 -0
  32. package/.agent/agents/debugger.md +55 -0
  33. package/.agent/agents/email-assistant.md +49 -0
  34. package/.agent/agents/file-manager.md +42 -0
  35. package/.agent/agents/python-developer.md +60 -0
  36. package/.agent/agents/scheduler.md +59 -0
  37. package/.agent/agents/web-developer.md +45 -0
  38. package/.agent/data/default.json +29 -0
  39. package/.agent/data/plugins-state.json +255 -0
  40. package/.agent/mcp_config.json +4 -0
  41. package/.agent/mcp_config_updated.json +12 -0
  42. package/.agent/plugins.json +5 -0
  43. package/.agent/rules/GEMINI.md +273 -0
  44. package/.agent/rules/allow-rule.md +77 -0
  45. package/.agent/rules/log-rule.md +83 -0
  46. package/.agent/rules/security-rule.md +93 -0
  47. package/.agent/scripts/auto_preview.py +148 -0
  48. package/.agent/scripts/checklist.py +217 -0
  49. package/.agent/scripts/session_manager.py +120 -0
  50. package/.agent/scripts/verify_all.py +327 -0
  51. package/.agent/skills/api-patterns/SKILL.md +81 -0
  52. package/.agent/skills/api-patterns/api-style.md +42 -0
  53. package/.agent/skills/api-patterns/auth.md +24 -0
  54. package/.agent/skills/api-patterns/documentation.md +26 -0
  55. package/.agent/skills/api-patterns/graphql.md +41 -0
  56. package/.agent/skills/api-patterns/rate-limiting.md +31 -0
  57. package/.agent/skills/api-patterns/response.md +37 -0
  58. package/.agent/skills/api-patterns/rest.md +40 -0
  59. package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
  60. package/.agent/skills/api-patterns/security-testing.md +122 -0
  61. package/.agent/skills/api-patterns/trpc.md +41 -0
  62. package/.agent/skills/api-patterns/versioning.md +22 -0
  63. package/.agent/skills/app-builder/SKILL.md +75 -0
  64. package/.agent/skills/app-builder/agent-coordination.md +71 -0
  65. package/.agent/skills/app-builder/feature-building.md +53 -0
  66. package/.agent/skills/app-builder/project-detection.md +34 -0
  67. package/.agent/skills/app-builder/scaffolding.md +118 -0
  68. package/.agent/skills/app-builder/tech-stack.md +40 -0
  69. package/.agent/skills/app-builder/templates/SKILL.md +39 -0
  70. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
  71. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
  72. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
  73. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
  74. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
  75. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
  76. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
  77. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
  78. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
  79. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
  80. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
  81. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
  82. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
  83. package/.agent/skills/architecture/SKILL.md +55 -0
  84. package/.agent/skills/architecture/context-discovery.md +43 -0
  85. package/.agent/skills/architecture/examples.md +94 -0
  86. package/.agent/skills/architecture/pattern-selection.md +68 -0
  87. package/.agent/skills/architecture/patterns-reference.md +50 -0
  88. package/.agent/skills/architecture/trade-off-analysis.md +77 -0
  89. package/.agent/skills/clean-code/SKILL.md +201 -0
  90. package/.agent/skills/doc.md +177 -0
  91. package/.agent/skills/frontend-design/SKILL.md +418 -0
  92. package/.agent/skills/frontend-design/animation-guide.md +331 -0
  93. package/.agent/skills/frontend-design/color-system.md +311 -0
  94. package/.agent/skills/frontend-design/decision-trees.md +418 -0
  95. package/.agent/skills/frontend-design/motion-graphics.md +306 -0
  96. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
  97. package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
  98. package/.agent/skills/frontend-design/typography-system.md +345 -0
  99. package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
  100. package/.agent/skills/frontend-design/visual-effects.md +383 -0
  101. package/.agent/skills/i18n-localization/SKILL.md +154 -0
  102. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
  103. package/.agent/skills/mcp-builder/SKILL.md +176 -0
  104. package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
  105. package/.agent/workflows/brainstorm.md +113 -0
  106. package/.agent/workflows/create.md +59 -0
  107. package/.agent/workflows/debug.md +103 -0
  108. package/.agent/workflows/deploy.md +176 -0
  109. package/.agent/workflows/enhance.md +63 -0
  110. package/.agent/workflows/orchestrate.md +237 -0
  111. package/.agent/workflows/plan.md +89 -0
  112. package/.agent/workflows/preview.md +81 -0
  113. package/.agent/workflows/simple-test.md +42 -0
  114. package/.agent/workflows/status.md +86 -0
  115. package/.agent/workflows/structured-orchestrate.md +180 -0
  116. package/.agent/workflows/test.md +144 -0
  117. package/.agent/workflows/ui-ux-pro-max.md +296 -0
  118. package/.claude/settings.local.json +11 -1
  119. package/.editorconfig +56 -0
  120. package/.husky/pre-commit +4 -0
  121. package/.lintstagedrc +7 -0
  122. package/.prettierignore +29 -0
  123. package/.prettierrc +11 -0
  124. package/CLAUDE.md +2 -0
  125. package/README.md +64 -55
  126. package/SPEC.md +102 -61
  127. package/cli/bin/foliko.js +11 -11
  128. package/cli/src/commands/chat.js +143 -141
  129. package/cli/src/commands/list.js +93 -90
  130. package/cli/src/index.js +75 -75
  131. package/cli/src/ui/chat-ui.js +201 -199
  132. package/cli/src/utils/ansi.js +40 -40
  133. package/cli/src/utils/markdown.js +292 -296
  134. package/docker-compose.yml +1 -1
  135. package/docs/ai-sdk-optimization.md +655 -643
  136. package/docs/features.md +80 -80
  137. package/docs/quick-reference.md +49 -46
  138. package/docs/user-manual.md +411 -380
  139. package/examples/ambient-example.js +194 -196
  140. package/examples/basic.js +50 -45
  141. package/examples/bootstrap.js +121 -112
  142. package/examples/mcp-example.js +19 -16
  143. package/examples/skill-example.js +20 -20
  144. package/examples/test-chat.js +137 -135
  145. package/examples/test-mcp.js +85 -79
  146. package/examples/test-reload.js +59 -61
  147. package/examples/test-telegram.js +50 -50
  148. package/examples/test-tg-bot.js +45 -42
  149. package/examples/test-tg-simple.js +47 -46
  150. package/examples/test-tg.js +62 -62
  151. package/examples/test-think.js +43 -37
  152. package/examples/test-web-plugin.js +103 -98
  153. package/examples/test-weixin-feishu.js +103 -100
  154. package/examples/workflow.js +158 -158
  155. package/package.json +37 -3
  156. package/plugins/ai-plugin.js +102 -100
  157. package/plugins/ambient-agent/EventWatcher.js +113 -0
  158. package/plugins/ambient-agent/ExplorerLoop.js +640 -0
  159. package/plugins/ambient-agent/GoalManager.js +197 -0
  160. package/plugins/ambient-agent/Reflector.js +95 -0
  161. package/plugins/ambient-agent/StateStore.js +90 -0
  162. package/plugins/ambient-agent/constants.js +101 -0
  163. package/plugins/ambient-agent/index.js +579 -0
  164. package/plugins/audit-plugin.js +187 -187
  165. package/plugins/default-plugins.js +662 -649
  166. package/plugins/email/constants.js +64 -0
  167. package/plugins/email/handlers.js +461 -0
  168. package/plugins/email/index.js +278 -0
  169. package/plugins/email/monitor.js +269 -0
  170. package/plugins/email/parser.js +138 -0
  171. package/plugins/email/reply.js +151 -0
  172. package/plugins/email/utils.js +124 -0
  173. package/plugins/feishu-plugin.js +481 -477
  174. package/plugins/file-system-plugin.js +826 -476
  175. package/plugins/install-plugin.js +199 -197
  176. package/plugins/python-executor-plugin.js +367 -365
  177. package/plugins/python-plugin-loader.js +481 -479
  178. package/plugins/rules-plugin.js +294 -292
  179. package/plugins/scheduler-plugin.js +691 -689
  180. package/plugins/session-plugin.js +369 -367
  181. package/plugins/shell-executor-plugin.js +197 -197
  182. package/plugins/storage-plugin.js +240 -238
  183. package/plugins/subagent-plugin.js +845 -785
  184. package/plugins/telegram-plugin.js +482 -475
  185. package/plugins/think-plugin.js +345 -343
  186. package/plugins/tools-plugin.js +196 -194
  187. package/plugins/web-plugin.js +606 -604
  188. package/plugins/weixin-plugin.js +545 -538
  189. package/reports/system-health-report-20260401.md +79 -0
  190. package/skills/ambient-agent/SKILL.md +49 -39
  191. package/skills/foliko-dev/AGENTS.md +64 -61
  192. package/skills/foliko-dev/SKILL.md +125 -119
  193. package/skills/mcp-usage/SKILL.md +19 -17
  194. package/skills/python-plugin-dev/SKILL.md +16 -15
  195. package/skills/skill-guide/SKILL.md +12 -12
  196. package/skills/subagent-guide/SKILL.md +237 -0
  197. package/skills/workflow-guide/SKILL.md +90 -45
  198. package/skills/workflow-troubleshooting/DEBUGGING.md +36 -21
  199. package/skills/workflow-troubleshooting/SKILL.md +156 -79
  200. package/src/capabilities/index.js +11 -11
  201. package/src/capabilities/skill-manager.js +609 -595
  202. package/src/capabilities/workflow-engine.js +1109 -1195
  203. package/src/core/agent-chat.js +882 -735
  204. package/src/core/agent.js +892 -688
  205. package/src/core/framework.js +465 -431
  206. package/src/core/index.js +19 -19
  207. package/src/core/plugin-base.js +219 -219
  208. package/src/core/plugin-manager.js +863 -767
  209. package/src/core/provider.js +114 -111
  210. package/src/core/sub-agent-config.js +264 -0
  211. package/src/core/system-prompt-builder.js +120 -0
  212. package/src/core/tool-registry.js +517 -134
  213. package/src/core/tool-router.js +297 -216
  214. package/src/executors/executor-base.js +12 -12
  215. package/src/executors/mcp-executor.js +741 -729
  216. package/src/index.js +25 -37
  217. package/src/utils/circuit-breaker.js +301 -0
  218. package/src/utils/error-boundary.js +363 -0
  219. package/src/utils/error.js +374 -0
  220. package/src/utils/event-emitter.js +97 -97
  221. package/src/utils/id.js +133 -0
  222. package/src/utils/index.js +217 -3
  223. package/src/utils/logger.js +181 -0
  224. package/src/utils/plugin-helpers.js +90 -0
  225. package/src/utils/retry.js +122 -0
  226. package/src/utils/sandbox.js +292 -0
  227. package/test/tool-registry-validation.test.js +218 -0
  228. package/test_report.md +70 -0
  229. package/website/docs/api.html +169 -107
  230. package/website/docs/configuration.html +296 -144
  231. package/website/docs/plugin-development.html +154 -85
  232. package/website/docs/project-structure.html +110 -109
  233. package/website/docs/skill-development.html +117 -61
  234. package/website/index.html +209 -205
  235. package/website/script.js +136 -133
  236. package/website/styles.css +1 -1
  237. package/plugins/ambient-agent-plugin.js +0 -1565
  238. package/plugins/email.js +0 -1142
@@ -0,0 +1,640 @@
1
+ /**
2
+ * ExplorerLoop - 主自主循环,决定并执行操作
3
+ */
4
+
5
+ const { Plugin } = require('../../src/core/plugin-base')
6
+ const { logger } = require('../../src/utils/logger')
7
+ const { Agent } = require('../../src/core/agent')
8
+ const { StepExecutor } = require('../../src/capabilities/workflow-engine')
9
+ const { SystemPrompts, ThinkModePrompts, FreeThinkPrompts } = require('./constants')
10
+
11
+ const log = logger.child('Ambient:ExplorerLoop')
12
+
13
+ class ExplorerLoop {
14
+ constructor(goalManager, reflector, framework, config) {
15
+ this._goalManager = goalManager
16
+ this._reflector = reflector
17
+ this._framework = framework
18
+ this._config = config
19
+ this._running = false
20
+ this._timer = null
21
+ this._lastActionTime = 0
22
+ this._tickCount = 0
23
+ this._recentActivities = []
24
+ this._stepExecutor = new StepExecutor(framework)
25
+ this._ambientAgent = null
26
+ this._initAmbientAgent()
27
+ }
28
+
29
+ // ============================================================================
30
+ // 生命周期
31
+ // ============================================================================
32
+
33
+ /**
34
+ * 初始化 Ambient 专用子 Agent
35
+ */
36
+ _initAmbientAgent() {
37
+ const aiPlugin = this._framework.pluginManager?.get('ai')
38
+ const llmConfig = aiPlugin ? aiPlugin.getConfig() : {}
39
+
40
+ this._ambientAgent = new Agent(this._framework, {
41
+ name: 'ambient-worker',
42
+ systemPrompt: SystemPrompts.ambientWorker,
43
+ model: llmConfig.model,
44
+ provider: llmConfig.provider,
45
+ apiKey: llmConfig.apiKey,
46
+ baseURL: llmConfig.baseURL
47
+ })
48
+
49
+ // 注册到主 agent(如果主 agent 已存在)
50
+ if (this._framework._mainAgent) {
51
+ this._registerAmbientWorker(this._framework._mainAgent)
52
+ } else {
53
+ // 等待主 agent 创建
54
+ this._framework.once('agent:created', (agent) => {
55
+ if (this._framework._mainAgent && agent === this._framework._mainAgent) {
56
+ this._registerAmbientWorker(agent)
57
+ }
58
+ })
59
+ }
60
+ }
61
+
62
+ /**
63
+ * 注册 ambient-worker 到主 agent
64
+ */
65
+ _registerAmbientWorker(agent) {
66
+ if (!this._ambientAgent) return
67
+ agent.registerSubAgent(
68
+ 'ambient-worker',
69
+ this._ambientAgent,
70
+ '后台任务执行器',
71
+ '执行需要 AI 能力的后台任务'
72
+ )
73
+ log.info('ambient-worker subagent registered')
74
+ }
75
+
76
+ /**
77
+ * 启动探索循环
78
+ */
79
+ start() {
80
+ if (this._running) return
81
+ this._running = true
82
+ this._scheduleNext()
83
+ }
84
+
85
+ /**
86
+ * 停止探索循环
87
+ */
88
+ stop() {
89
+ this._running = false
90
+ if (this._timer) {
91
+ clearTimeout(this._timer)
92
+ this._timer = null
93
+ }
94
+ }
95
+
96
+ /**
97
+ * 暂停探索循环
98
+ */
99
+ pause() {
100
+ this._running = false
101
+ if (this._timer) {
102
+ clearTimeout(this._timer)
103
+ this._timer = null
104
+ }
105
+ }
106
+
107
+ /**
108
+ * 恢复探索循环
109
+ */
110
+ resume() {
111
+ if (!this._running) {
112
+ this._running = true
113
+ this._scheduleNext()
114
+ }
115
+ }
116
+
117
+ /**
118
+ * 检查是否运行中
119
+ */
120
+ isRunning() {
121
+ return this._running
122
+ }
123
+
124
+ /**
125
+ * 获取状态
126
+ */
127
+ getStatus() {
128
+ return {
129
+ running: this._running,
130
+ tickCount: this._tickCount,
131
+ lastTick: this._lastActionTime ? new Date(this._lastActionTime) : null,
132
+ recentActivities: this._recentActivities.slice(-10),
133
+ activeGoals: this._goalManager.getActiveGoals().length,
134
+ pendingGoals: this._goalManager.getPendingGoals().length
135
+ }
136
+ }
137
+
138
+ // ============================================================================
139
+ // 主循环
140
+ // ============================================================================
141
+
142
+ _scheduleNext() {
143
+ if (!this._running) return
144
+ this._timer = setTimeout(() => this._tick(), this._config.tickInterval)
145
+ }
146
+
147
+ async _tick() {
148
+ if (!this._running) return
149
+
150
+ // 如果处于嵌套执行上下文中,跳过此tick
151
+ try {
152
+ if (this._framework.getExecutionContext) {
153
+ const ctx = this._framework.getExecutionContext()
154
+ if (ctx && ctx.id) {
155
+ // 跳过此tick,重新调度
156
+ this._scheduleNext()
157
+ return
158
+ }
159
+ }
160
+ } catch (e) {
161
+ // getExecutionContext可能不存在,继续执行
162
+ }
163
+
164
+ this._tickCount++
165
+
166
+ try {
167
+ await this._processGoals()
168
+ } catch (err) {
169
+ log.error('Tick错误:', err.message)
170
+ this._addActivity('error', { message: err.message })
171
+ }
172
+ this._lastActionTime = Date.now()
173
+ // 调度下一次tick
174
+ this._scheduleNext()
175
+ }
176
+
177
+ async _processGoals() {
178
+ const activeGoals = this._goalManager.getActiveGoals()
179
+
180
+ for (const goal of activeGoals) {
181
+ // 检查冷却时间
182
+ if (Date.now() - this._lastActionTime < this._config.cooldownPeriod) {
183
+ continue
184
+ }
185
+
186
+ // 检查目标是否有未处理的事件
187
+ const hasNewEvents = goal.eventsReceived && goal.eventsReceived.length > 0
188
+
189
+ // 检查是否是事件驱动目标
190
+ const isEventDriven = goal.conditions?.events && goal.conditions.events.length > 0
191
+
192
+ // 事件驱动目标:只有在新事件时才执行 actions
193
+ if (isEventDriven && !hasNewEvents) {
194
+ continue
195
+ }
196
+
197
+ // 获取下一个操作
198
+ if (goal.actions && goal.actions.length > 0) {
199
+ // 如果有多个 action,按顺序执行所有 action
200
+ if (goal.actions.length > 1) {
201
+ const eventData = hasNewEvents ? goal.eventsReceived[0] : null
202
+ const results = await this._executeAllActions(goal.actions, eventData)
203
+
204
+ // 最后一个结果用于评估
205
+ const lastResult = results.length > 0 ? results[results.length - 1].result : null
206
+
207
+ // 评估结果
208
+ const outcome = this._reflector.evaluateOutcome(goal, lastResult)
209
+
210
+ if (outcome.goalComplete && !isEventDriven) {
211
+ // 非事件驱动目标可以完成
212
+ this._goalManager.completeGoal(goal.id)
213
+ this._addActivity('goal_completed', { goalId: goal.id, attempts: goal.attempts })
214
+ this._notifyUser('目标完成', `目标 "${goal.title}" 已完成(尝试次数:${goal.attempts})`, 'success')
215
+ } else if (!outcome.shouldContinue && !isEventDriven) {
216
+ // 非事件驱动目标可以失败
217
+ this._goalManager.failGoal(goal.id, '达到最大尝试次数')
218
+ this._addActivity('goal_failed', { goalId: goal.id, reason: '达到最大尝试次数' })
219
+ this._notifyUser('目标失败', `目标 "${goal.title}" 失败:达到最大尝试次数`, 'error')
220
+ } else {
221
+ // 事件驱动目标:不论结果如何,都重置并继续等待下一事件
222
+ this._addActivity('actions_completed', { goalId: goal.id, actionCount: results.length })
223
+ }
224
+
225
+ // 执行操作后清除已处理的事件,并重置 actions 以处理下一事件
226
+ if (hasNewEvents) {
227
+ this._resetGoalForNextEvent(goal)
228
+ }
229
+ continue
230
+ }
231
+
232
+ // 单个 action,使用原有逻辑
233
+ const action = goal.actions[0]
234
+
235
+ // 循环检测(仅用于非事件驱动的操作)
236
+ if (!hasNewEvents && this._reflector.checkLoopDetection(goal, action.id)) {
237
+ this._addActivity('goal_failed', { goalId: goal.id, reason: '检测到循环' })
238
+ // 通知用户目标失败
239
+ this._notifyUser('目标失败', `目标 "${goal.title}" 失败:检测到循环(同一操作连续重复)`, 'error')
240
+ continue
241
+ }
242
+
243
+ // 执行操作
244
+ const result = await this._executeAction(action, hasNewEvents ? goal.eventsReceived[0] : null)
245
+
246
+ // 评估结果
247
+ const outcome = this._reflector.evaluateOutcome(goal, result)
248
+
249
+ if (outcome.goalComplete) {
250
+ this._goalManager.completeGoal(goal.id)
251
+ this._addActivity('goal_completed', { goalId: goal.id, attempts: goal.attempts })
252
+ // 通知用户目标完成
253
+ this._notifyUser('目标完成', `目标 "${goal.title}" 已完成(尝试次数:${goal.attempts})`, 'success')
254
+ } else if (!outcome.shouldContinue) {
255
+ this._goalManager.failGoal(goal.id, '达到最大尝试次数')
256
+ this._addActivity('goal_failed', { goalId: goal.id, reason: '达到最大尝试次数' })
257
+ // 通知用户目标失败
258
+ this._notifyUser('目标失败', `目标 "${goal.title}" 失败:达到最大尝试次数`, 'error')
259
+ } else if (outcome.actionTaken) {
260
+ // 从队列中移除已完成的操作
261
+ if (!hasNewEvents) {
262
+ goal.actions.shift()
263
+ }
264
+ this._addActivity('action_executed', { goalId: goal.id, action: action.id, hasEvents: hasNewEvents })
265
+ }
266
+
267
+ // 执行操作后清除已处理的事件,并重置 actions 以处理下一事件
268
+ if (hasNewEvents) {
269
+ this._resetGoalForNextEvent(goal)
270
+ }
271
+ }
272
+ }
273
+
274
+ // 激活满足条件的待处理目标
275
+ const pendingGoals = this._goalManager.getPendingGoals()
276
+ for (const goal of pendingGoals) {
277
+ // 自动激活没有条件的目标
278
+ // 有条件的目标会在事件触发时被 EventWatcher 自动激活
279
+ if (!goal.conditions || Object.keys(goal.conditions).length === 0) {
280
+ this._goalManager.activateGoal(goal.id)
281
+ this._addActivity('goal_activated', { goalId: goal.id })
282
+ } else {
283
+ // 有条件的目标,立即激活以便开始监听事件
284
+ this._goalManager.activateGoal(goal.id)
285
+ this._addActivity('goal_activated', { goalId: goal.id, reason: '事件监听目标已激活' })
286
+ }
287
+ }
288
+ }
289
+
290
+ /**
291
+ * 重置目标以处理下一事件
292
+ */
293
+ _resetGoalForNextEvent(goal) {
294
+ goal.eventsReceived = []
295
+ // 重置 actions 为原始 actions,以便处理下一事件
296
+ if (goal._originalActions) {
297
+ goal.actions = goal._originalActions.slice()
298
+ }
299
+ this._goalManager.updateGoal(goal.id, { eventsReceived: [], actions: goal.actions })
300
+ }
301
+
302
+ // ============================================================================
303
+ // Action 执行
304
+ // ============================================================================
305
+
306
+ /**
307
+ * 执行单个 action
308
+ * @param {Object} action - action 配置
309
+ * @param {Object} eventData - 事件数据
310
+ * @param {Object} context - 执行上下文(包含 stepOutputs 等)
311
+ * @returns {Promise<Object>} 执行结果
312
+ */
313
+ async _executeAction(action, eventData = null, context = null) {
314
+ if (!this._framework) {
315
+ return { success: false, error: '框架不可用' }
316
+ }
317
+
318
+ try {
319
+ // 如果没有提供 context,创建一个新的
320
+ if (!context) {
321
+ context = {
322
+ input: {},
323
+ variables: {
324
+ _event: eventData?.data || eventData || null,
325
+ _eventRaw: eventData || null,
326
+ _action: action
327
+ },
328
+ lastResult: null
329
+ }
330
+ } else {
331
+ // 更新 context 中的 _action
332
+ context.variables._action = action
333
+ }
334
+
335
+ // 将 action 转换为 step 配置
336
+ const step = this._actionToStep(action)
337
+
338
+ // 解析 step.args 中的变量引用(支持 ${actionId.output} 格式)
339
+ if (step.args) {
340
+ step.args = this._resolveActionArgs(step.args, context)
341
+ }
342
+ if (step.input) {
343
+ step.input = this._resolveActionArgs(step.input, context)
344
+ }
345
+
346
+ // message 和 think 类型使用专用子 agent 执行
347
+ if (step.type === 'message' || step.type === 'think') {
348
+ return await this._executeWithAmbientAgent(step, context)
349
+ }
350
+
351
+ // 其他类型使用 StepExecutor 直接执行
352
+ const result = await this._stepExecutor.executeStep(step, context)
353
+ return result
354
+ } catch (err) {
355
+ return { success: false, error: err.message }
356
+ }
357
+ }
358
+
359
+ /**
360
+ * 解析 action 参数中的变量引用
361
+ * 支持 ${actionId.output}、${actionId.output.field} 和 ${event.xxx} 格式
362
+ */
363
+ _resolveActionArgs(args, context) {
364
+ if (!args || typeof args !== 'object') return args
365
+
366
+ const stepOutputs = context.variables._stepOutputs || {}
367
+ const eventData = context.variables._event || {}
368
+
369
+ const resolved = {}
370
+ for (const [key, value] of Object.entries(args)) {
371
+ resolved[key] = this._resolveActionValue(value, stepOutputs, eventData)
372
+ }
373
+ return resolved
374
+ }
375
+
376
+ /**
377
+ * 解析单个值中的 ${actionId.output} 引用和 ${event.xxx} 引用
378
+ */
379
+ _resolveActionValue(value, stepOutputs, eventData) {
380
+ if (typeof value === 'string') {
381
+ // 处理 ${actionId.output} 和 ${actionId.output.field} 格式
382
+ const result = value.replace(/\$\{([^}]+)\}/g, (match, path) => {
383
+ if (path === 'output' || path === 'result') {
384
+ // {{result}} 引用上一步结果
385
+ return stepOutputs._lastResult ?? match
386
+ }
387
+ if (path.startsWith('event.')) {
388
+ // ${event.xxx} 引用事件数据
389
+ let eventPath = path.replace(/^event\./, '')
390
+ // 优先在 eventData.data 中查找(因为事件数据通常存储在 data 字段)
391
+ if (eventData?.data) {
392
+ let val = this._getNestedValue(eventData.data, eventPath)
393
+ if (val !== undefined) return val
394
+ }
395
+ // 再尝试直接在 eventData 中查找
396
+ let val = this._getNestedValue(eventData, eventPath)
397
+ if (val !== undefined) return val
398
+ // 兼容:如果 path 是 event.data.xxx 但 eventData 没有 data,尝试跳过一层
399
+ if (eventPath.startsWith('data.')) {
400
+ eventPath = eventPath.replace(/^data\./, '')
401
+ val = this._getNestedValue(eventData, eventPath)
402
+ if (val !== undefined) return val
403
+ }
404
+ // 特殊回退:如果查找 body 但找不到,尝试 text(邮件数据中用 text)
405
+ if (eventPath === 'body' || eventPath === 'text') {
406
+ const textVal = this._getNestedValue(eventData?.data, 'text')
407
+ if (textVal !== undefined) return textVal
408
+ }
409
+ return match
410
+ }
411
+ if (path === 'event') {
412
+ // ${event} 引用整个事件数据对象
413
+ return eventData ?? match
414
+ }
415
+ if (path.includes('.')) {
416
+ // ${actionId.output.field} 格式
417
+ const [actionId, ...fieldParts] = path.split('.')
418
+ if (actionId === 'result' || actionId === 'output') {
419
+ // {{result.field}} 格式
420
+ const val = this._getNestedValue(stepOutputs._lastResult, fieldParts.join('.'))
421
+ return val !== undefined ? val : match
422
+ }
423
+ const actionOutput = stepOutputs[actionId]
424
+ if (actionOutput !== undefined) {
425
+ const val = this._getNestedValue(actionOutput, fieldParts.join('.'))
426
+ return val !== undefined ? val : match
427
+ }
428
+ } else {
429
+ // ${actionId.output} 格式
430
+ const actionOutput = stepOutputs[path]
431
+ if (actionOutput !== undefined) return actionOutput
432
+ }
433
+ return match
434
+ })
435
+ return result
436
+ } else if (Array.isArray(value)) {
437
+ return value.map(v => this._resolveActionValue(v, stepOutputs, eventData))
438
+ } else if (typeof value === 'object' && value !== null) {
439
+ const resolved = {}
440
+ for (const [k, v] of Object.entries(value)) {
441
+ resolved[k] = this._resolveActionValue(v, stepOutputs, eventData)
442
+ }
443
+ return resolved
444
+ }
445
+ return value
446
+ }
447
+
448
+ _getNestedValue(obj, path) {
449
+ if (!obj) return undefined
450
+ const parts = path.split('.')
451
+ let current = obj
452
+ for (const part of parts) {
453
+ if (current === null || current === undefined) return undefined
454
+ current = current[part]
455
+ }
456
+ return current
457
+ }
458
+
459
+ /**
460
+ * 按顺序执行所有 actions
461
+ * @param {Array} actions - action 数组
462
+ * @param {Object} eventData - 事件数据
463
+ * @returns {Promise<Array>} 所有执行结果
464
+ */
465
+ async _executeAllActions(actions, eventData = null) {
466
+ const results = []
467
+ const stepOutputs = {}
468
+
469
+ const context = {
470
+ input: {},
471
+ variables: {
472
+ _event: eventData?.data || eventData || null,
473
+ _eventRaw: eventData || null,
474
+ _stepOutputs: stepOutputs
475
+ },
476
+ lastResult: null
477
+ }
478
+
479
+ for (const action of actions) {
480
+ // 执行前更新 stepOutputs
481
+ context.variables._stepOutputs = stepOutputs
482
+
483
+ const result = await this._executeAction(action, eventData, context)
484
+
485
+ // 保存结果:使用 action id 作为键
486
+ if (action.id) {
487
+ stepOutputs[action.id] = result
488
+ }
489
+ // 同时保存为 _lastResult 供 {{result}} 引用
490
+ stepOutputs._lastResult = result
491
+ context.lastResult = result
492
+
493
+ results.push({ actionId: action.id, result })
494
+
495
+ // 如果执行失败,停止执行
496
+ if (result && result.success === false) {
497
+ break
498
+ }
499
+ }
500
+
501
+ return results
502
+ }
503
+
504
+ /**
505
+ * 使用 ambient 子 agent 执行 AI 相关操作
506
+ */
507
+ async _executeWithAmbientAgent(step, context) {
508
+ if (!this._ambientAgent) {
509
+ return { success: false, error: 'Ambient agent 未初始化' }
510
+ }
511
+
512
+ if (step.type === 'message') {
513
+ let content = step.content || ''
514
+ content = this._stepExecutor._resolveValue(content, context)
515
+
516
+ if (context.variables._event) {
517
+ content = `${content}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]`
518
+ }
519
+
520
+ try {
521
+ const result = await this._ambientAgent.pushMessage(content)
522
+ return { success: true, result }
523
+ } catch (err) {
524
+ return { success: false, error: err.message }
525
+ }
526
+ }
527
+
528
+ if (step.type === 'think') {
529
+ const thinkPlugin = this._framework.pluginManager?.get('think')
530
+ if (!thinkPlugin) {
531
+ return { success: false, error: '思考插件不可用' }
532
+ }
533
+
534
+ let topic = step.topic || 'Ambient代理反思'
535
+ topic = this._stepExecutor._resolveValue(topic, context)
536
+
537
+ if (context.variables._event) {
538
+ topic = `${topic}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]`
539
+ }
540
+
541
+ try {
542
+ const result = await thinkPlugin._triggerThinking({
543
+ topic,
544
+ mode: step.mode || 'reflect',
545
+ depth: step.depth || 2
546
+ })
547
+ return result
548
+ } catch (err) {
549
+ return { success: false, error: err.message }
550
+ }
551
+ }
552
+
553
+ return { success: false, error: `未知 AI 操作类型: ${step.type}` }
554
+ }
555
+
556
+ /**
557
+ * 将 action 转换为 step 配置
558
+ */
559
+ _actionToStep(action) {
560
+ const step = {
561
+ id: action.id || `action_${Date.now()}`,
562
+ type: action.type,
563
+ name: action.name || action.id
564
+ }
565
+
566
+ switch (action.type) {
567
+ case 'tool':
568
+ step.tool = action.name
569
+ step.args = action.args || {}
570
+ break
571
+ case 'message':
572
+ step.content = action.content || ''
573
+ break
574
+ case 'think':
575
+ step.topic = action.topic || 'Ambient代理反思'
576
+ step.mode = action.mode || 'reflect'
577
+ step.depth = action.depth || 2
578
+ break
579
+ }
580
+
581
+ return step
582
+ }
583
+
584
+ // ============================================================================
585
+ // 辅助方法
586
+ // ============================================================================
587
+
588
+ _getActiveAgent() {
589
+ if (this._framework._mainAgent) {
590
+ return this._framework._mainAgent
591
+ }
592
+ const agents = this._framework._agents || []
593
+ return agents.length > 0 ? agents[agents.length - 1] : null
594
+ }
595
+
596
+ _addActivity(type, data) {
597
+ this._recentActivities.push({
598
+ type,
599
+ data,
600
+ timestamp: new Date()
601
+ })
602
+ // 只保留最近50条活动记录
603
+ if (this._recentActivities.length > 50) {
604
+ this._recentActivities = this._recentActivities.slice(-50)
605
+ }
606
+ }
607
+
608
+ /**
609
+ * 通知用户(通过事件发送给消息插件)
610
+ */
611
+ _notifyUser(title, message, level = 'info') {
612
+ if (!this._framework) return
613
+
614
+ // 获取目标相关的sessionId(如果有)
615
+ let sessionId = null
616
+ const sessionPlugin = this._framework.pluginManager.get('session')
617
+ if (sessionPlugin) {
618
+ const sessions = sessionPlugin.listSessions()
619
+ if (sessions.length > 0) {
620
+ // 取最新的会话
621
+ sessions.sort((a, b) => new Date(b.lastActive).getTime() - new Date(a.lastActive).getTime())
622
+ sessionId = sessions[0].id
623
+ }
624
+ }
625
+
626
+ // 发送统一的通知事件
627
+ this._framework.emit('notification', {
628
+ title,
629
+ message,
630
+ source: 'ambient',
631
+ level, // 'info' | 'warning' | 'success' | 'error'
632
+ sessionId,
633
+ timestamp: new Date()
634
+ })
635
+
636
+ log.info(`${title}: ${message}`)
637
+ }
638
+ }
639
+
640
+ module.exports = { ExplorerLoop }