feat(planner): finding 当场唤醒 + planner N 秒心跳触发(任务参数,默认5min)

触发机制补两条(见 docs/planner-trigger-impl-plan.md):

1) finding 当场唤醒:worker 飞行中 report_finding 落库即唤醒本任务 planner,
   不再等 worker 整趟收工才反应。
   - worker.Execute 加 notify 入参并 SetNotify(补上 worker toolset 之前缺失的接线)
   - addFinding 写 KindFinding 成功后调 notify(只对 finding,不对 fact)
   - engine 两处 worker.Execute 调用传 t.Notify

2) planner N 秒心跳:距上轮规划结束/任务开始满 N 秒且期间无触发 → 触发一轮,
   兜底卡死 + 唤醒去监督飞行中的 worker(steer/kill) + 周期复查。
   - plannerLoop 重构为 runRound 闭包 + 三臂 select(ctx/notify/heartbeat)
   - 心跳在 loop 入口臂 = 从任务 start 计时(覆盖 seed 跳过首轮),每轮结束重臂
   - 新增任务参数 plan_heartbeat_seconds,下限=默认=300(5min),低于自动抬到300
   - DB 迁移:schema.sql 加列 + ALTER ADD COLUMN IF NOT EXISTS 补旧库
   - 全链路接线:db.CreateTask/manager/server HTTP/orchestration spawn_task
   - 前端:api.createTask 传参 + 建任务表单新增"planner 心跳(分钟)"字段
This commit is contained in:
Autumn-27 committed 2026-08-12 11:18:07 -04:00
1 parent 06c1ac748f
commit 47ba58bda6
11 files changed
+168 -82

No files matched your search

+3
View File
@@ -647,6 +647,9 @@ func (t *ToolSet) addFinding() actool.CoreTool {
_ = t.ts.Link(intent, db.RelYields, id) // chain: intent -> finding
}
_, _ = t.ts.AddStandaloneFinding(t.taskID, id, a.VulnClass, a.Severity, a.Summary, a.Evidence, t.worker, anchors)
if t.notify != nil {
t.notify() // 确证漏洞落库 → 当场唤醒本任务 planner(不等 worker 收工,debounce 合并)
}
} else {
// conversation context: no exploration store available, cannot record finding
return actool.Errorf("report_finding 需要任务上下文(exploration store 未初始化)"), nil