import { CapabilityError, type CapabilityRegistry } from "@umt/agent"; import { CAPABILITY_CATALOG, capabilitySpec, KnownCapabilityId } from "@umt/contracts"; import type { ServerContext } from "../types.ts"; import { createAgentCheck } from "./store.ts"; // schedule.draft_check:智能体按用户的自然语言需求起草巡检。建成「未启用」,不会自己运行; // 用户在计划页检查、修改后再启用(授权只放行用户发起的对话,见 agent/authorize.ts)。 /** 巡检只开放只读 / 预览 / 提案类能力(与计划页表单一致),且不能再起草巡检 */ const ALLOWED = new Set( Object.values(CAPABILITY_CATALOG) .filter((c) => ["read", "preview", "propose"].includes(c.kind) && c.id !== "schedule.draft_check") .map((c) => c.id as string), ); export function registerScheduleCapabilities(registry: CapabilityRegistry, ctx: ServerContext): void { registry.register(capabilitySpec("schedule.draft_check"), async (input) => { const asked = [...new Set(input.capabilities ?? [])]; const caps = asked.filter((c) => KnownCapabilityId.safeParse(c).success && ALLOWED.has(c)); const dropped = asked.filter((c) => !caps.includes(c)); let row: Awaited>; try { row = await createAgentCheck( ctx.db, { name: input.name, description: "智能体按对话起草", enabled: false, schedule: { type: "interval", everyMs: Math.round(input.everyHours * 3600_000) }, config: { goalTemplate: input.goal, capabilityAllowlist: caps as KnownCapabilityId[], budget: { ...(input.maxToolCalls ? { maxToolCalls: input.maxToolCalls } : {}), ...(input.maxDurationSec ? { maxDurationSec: input.maxDurationSec } : {}), }, triggerCondition: input.triggerCondition ?? "always", reportAsDecision: input.reportAsDecision ?? false, }, }, new Date(), ); } catch (e) { throw new CapabilityError("precondition", `巡检没建成:${(e as Error).message}`); } return { jobId: row.id, name: row.name, everyHours: input.everyHours, capabilities: caps, dropped, enabled: false as const, // copy-allow: 给模型看的下一步提示 next: "已建成未启用的巡检。请用户到「计划」页检查目标、间隔和能力,确认后打开开关启用。", }; }); }