feat(agent): 用对话起草巡检(schedule.draft_check + 起草巡检 Skill + 计划页入口与对话卡片)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
8eb8a68295
commit
7c4ad901d9
14 files changed
+408
-7
No files matched your search
@@ -0,0 +1,76 @@
|
||||
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||
import { createCapabilityRegistry } from "../../src/modules/agent/capability-registry.ts";
|
||||
import { registerScheduleCapabilities } from "../../src/modules/schedules/capabilities.ts";
|
||||
import { createHarness, type Harness, pgAvailable } from "../sources-identity/harness.ts";
|
||||
|
||||
// schedule.draft_check:智能体按对话起草巡检 → 建成未启用的 agent_check;只读能力白名单;只放行用户发起的对话
|
||||
const pg = await pgAvailable();
|
||||
// biome-ignore lint/suspicious/noExplicitAny: 能力输出按 schema 校验过,测试里直接取字段
|
||||
type Any = any;
|
||||
|
||||
describe.skipIf(!pg)("起草巡检能力(真实 PG)", () => {
|
||||
let h: Harness;
|
||||
beforeAll(async () => {
|
||||
h = await createHarness("draftcheck");
|
||||
const registry = createCapabilityRegistry({ logger: h.ctx.logger, db: h.db, settings: h.ctx.settings });
|
||||
registerScheduleCapabilities(registry, h.ctx);
|
||||
(h.ctx as { capabilities: unknown }).capabilities = registry;
|
||||
}, 120_000);
|
||||
afterAll(async () => {
|
||||
await h?.close();
|
||||
});
|
||||
|
||||
const run = async (trigger: "user" | "schedule") =>
|
||||
(
|
||||
await h.db
|
||||
.insertInto("agent_run")
|
||||
.values({ trigger, goal: "起草巡检", status: "running", mode: "execute" } as never)
|
||||
.returning("id")
|
||||
.executeTakeFirstOrThrow()
|
||||
).id;
|
||||
const invoke = (input: Record<string, unknown>, agentRunId: string) =>
|
||||
h.ctx.capabilities.invoke("schedule.draft_check", input, {
|
||||
actorId: "agent",
|
||||
taskId: crypto.randomUUID(),
|
||||
agentRunId,
|
||||
scopeIds: [],
|
||||
signal: new AbortController().signal,
|
||||
source: "agent",
|
||||
}) as Promise<Any>;
|
||||
const input = {
|
||||
name: "失败下载日报",
|
||||
goal: "检查下载器里失败或卡住的任务,有就写一段话汇报",
|
||||
everyHours: 24,
|
||||
capabilities: ["download.list", "downloader.list", "download.submit", "not.exists"],
|
||||
triggerCondition: "stalled_downloads",
|
||||
reportAsDecision: true,
|
||||
};
|
||||
|
||||
it("对话里起草:建成未启用的巡检;写入 / 不存在的能力去掉", async () => {
|
||||
const out = await invoke(input, await run("user"));
|
||||
expect(out).toMatchObject({
|
||||
enabled: false,
|
||||
capabilities: ["download.list", "downloader.list"],
|
||||
dropped: ["download.submit", "not.exists"],
|
||||
});
|
||||
const job = await h.db
|
||||
.selectFrom("scheduled_job")
|
||||
.selectAll()
|
||||
.where("id", "=", out.jobId)
|
||||
.executeTakeFirstOrThrow();
|
||||
expect(job).toMatchObject({ kind: "agent_check", enabled: false, builtin: false, name: "失败下载日报" });
|
||||
expect(job.schedule).toMatchObject({ type: "interval", everyMs: 24 * 3600_000 });
|
||||
expect(job.config).toMatchObject({
|
||||
goalTemplate: input.goal,
|
||||
capabilityAllowlist: ["download.list", "downloader.list"],
|
||||
triggerCondition: "stalled_downloads",
|
||||
reportAsDecision: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("定时巡检的会话里不能再起草巡检", async () => {
|
||||
const before = await h.db.selectFrom("scheduled_job").select("id").execute();
|
||||
await expect(invoke(input, await run("schedule"))).rejects.toThrow(/用户发起的对话/);
|
||||
expect(await h.db.selectFrom("scheduled_job").select("id").execute()).toHaveLength(before.length);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user