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

+2 -1
View File
@@ -109,13 +109,14 @@ export const api = {
// ---- tasks ----
tasks: () => get<{ tasks: Task[]; active: string }>("/tasks").then((r) => ({ tasks: arr(r.tasks), active: r.active ?? "" })),
createTask: (description: string, goal: string, llmProfileId?: number, timeoutSeconds?: number, seedFirstIntent?: boolean) =>
createTask: (description: string, goal: string, llmProfileId?: number, timeoutSeconds?: number, seedFirstIntent?: boolean, planHeartbeatSeconds?: number) =>
post<Task>("/tasks", {
description,
goal,
llm_profile_id: llmProfileId ?? null,
timeout_seconds: timeoutSeconds ?? 0,
seed_first_intent: seedFirstIntent ?? true,
plan_heartbeat_seconds: planHeartbeatSeconds ?? 0, // 0 = 后端归一到默认 300(5min)
}),
deleteTask: (id: string) => del<{ deleted: number }>(`/tasks/${id}`),
controlTask: (id: string, action: "pause" | "resume") => post<{ id: string; paused: boolean }>(`/tasks/${id}/control`, { action }),