fix: preserve investigation execution history by round
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState, type ReactNode } from "react";
|
||||
import { MessageSquare } from "lucide-react";
|
||||
import { History, LoaderCircle, MessageSquare } from "lucide-react";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import { Disclosure } from "@/shared/ui/disclosure";
|
||||
import { StatusBadge } from "@/shared/ui/status";
|
||||
@@ -11,7 +11,8 @@ import type {
|
||||
OperationalTaskAction,
|
||||
OperationalTaskEvidence,
|
||||
OperationalTaskReport,
|
||||
OperationalTaskReportPage
|
||||
OperationalTaskReportPage,
|
||||
TaskExecutionHistoryTarget
|
||||
} from "../api/operational-task-client";
|
||||
import {
|
||||
investigationDiagram,
|
||||
@@ -24,11 +25,13 @@ import { useReportUpdateMotion } from "../hooks/use-report-update-motion";
|
||||
import { ExpandableReading } from "@/shared/ui/reading-workspace";
|
||||
import { InvestigationReportBlocks } from "./investigation-report-blocks";
|
||||
import { InvestigationReportNavigation, type InvestigationReportSection } from "./investigation-report-navigation";
|
||||
import { showMapNotice } from "@/features/map/core";
|
||||
|
||||
type ReportSelection = { snapshot: OperationalTaskReport; diagram: InvestigationDiagram | null };
|
||||
type Props = {
|
||||
task: OperationalTask;
|
||||
onContinue: (discussion?: InvestigationDiscussion) => Promise<boolean | void> | boolean | void;
|
||||
onViewExecutionHistory?: (target: TaskExecutionHistoryTarget, knownRunId?: string) => Promise<void> | void;
|
||||
renderAction: (action: OperationalTaskAction) => ReactNode;
|
||||
renderEvidence: (evidence: OperationalTaskEvidence) => ReactNode;
|
||||
onLoadReports?: (offset: number) => Promise<OperationalTaskReportPage>;
|
||||
@@ -126,6 +129,7 @@ function investigationTimeline(
|
||||
export function InvestigationTaskReport({
|
||||
task,
|
||||
onContinue,
|
||||
onViewExecutionHistory,
|
||||
renderAction,
|
||||
renderEvidence,
|
||||
onLoadReports,
|
||||
@@ -139,8 +143,24 @@ export function InvestigationTaskReport({
|
||||
const [historyOffset, setHistoryOffset] = useState<number | null>(0);
|
||||
const [historyLoading, setHistoryLoading] = useState(false);
|
||||
const [historyError, setHistoryError] = useState<string | null>(null);
|
||||
const [openingExecutionHistory, setOpeningExecutionHistory] = useState(false);
|
||||
const live = selection === null;
|
||||
const snapshot = selection?.snapshot ?? task.latest_report;
|
||||
const displayedGeneration = live ? task.execution_generation : snapshot?.execution_generation;
|
||||
const fallbackExecutionRun = task.latest_run ?? task.active_run;
|
||||
const executionHistoryTarget: TaskExecutionHistoryTarget | null = snapshot ? {
|
||||
executionGeneration: snapshot.execution_generation ?? null,
|
||||
sessionId: snapshot.source_session_id
|
||||
} : live && fallbackExecutionRun?.agent_session_id ? {
|
||||
executionGeneration: fallbackExecutionRun.generation,
|
||||
sessionId: fallbackExecutionRun.agent_session_id
|
||||
} : null;
|
||||
const knownExecutionRun = executionHistoryTarget
|
||||
? [task.active_run, task.latest_run].find((run) => run
|
||||
&& (executionHistoryTarget.executionGeneration == null
|
||||
|| run.generation === executionHistoryTarget.executionGeneration)
|
||||
&& run.agent_session_id === executionHistoryTarget.sessionId) ?? null
|
||||
: null;
|
||||
const report = snapshot?.report;
|
||||
const progress = task.latest_progress?.execution_generation === task.execution_generation ? task.latest_progress : null;
|
||||
// A current final report owns its archived graph. During investigation, a
|
||||
@@ -221,6 +241,25 @@ export function InvestigationTaskReport({
|
||||
setHistoryLoading(false);
|
||||
}
|
||||
};
|
||||
const openExecutionHistory = async () => {
|
||||
if (!onViewExecutionHistory || !executionHistoryTarget || openingExecutionHistory) return;
|
||||
setOpeningExecutionHistory(true);
|
||||
try {
|
||||
if (knownExecutionRun) {
|
||||
await onViewExecutionHistory(executionHistoryTarget, knownExecutionRun.run_id);
|
||||
} else {
|
||||
await onViewExecutionHistory(executionHistoryTarget);
|
||||
}
|
||||
} catch (error) {
|
||||
showMapNotice({
|
||||
tone: "error",
|
||||
title: "调查执行记录打开失败",
|
||||
message: error instanceof Error ? error.message : "请稍后重试。"
|
||||
});
|
||||
} finally {
|
||||
setOpeningExecutionHistory(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ExpandableReading title={snapshot?.report.title ?? task.title}
|
||||
@@ -229,7 +268,7 @@ export function InvestigationTaskReport({
|
||||
<header className="investigation-report-header">
|
||||
<div className="min-w-0">
|
||||
<p className="text-xs text-[var(--ink-muted)]">
|
||||
调查任务 · 第 {task.execution_generation} 轮
|
||||
{displayedGeneration == null ? "调查任务 · 历史轮次" : `调查任务 · 第 ${displayedGeneration} 轮`}
|
||||
</p>
|
||||
<div className="mt-1 flex flex-wrap items-center gap-2">
|
||||
<h2 className="text-base font-semibold leading-6 text-[var(--ink)]">{task.title}</h2>
|
||||
@@ -247,17 +286,31 @@ export function InvestigationTaskReport({
|
||||
</div>
|
||||
<p className="mt-1 text-xs leading-5 text-[var(--ink-muted)]">{task.summary}</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
title="打开调查协作对话,引用此任务,不会直接发送。"
|
||||
onClick={() =>
|
||||
onContinue({ context: snapshot ? { report_revision: snapshot.revision } : {}, reportGeneration: snapshot?.execution_generation })
|
||||
}
|
||||
>
|
||||
<MessageSquare size={14} aria-hidden />
|
||||
Agent 协作
|
||||
</Button>
|
||||
<div className="flex flex-wrap items-center justify-end gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={!onViewExecutionHistory || !executionHistoryTarget || openingExecutionHistory}
|
||||
title={onViewExecutionHistory && executionHistoryTarget ? "在左侧 Agent 中打开所选轮次自动同步的调查执行记录,并可创建调试会话。" : "所选轮次还没有可查看的执行记录。"}
|
||||
onClick={() => void openExecutionHistory()}
|
||||
>
|
||||
{openingExecutionHistory
|
||||
? <LoaderCircle size={14} className="animate-spin" aria-hidden />
|
||||
: <History size={14} aria-hidden />}
|
||||
执行记录
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
title="打开调查协作对话,引用此任务,不会直接发送。"
|
||||
onClick={() =>
|
||||
onContinue({ context: snapshot ? { report_revision: snapshot.revision } : {}, reportGeneration: snapshot?.execution_generation })
|
||||
}
|
||||
>
|
||||
<MessageSquare size={14} aria-hidden />
|
||||
Agent 协作
|
||||
</Button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<InvestigationReportNavigation reportRef={reportRef} sections={sections} />
|
||||
|
||||
Reference in New Issue
Block a user