feat(agent): 用对话起草巡检(schedule.draft_check + 起草巡检 Skill + 计划页入口与对话卡片)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-03 00:04:31 +08:00
1 parent 8eb8a68295
commit 7c4ad901d9
14 files changed
+408 -7

No files matched your search

@@ -8,6 +8,7 @@ import {
import {
Ban,
Brain,
CalendarCog,
Check,
ChevronDown,
ChevronRight,
@@ -38,6 +39,7 @@ import { CHEVRON_CLASS, Disclosure } from "~/components/agentui/disclosure.tsx";
import { Markdown } from "~/components/agentui/markdown.tsx";
import { MessageMarker, MessageTyping } from "~/components/agentui/message.tsx";
import { StreamingResponse } from "~/components/agentui/streaming-response.tsx";
import { consolePath } from "~/components/console/nav.ts";
import { StatusBadge } from "~/components/console/status-badge.tsx";
import { TechInfo } from "~/components/console/tech-info.tsx";
import { formatUsd } from "~/components/schedules/labels.ts";
@@ -542,6 +544,11 @@ export function AgentConversation({
until={p.body[0]?.at ?? null}
/>
) : null}
{p.activity
.flatMap((a) => (a.kind === "tool" ? draftCheckOf(a.call) : []))
.map((d) => (
<DraftCheckCard key={d.jobId} {...d} />
))}
{p.body.map((t) => {
const streaming = streamingText && t.key === lastKey;
// 旧会话 / 不守汇报格式的模型:UUID、能力 ID、原因码换成名称和人话,原文进技术信息
@@ -601,6 +608,33 @@ export function AgentConversation({
);
}
/** 起草巡检(schedule.draft_check)成功的输出:巡检 ID 与名称(输出摘要可能被截断,按字段取) */
export function draftCheckOf(call: ToolCallView): { jobId: string; name: string }[] {
if (call.capabilityId !== "schedule.draft_check" || call.status === "running" || call.status === "failed") return [];
if (isDenied(call) || !call.outputSummary) return [];
const jobId = /"jobId"\s*:\s*"([\w.-]+)"/.exec(call.outputSummary)?.[1];
const name = /"name"\s*:\s*"((?:[^"\\]|\\.)*)"/.exec(call.outputSummary)?.[1];
return jobId ? [{ jobId, name: name ?? "巡检" }] : [];
}
/** 对话里起草好的巡检:未启用,去计划页检查后启用 */
function DraftCheckCard({ jobId, name }: { jobId: string; name: string }) {
return (
<div className="flex w-full max-w-[72ch] flex-wrap items-center justify-between gap-3 rounded-lg border border-border/70 px-3 py-2.5">
<div className="min-w-0">
<p className="flex items-center gap-2 text-caption font-medium text-foreground">
<CalendarCog aria-hidden className="size-4 shrink-0 text-muted-foreground" />
巡检草稿「{name}」<StatusBadge>未启用</StatusBadge>
</p>
<p className="mt-0.5 text-meta text-muted-foreground">到计划页检查目标、间隔和能力,确认后打开开关启用。</p>
</div>
<Button asChild size="sm" variant="outline">
<Link to={`${consolePath("schedules")}?job=${encodeURIComponent(jobId)}`}>检查并启用</Link>
</Button>
</div>
);
}
/** 回合开始的时间:前一条用户消息;第一轮是会话开始 */
function turnSince(blocks: ConvBlock[], index: number, run: AgentRunView): string | null {
const prev = blocks[index - 1];