foliko 1.0.68 → 1.0.69

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.
@@ -115,13 +115,15 @@ class GoalManager {
115
115
  }
116
116
 
117
117
  createGoal(goalData) {
118
+ const actions = goalData.actions || []
118
119
  const goal = {
119
120
  id: generateId(),
120
121
  title: goalData.title || '未命名目标',
121
122
  description: goalData.description || '',
122
123
  priority: goalData.priority || 5,
123
124
  state: GoalState.PENDING,
124
- actions: goalData.actions || [],
125
+ actions: actions,
126
+ _originalActions: actions.slice(), // 保存原始 actions,用于事件驱动目标重置
125
127
  conditions: goalData.conditions || {},
126
128
  createdAt: new Date(),
127
129
  updatedAt: new Date(),
@@ -203,9 +205,22 @@ class GoalManager {
203
205
  addEventToGoal(goalId, event) {
204
206
  const goal = this._goals.get(goalId)
205
207
  if (!goal) return
208
+
209
+ // 通用去重:如果最近 2 秒内已有相同类型的事件,跳过
210
+ const now = Date.now()
211
+ const recentSameEvent = goal.eventsReceived.find(e =>
212
+ e.event === event.type && (now - new Date(e.timestamp).getTime()) < 2000
213
+ )
214
+ if (recentSameEvent) {
215
+ console.log(`[Ambient] 跳过重复事件: ${event.type} (2秒内不重复添加)`)
216
+ return
217
+ }
218
+
219
+ // event 格式: { type: 'email:received', email: {...}, timestamp: ... }
220
+ // 需要提取 type 和 data(email 对象)
206
221
  goal.eventsReceived.push({
207
222
  event: event.type,
208
- data: event.data,
223
+ data: event.data || event.email || event,
209
224
  timestamp: new Date()
210
225
  })
211
226
  this._persist()
@@ -257,11 +272,27 @@ class EventWatcher {
257
272
 
258
273
  _handleEvent(type, data) {
259
274
  const activeGoals = this._goalManager.getActiveGoals()
275
+ console.log(`[Ambient] _handleEvent: type=${type}, activeGoals=${activeGoals.length}`)
260
276
  for (const goal of activeGoals) {
261
277
  // 检查目标条件是否匹配此事件
262
- if (this._isRelevantToGoal(goal, type, data)) {
278
+ const isRelevant = this._isRelevantToGoal(goal, type, data)
279
+ console.log(`[Ambient] goal=${goal.id}, isRelevant=${isRelevant}, conditions=${JSON.stringify(goal.conditions)}`)
280
+ if (isRelevant) {
263
281
  // 将 data 展平到事件对象中,方便通过 {{_event.xxx}} 访问
264
282
  this._goalManager.addEventToGoal(goal.id, { type, ...data })
283
+ console.log(`[Ambient] Event added to goal ${goal.id}`)
284
+ }
285
+ }
286
+
287
+ // 检查是否有 pending 目标需要激活
288
+ const pendingGoals = this._goalManager.getPendingGoals()
289
+ for (const goal of pendingGoals) {
290
+ if (this._isRelevantToGoal(goal, type, data)) {
291
+ // 激活目标
292
+ this._goalManager.activateGoal(goal.id)
293
+ // 添加事件
294
+ this._goalManager.addEventToGoal(goal.id, { type, ...data })
295
+ console.log(`[Ambient] 事件触发激活目标: ${goal.id} (事件: ${type})`)
265
296
  }
266
297
  }
267
298
  }
@@ -300,10 +331,17 @@ class Reflector {
300
331
  evaluateOutcome(goal, actionResult) {
301
332
  if (!goal) return null
302
333
 
334
+ // 检查是否是事件驱动目标
335
+ const isEventDriven = goal.conditions?.events && goal.conditions.events.length > 0
336
+
303
337
  // 检查错误
304
338
  if (actionResult && actionResult.error) {
305
339
  // 增加尝试次数但不立即失败
306
340
  goal.attempts++
341
+ // 事件驱动目标:错误不导致失败或完成
342
+ if (isEventDriven) {
343
+ return { shouldContinue: true, goalComplete: false }
344
+ }
307
345
  return {
308
346
  shouldContinue: goal.attempts < goal.maxAttempts,
309
347
  actionTaken: false
@@ -314,6 +352,11 @@ class Reflector {
314
352
  if (actionResult && actionResult.success === true) {
315
353
  goal.attempts++
316
354
 
355
+ // 事件驱动目标:永远不会完成,持续等待下一事件
356
+ if (isEventDriven) {
357
+ return { shouldContinue: true, goalComplete: false }
358
+ }
359
+
317
360
  // 检查目标是否完成
318
361
  if (goal.actions.length === 0 || goal.attempts >= goal.maxAttempts) {
319
362
  return { shouldContinue: false, goalComplete: true }
@@ -325,6 +368,11 @@ class Reflector {
325
368
  // 工具执行无错误 - 视为成功
326
369
  goal.attempts++
327
370
 
371
+ // 事件驱动目标:永远不会完成
372
+ if (isEventDriven) {
373
+ return { shouldContinue: true, goalComplete: false }
374
+ }
375
+
328
376
  // 检查目标是否完成(没有更多操作)
329
377
  if (goal.actions.length === 0 || goal.attempts >= goal.maxAttempts) {
330
378
  return { shouldContinue: false, goalComplete: true }
@@ -510,9 +558,59 @@ class ExplorerLoop {
510
558
  // 检查目标是否有未处理的事件
511
559
  const hasNewEvents = goal.eventsReceived && goal.eventsReceived.length > 0
512
560
 
561
+ // 检查是否是事件驱动目标
562
+ const isEventDriven = goal.conditions?.events && goal.conditions.events.length > 0
563
+
564
+ // 事件驱动目标:只有在新事件时才执行 actions
565
+ if (isEventDriven && !hasNewEvents) {
566
+ continue
567
+ }
568
+
513
569
  // 获取下一个操作
514
570
  if (goal.actions && goal.actions.length > 0) {
515
- const action = goal.actions[0] // 简单策略:取第一个操作
571
+ // 如果有多个 action,按顺序执行所有 action
572
+ if (goal.actions.length > 1) {
573
+ const eventData = hasNewEvents ? goal.eventsReceived[0] : null
574
+ const results = await this._executeAllActions(goal.actions, eventData)
575
+
576
+ // 最后一个结果用于评估
577
+ const lastResult = results.length > 0 ? results[results.length - 1].result : null
578
+
579
+ // 评估结果
580
+ const outcome = this._reflector.evaluateOutcome(goal, lastResult)
581
+
582
+ // 检查是否是事件驱动目标
583
+ const isEventDriven = goal.conditions?.events && goal.conditions.events.length > 0
584
+
585
+ if (outcome.goalComplete && !isEventDriven) {
586
+ // 非事件驱动目标可以完成
587
+ this._goalManager.completeGoal(goal.id)
588
+ this._addActivity('goal_completed', { goalId: goal.id, attempts: goal.attempts })
589
+ this._notifyUser('目标完成', `目标 "${goal.title}" 已完成(尝试次数:${goal.attempts})`, 'success')
590
+ } else if (!outcome.shouldContinue && !isEventDriven) {
591
+ // 非事件驱动目标可以失败
592
+ this._goalManager.failGoal(goal.id, '达到最大尝试次数')
593
+ this._addActivity('goal_failed', { goalId: goal.id, reason: '达到最大尝试次数' })
594
+ this._notifyUser('目标失败', `目标 "${goal.title}" 失败:达到最大尝试次数`, 'error')
595
+ } else {
596
+ // 事件驱动目标:不论结果如何,都重置并继续等待下一事件
597
+ this._addActivity('actions_completed', { goalId: goal.id, actionCount: results.length })
598
+ }
599
+
600
+ // 执行操作后清除已处理的事件,并重置 actions 以处理下一事件
601
+ if (hasNewEvents) {
602
+ goal.eventsReceived = []
603
+ // 重置 actions 为原始 actions,以便处理下一事件
604
+ if (goal._originalActions) {
605
+ goal.actions = goal._originalActions.slice()
606
+ }
607
+ this._goalManager.updateGoal(goal.id, { eventsReceived: [], actions: goal.actions })
608
+ }
609
+ continue
610
+ }
611
+
612
+ // 单个 action,使用原有逻辑
613
+ const action = goal.actions[0]
516
614
 
517
615
  // 循环检测(仅用于非事件驱动的操作)
518
616
  if (!hasNewEvents && this._reflector.checkLoopDetection(goal, action.id)) {
@@ -540,14 +638,21 @@ class ExplorerLoop {
540
638
  this._notifyUser('目标失败', `目标 "${goal.title}" 失败:达到最大尝试次数`, 'error')
541
639
  } else if (outcome.actionTaken) {
542
640
  // 从队列中移除已完成的操作
543
- goal.actions.shift()
641
+ // 注意:对于事件驱动目标,如果 hasNewEvents 为 true,不移除 action 而是在下面重置
642
+ if (!hasNewEvents) {
643
+ goal.actions.shift()
644
+ }
544
645
  this._addActivity('action_executed', { goalId: goal.id, action: action.id, hasEvents: hasNewEvents })
545
646
  }
546
647
 
547
- // 执行操作后清除已处理的事件
548
- if (hasNewEvents && outcome.actionTaken) {
648
+ // 执行操作后清除已处理的事件,并重置 actions 以处理下一事件
649
+ if (hasNewEvents) {
549
650
  goal.eventsReceived = []
550
- this._goalManager.updateGoal(goal.id, { eventsReceived: [] })
651
+ // 重置 actions 为原始 actions,以便处理下一事件
652
+ if (goal._originalActions) {
653
+ goal.actions = goal._originalActions.slice()
654
+ }
655
+ this._goalManager.updateGoal(goal.id, { eventsReceived: [], actions: goal.actions })
551
656
  }
552
657
  }
553
658
  }
@@ -555,33 +660,59 @@ class ExplorerLoop {
555
660
  // 激活满足条件的待处理目标
556
661
  const pendingGoals = this._goalManager.getPendingGoals()
557
662
  for (const goal of pendingGoals) {
558
- // 自动激活没有条件或条件已满足的目标
663
+ // 自动激活没有条件的目标
664
+ // 有条件的目标会在事件触发时被 EventWatcher 自动激活
559
665
  if (!goal.conditions || Object.keys(goal.conditions).length === 0) {
560
666
  this._goalManager.activateGoal(goal.id)
561
667
  this._addActivity('goal_activated', { goalId: goal.id })
668
+ } else {
669
+ // 有条件的目标,立即激活以便开始监听事件
670
+ this._goalManager.activateGoal(goal.id)
671
+ this._addActivity('goal_activated', { goalId: goal.id, reason: '事件监听目标已激活' })
562
672
  }
563
673
  }
564
674
  }
565
675
 
566
- async _executeAction(action, eventData = null) {
676
+ /**
677
+ * 执行单个 action
678
+ * @param {Object} action - action 配置
679
+ * @param {Object} eventData - 事件数据
680
+ * @param {Object} context - 执行上下文(包含 stepOutputs 等)
681
+ * @returns {Promise<Object>} 执行结果
682
+ */
683
+ async _executeAction(action, eventData = null, context = null) {
567
684
  if (!this._framework) {
568
685
  return { success: false, error: '框架不可用' }
569
686
  }
570
687
 
571
688
  try {
572
- // 创建执行上下文
573
- const context = {
574
- input: {},
575
- variables: {
576
- _event: eventData,
577
- _action: action
578
- },
579
- lastResult: null
689
+ // 如果没有提供 context,创建一个新的
690
+ if (!context) {
691
+ context = {
692
+ input: {},
693
+ variables: {
694
+ _event: eventData?.data || eventData || null,
695
+ _eventRaw: eventData || null,
696
+ _action: action
697
+ },
698
+ lastResult: null
699
+ }
700
+ } else {
701
+ // 更新 context 中的 _action
702
+ context.variables._action = action
580
703
  }
581
704
 
582
705
  // 将 action 转换为 step 配置
583
706
  const step = this._actionToStep(action)
584
707
 
708
+ // 解析 step.args 中的变量引用(支持 ${actionId.output} 格式)
709
+ if (step.args) {
710
+ step.args = this._resolveActionArgs(step.args, context)
711
+ }
712
+ if (step.input) {
713
+ step.input = this._resolveActionArgs(step.input, context)
714
+ }
715
+
585
716
  // message 和 think 类型使用专用子 agent 执行
586
717
  if (step.type === 'message' || step.type === 'think') {
587
718
  return await this._executeWithAmbientAgent(step, context)
@@ -595,6 +726,154 @@ class ExplorerLoop {
595
726
  }
596
727
  }
597
728
 
729
+ /**
730
+ * 解析 action 参数中的变量引用
731
+ * 支持 ${actionId.output}、${actionId.output.field} 和 ${event.xxx} 格式
732
+ * @param {Object} args - 参数对象
733
+ * @param {Object} context - 执行上下文
734
+ * @returns {Object} 解析后的参数
735
+ */
736
+ _resolveActionArgs(args, context) {
737
+ if (!args || typeof args !== 'object') return args
738
+
739
+ const stepOutputs = context.variables._stepOutputs || {}
740
+ const eventData = context.variables._event || {}
741
+
742
+ const resolved = {}
743
+ for (const [key, value] of Object.entries(args)) {
744
+ resolved[key] = this._resolveActionValue(value, stepOutputs, eventData)
745
+ }
746
+ return resolved
747
+ }
748
+
749
+ /**
750
+ * 解析单个值中的 ${actionId.output} 引用和 ${event.xxx} 引用
751
+ */
752
+ _resolveActionValue(value, stepOutputs, eventData) {
753
+ if (typeof value === 'string') {
754
+ // 处理 ${actionId.output} 和 ${actionId.output.field} 格式
755
+ const result = value.replace(/\$\{([^}]+)\}/g, (match, path) => {
756
+ if (path === 'output' || path === 'result') {
757
+ // {{result}} 引用上一步结果
758
+ return stepOutputs._lastResult ?? match
759
+ }
760
+ if (path.startsWith('event.')) {
761
+ // ${event.xxx} 引用事件数据
762
+ let eventPath = path.replace(/^event\./, '')
763
+ // 优先在 eventData.data 中查找(因为事件数据通常存储在 data 字段)
764
+ if (eventData?.data) {
765
+ let val = this._getNestedValue(eventData.data, eventPath)
766
+ if (val !== undefined) return val
767
+ }
768
+ // 再尝试直接在 eventData 中查找
769
+ let val = this._getNestedValue(eventData, eventPath)
770
+ if (val !== undefined) return val
771
+ // 兼容:如果 path 是 event.data.xxx 但 eventData 没有 data,尝试跳过一层
772
+ if (eventPath.startsWith('data.')) {
773
+ eventPath = eventPath.replace(/^data\./, '')
774
+ val = this._getNestedValue(eventData, eventPath)
775
+ if (val !== undefined) return val
776
+ }
777
+ // 特殊回退:如果查找 body 但找不到,尝试 text(邮件数据中用 text)
778
+ if (eventPath === 'body' || eventPath === 'text') {
779
+ const textVal = this._getNestedValue(eventData?.data, 'text')
780
+ if (textVal !== undefined) return textVal
781
+ }
782
+ return match
783
+ }
784
+ if (path === 'event') {
785
+ // ${event} 引用整个事件数据对象
786
+ return eventData ?? match
787
+ }
788
+ if (path.includes('.')) {
789
+ // ${actionId.output.field} 格式
790
+ const [actionId, ...fieldParts] = path.split('.')
791
+ if (actionId === 'result' || actionId === 'output') {
792
+ // {{result.field}} 格式
793
+ const val = this._getNestedValue(stepOutputs._lastResult, fieldParts.join('.'))
794
+ return val !== undefined ? val : match
795
+ }
796
+ const actionOutput = stepOutputs[actionId]
797
+ if (actionOutput !== undefined) {
798
+ const val = this._getNestedValue(actionOutput, fieldParts.join('.'))
799
+ return val !== undefined ? val : match
800
+ }
801
+ } else {
802
+ // ${actionId.output} 格式
803
+ const actionOutput = stepOutputs[path]
804
+ if (actionOutput !== undefined) return actionOutput
805
+ }
806
+ return match
807
+ })
808
+ return result
809
+ } else if (Array.isArray(value)) {
810
+ return value.map(v => this._resolveActionValue(v, stepOutputs, eventData))
811
+ } else if (typeof value === 'object' && value !== null) {
812
+ const resolved = {}
813
+ for (const [k, v] of Object.entries(value)) {
814
+ resolved[k] = this._resolveActionValue(v, stepOutputs, eventData)
815
+ }
816
+ return resolved
817
+ }
818
+ return value
819
+ }
820
+
821
+ _getNestedValue(obj, path) {
822
+ if (!obj) return undefined
823
+ const parts = path.split('.')
824
+ let current = obj
825
+ for (const part of parts) {
826
+ if (current === null || current === undefined) return undefined
827
+ current = current[part]
828
+ }
829
+ return current
830
+ }
831
+
832
+ /**
833
+ * 按顺序执行所有 actions
834
+ * @param {Array} actions - action 数组
835
+ * @param {Object} eventData - 事件数据
836
+ * @returns {Promise<Array>} 所有执行结果
837
+ */
838
+ async _executeAllActions(actions, eventData = null) {
839
+ const results = []
840
+ const stepOutputs = {}
841
+
842
+ const context = {
843
+ input: {},
844
+ variables: {
845
+ _event: eventData?.data || eventData || null,
846
+ _eventRaw: eventData || null,
847
+ _stepOutputs: stepOutputs
848
+ },
849
+ lastResult: null
850
+ }
851
+
852
+ for (const action of actions) {
853
+ // 执行前更新 stepOutputs
854
+ context.variables._stepOutputs = stepOutputs
855
+
856
+ const result = await this._executeAction(action, eventData, context)
857
+
858
+ // 保存结果:使用 action id 作为键
859
+ if (action.id) {
860
+ stepOutputs[action.id] = result
861
+ }
862
+ // 同时保存为 _lastResult 供 {{result}} 引用
863
+ stepOutputs._lastResult = result
864
+ context.lastResult = result
865
+
866
+ results.push({ actionId: action.id, result })
867
+
868
+ // 如果执行失败,停止执行
869
+ if (result && result.success === false) {
870
+ break
871
+ }
872
+ }
873
+
874
+ return results
875
+ }
876
+
598
877
  /**
599
878
  * 使用 ambient 子 agent 执行 AI 相关操作
600
879
  */
@@ -735,7 +1014,20 @@ class AmbientAgentPlugin extends Plugin {
735
1014
  super()
736
1015
  this.name = 'ambient'
737
1016
  this.version = '1.0.0'
738
- this.description = 'Ambient Agent - 持续后台运行的智能代理,用于主动监控和执行操作'
1017
+ this.description = `Ambient Agent - 持续后台运行的智能代理,用于主动监控事件并执行操作
1018
+
1019
+ ## 支持的事件类型(创建目标时 conditions.events 必须使用这些确切的名称):
1020
+ - email:received - 收到新邮件(参数:from, subject, text, messageId)
1021
+ - tool:result - 工具执行完成(参数:name, args, result)
1022
+ - scheduler:reminder - 定时提醒(参数:taskId, message, time)
1023
+ - scheduler:task_completed - 任务完成(参数:taskId, result)
1024
+ - scheduler:task_failed - 任务失败(参数:taskId, error)
1025
+ - agent:message - 代理消息(参数:content, sessionId)
1026
+ - think:thought_completed - 思考完成(参数:mode, topic, thought)
1027
+ - webhook:received - Webhook接收(参数:headers, body, query, path)
1028
+
1029
+ ## 在 action 参数中引用事件数据:
1030
+ 使用 \${event.xxx} 语法,例如:\${event.from}、\${event.subject}、\${event.text}`
739
1031
  this.priority = 18
740
1032
  this.system = true
741
1033
 
@@ -785,10 +1077,29 @@ class AmbientAgentPlugin extends Plugin {
785
1077
  this._eventWatcher = new EventWatcher(this._goalManager, this._framework)
786
1078
  this._eventWatcher.start()
787
1079
 
788
- // 动态更新description显示支持的事件
789
- const events = Array.from(this._eventWatcher._relevantEvents).join(', ')
1080
+ // 动态更新description显示支持的事件及参数
1081
+ const eventDescriptions = {
1082
+ 'tool:result': '工具执行结果事件 - 参数: name(工具名), args(执行参数), result(执行结果), error(错误信息)',
1083
+ 'scheduler:reminder': '定时提醒事件 - 参数: taskId(任务ID), message(提醒消息), time(触发时间)',
1084
+ 'scheduler:task_completed': '任务完成事件 - 参数: taskId(任务ID), result(任务结果)',
1085
+ 'scheduler:task_failed': '任务失败事件 - 参数: taskId(任务ID), error(错误信息)',
1086
+ 'agent:message': '代理消息事件 - 参数: content(消息内容), sessionId(会话ID), agentId(代理ID)',
1087
+ 'think:thought_completed': '思考完成事件 - 参数: mode(思考模式), topic(思考主题), thought(思考内容), depth(思考深度)',
1088
+ 'email:received': '收到邮件事件 - 参数: from(发件人), to(收件人), subject(主题), text(正文), body(HTML正文), messageId(消息ID), timestamp(时间戳)',
1089
+ 'webhook:received': 'Webhook接收事件 - 参数: headers(HTTP头), body(请求体), query(查询参数), path(请求路径)'
1090
+ }
1091
+
1092
+ const events = Array.from(this._eventWatcher._relevantEvents)
1093
+ const eventList = events.map(e => {
1094
+ const desc = eventDescriptions[e] || e
1095
+ return ` - ${desc}`
1096
+ }).join('\n')
1097
+
790
1098
  this.description = `Ambient Agent - 持续后台运行的智能代理,用于主动监控和执行操作
791
- 支持的事件:${events}`
1099
+ 支持的事件及参数:
1100
+ ${eventList}
1101
+
1102
+ 使用 \${event.xxx} 在 action 参数中引用事件数据`
792
1103
 
793
1104
  // 启动探索循环
794
1105
  this._explorerLoop = new ExplorerLoop(this._goalManager, this._reflector, this._framework, {