feat(schedules): per-check cost and model summary on GET /schedules (INT-3)

agent_check items carry agentRuns (last 100 sessions: last/avg cost, 7-day count and cost,
models, recent 3); plan page and home plan band use it instead of fetching agent runs.
Migration 0052 indexes agent_run(created_by, created_at).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-29 17:55:47 +08:00
1 parent 82df5662f0
commit e681fcedf6
9 files changed
+200 -78

No files matched your search

@@ -297,6 +297,27 @@ describe.skipIf(!hasPg)("定时任务注册表(真实 PG)", () => {
expect(d).toMatchObject({ kind: "other", severity: "info", summary: "一切正常" });
expect(await sched.harvest()).toBe(0);
// INT-3:看板给巡检带最近会话的费用与模型汇总
await h.db
.updateTable("agent_run")
.set({ provider: "anthropic", model: "claude-x", usage: JSON.stringify({ costUsd: 0.25 }) })
.where("id", "=", ar.id)
.execute();
const listed = (await api("GET", "/schedules")).body.data.items.find((j: { id: string }) => j.id === id);
expect(listed.agentRuns).toMatchObject({
count: 1,
lastCostUsd: 0.25,
avgCostUsd: 0.25,
weekCount: 1,
weekCostUsd: 0.25,
lastProvider: "anthropic",
lastModel: "claude-x",
models: [{ provider: "anthropic", model: "claude-x", count: 1 }],
recent: [{ id: ar.id, status: "succeeded", summary: "一切正常", costUsd: 0.25 }],
});
const plain = (await api("GET", "/schedules")).body.data.items.find((j: { id: string }) => j.id === "test.keep");
expect(plain.agentRuns).toBeUndefined();
// 自定义可改名、可删;内置不可删
expect((await api("PATCH", `/schedules/${id}`, { name: "夜间巡检" })).body.data.name).toBe("夜间巡检");
expect((await api("DELETE", "/schedules/test.keep")).status).toBe(409);