feat: expand intelligent investigation workspace
Generic Container CI/CD / test-build-publish (push) Successful in 1m52s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 1m52s

This commit is contained in:
2026-10-30 17:30:19 +08:00
parent ddd03dbff6
commit c0e4231b83
223 changed files with 13155 additions and 5304 deletions
@@ -1,4 +1,5 @@
import { useEffect, useState, type ReactNode } from "react";
import { useEffect, useRef, useState, type ReactNode } from "react";
import { InvestigationQuestions } from "./investigation-questions";
import { History, LoaderCircle, MessageSquare } from "lucide-react";
import { Button } from "@/shared/ui/button";
import { Disclosure } from "@/shared/ui/disclosure";
@@ -34,6 +35,7 @@ type Props = {
onViewExecutionHistory?: (target: TaskExecutionHistoryTarget, knownRunId?: string) => Promise<void> | void;
renderAction: (action: OperationalTaskAction) => ReactNode;
renderEvidence: (evidence: OperationalTaskEvidence) => ReactNode;
onAnswerQuestion?: import("../api/operational-task-client").AnswerTaskQuestion;
onLoadReports?: (offset: number) => Promise<OperationalTaskReportPage>;
onReportViewed?: (generation: number, revision: number) => void;
focusActionId?: string | null;
@@ -82,6 +84,12 @@ function investigationTimeline(
detail: snapshot.report.current_conclusion
});
}
task.questions?.forEach((question) => {
items.push({ id: `question:${question.question_request_id}`, occurredAt: question.created_at,
title: "调查提出补充问题", detail: question.questions.map((item) => item.title).join(";") });
if (question.answered_at) items.push({ id: `answer:${question.question_request_id}`, occurredAt: question.answered_at,
title: "已收到调查回答", detail: "回答已保存为用户补充信息,调查接续处理。" });
});
task.actions.forEach((action) => {
const title = actionTitle(action);
items.push({
@@ -133,10 +141,14 @@ export function InvestigationTaskReport({
renderAction,
renderEvidence,
onLoadReports,
onAnswerQuestion,
onReportViewed,
focusActionId,
onLocate
}: Props) {
const questionsRef = useRef<HTMLElement>(null);
const hasPendingQuestions = task.questions?.some((request) => request.status === "pending"
&& request.execution_generation === task.execution_generation) ?? false;
// Null follows live task content. Only a deliberate history selection freezes a report.
const [selection, setSelection] = useState<ReportSelection | null>(null);
const [history, setHistory] = useState<OperationalTaskReport[]>([]);
@@ -313,6 +325,8 @@ export function InvestigationTaskReport({
</div>
</header>
<InvestigationQuestions task={task} onAnswer={onAnswerQuestion} sectionRef={questionsRef} />
<InvestigationReportNavigation reportRef={reportRef} sections={sections} />
{latestAvailable ? (
@@ -489,14 +503,19 @@ export function InvestigationTaskReport({
<Button
variant="outline"
size="sm"
onClick={() =>
discuss(
`我要补充“${formatInvestigationText(gap.missing_data)}”相关的现场事实。请说明需要的记录范围和时间信息。`,
{ gap_id: gap.gap_id }
)
}
onClick={() => {
if (hasPendingQuestions) {
questionsRef.current?.scrollIntoView({ block: "start" });
questionsRef.current?.focus({ preventScroll: true });
} else {
discuss(
`请帮我讨论“${formatInvestigationText(gap.missing_data)}”这个缺口的查询范围和补齐途径。`,
{ gap_id: gap.gap_id }
);
}
}}
>
补充事实
{hasPendingQuestions ? "回答当前调查问题" : "讨论此缺口"}
</Button>
</div>
</div>
@@ -548,12 +567,6 @@ export function InvestigationTaskReport({
) : null}
</>
)}
onAsk={(node) =>
discuss(
`请解释调查脉络中的“${node.label}”及其关联依据。`,
node.evidence_refs[0] ? { evidence_id: node.evidence_refs[0] } : undefined
)
}
/>
) : null}