fix(scheduler): 事件水位每 tick 都推进,避免新建触发器回放历史

原来 fire{Findings,Goals,TaskTimeouts,TaskCreates,ToolCalls} 在没有
对应触发器时直接 return,不推进水位。于是水位会一直停在旧值;等用户
新建/启用触发器(如预置的 reporter on_tool_call),下一个 tick 就把
水位之后的全部历史事件当成新事件触发一遍(逐条唤起 reporter 写报告)。

改为:即便当前没有对应触发器,也照常把水位推进到最新(静默消费),
触发动作只在存在触发器时执行。这样触发器只对启用之后出现的事件生效,
不再回放历史。goal 用 fired-set 同理:无触发器时也标记为已消费。
This commit is contained in:
Autumn-27 committed 2026-08-19 09:17:04 -04:00
1 parent e67ff26e90
commit 5244500fee
1 file changed
+23 -15
+23 -15
View File
@@ -155,19 +155,22 @@ func (sc *Scheduler) fireFindings(triggers []*db.AgentTrigger) {
want = append(want, tr)
}
}
if len(want) == 0 {
return
}
last, _ := strconv.ParseInt(sc.mustState(schedKeyLastFinding), 10, 64)
events, err := sc.pg.NewFindingsSince(last)
if err != nil || len(events) == 0 {
return
}
// Advance the watermark whether or not any on_finding trigger is active: a
// finding fires only for triggers live at the moment it appears. Otherwise a
// trigger enabled later would replay the entire historical backlog at once.
maxID := last
for _, e := range events {
if e.NodeID > maxID {
maxID = e.NodeID
}
if len(want) == 0 {
continue
}
msgCtx := fmt.Sprintf("\n\n【本次由任务发现 finding 触发】\n任务: #%d %s(目标:%s)\n发现: [%s/%s] %s",
e.TaskID, e.TaskDesc, e.TaskGoal, e.VulnClass, e.Severity, e.Summary)
for _, tr := range want {
@@ -185,13 +188,12 @@ func (sc *Scheduler) fireGoals(triggers []*db.AgentTrigger) {
want = append(want, tr)
}
}
if len(want) == 0 {
return
}
events, err := sc.pg.MetGoals()
if err != nil || len(events) == 0 {
return
}
// Mark goals as consumed whether or not a trigger is active, so enabling an
// on_goal_met trigger later doesn't replay every already-met goal.
fired := sc.firedGoalSet()
changed := false
for _, e := range events {
@@ -200,6 +202,9 @@ func (sc *Scheduler) fireGoals(triggers []*db.AgentTrigger) {
}
fired[e.NodeID] = true
changed = true
if len(want) == 0 {
continue
}
msgCtx := fmt.Sprintf("\n\n【本次由任务完成目标触发】\n任务: #%d %s(目标:%s)\n达成目标: %s",
e.TaskID, e.TaskDesc, e.TaskGoal, e.Summary)
for _, tr := range want {
@@ -220,19 +225,20 @@ func (sc *Scheduler) fireTaskTimeouts(triggers []*db.AgentTrigger) {
want = append(want, tr)
}
}
if len(want) == 0 {
return
}
last, _ := strconv.ParseInt(sc.mustState(schedKeyLastTimeout), 10, 64)
events, err := sc.pg.TimedOutTasksSince(last)
if err != nil || len(events) == 0 {
return
}
// Advance the watermark even with no active trigger — see fireFindings.
maxID := last
for _, e := range events {
if e.NodeID > maxID {
maxID = e.NodeID
}
if len(want) == 0 {
continue
}
msgCtx := fmt.Sprintf("\n\n【本次由任务超时触发】\n任务: #%d %s(目标:%s)",
e.TaskID, e.TaskDesc, e.TaskGoal)
for _, tr := range want {
@@ -251,19 +257,20 @@ func (sc *Scheduler) fireTaskCreates(triggers []*db.AgentTrigger) {
want = append(want, tr)
}
}
if len(want) == 0 {
return
}
last, _ := strconv.ParseInt(sc.mustState(schedKeyLastTaskCreate), 10, 64)
events, err := sc.pg.NewTasksSince(last)
if err != nil || len(events) == 0 {
return
}
// Advance the watermark even with no active trigger — see fireFindings.
maxID := last
for _, e := range events {
if e.NodeID > maxID {
maxID = e.NodeID
}
if len(want) == 0 {
continue
}
msgCtx := fmt.Sprintf("\n\n【本次由任务创建触发】\n任务ID: #%d\n任务描述: %s\n任务目标: %s",
e.TaskID, e.TaskDesc, e.TaskGoal)
for _, tr := range want {
@@ -283,19 +290,20 @@ func (sc *Scheduler) fireToolCalls(triggers []*db.AgentTrigger) {
want = append(want, tr)
}
}
if len(want) == 0 {
return
}
last, _ := strconv.ParseInt(sc.mustState(schedKeyLastToolCall), 10, 64)
events, err := sc.pg.NewToolCallsSince(last)
if err != nil || len(events) == 0 {
return
}
// Advance the watermark even with no active trigger — see fireFindings.
maxID := last
for _, e := range events {
if e.NodeID > maxID {
maxID = e.NodeID
}
if len(want) == 0 {
continue
}
errTag := ""
if e.ToolIsErr {
errTag = "[error] "