fix: preserve accurate investigation history
This commit is contained in:
@@ -103,18 +103,14 @@ function SituationCard({ centralStatement }) {
|
|||||||
function CurrentUnderstanding({ currentSummary }) {
|
function CurrentUnderstanding({ currentSummary }) {
|
||||||
const summary = resolveCurrentSummary(currentSummary);
|
const summary = resolveCurrentSummary(currentSummary);
|
||||||
|
|
||||||
|
if (!summary) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="investigation-card rounded-lg border border-gray-200 bg-white p-5">
|
<div className="investigation-card rounded-lg border border-gray-200 bg-white p-5">
|
||||||
<h2 className="mb-3 text-sm font-semibold uppercase tracking-wide text-gray-500">
|
<h2 className="mb-3 text-sm font-semibold uppercase tracking-wide text-gray-500">
|
||||||
What we've established
|
What we've established
|
||||||
</h2>
|
</h2>
|
||||||
{summary ? (
|
<p className="text-sm leading-relaxed text-gray-700">{summary}</p>
|
||||||
<p className="text-sm leading-relaxed text-gray-700">{summary}</p>
|
|
||||||
) : (
|
|
||||||
<p className="text-sm leading-relaxed text-gray-600">
|
|
||||||
We have separated what is known from what still needs checking.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -199,21 +195,30 @@ function CurrentFocusCard({ graph }) {
|
|||||||
|
|
||||||
// ── Investigation history card ────────────────────────────────
|
// ── Investigation history card ────────────────────────────────
|
||||||
function InvestigationHistoryCard({ turn }) {
|
function InvestigationHistoryCard({ turn }) {
|
||||||
|
const qSnippet = turn.question.length > 80
|
||||||
|
? turn.question.slice(0, 80) + "…"
|
||||||
|
: turn.question;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<details className="rounded-lg border border-gray-100 bg-gray-50/60 px-4 py-3">
|
<details className="rounded-lg border border-gray-100 bg-gray-50/60 px-4 py-3" key={turn.id}>
|
||||||
<summary className="cursor-pointer text-xs font-semibold uppercase tracking-wide text-gray-400 hover:text-gray-600">
|
<summary className="cursor-pointer text-xs font-semibold uppercase tracking-wide text-gray-400 hover:text-gray-600">
|
||||||
{new Date(turn.timestamp).toLocaleString(undefined, {
|
{new Date(turn.timestamp).toLocaleString(undefined, {
|
||||||
month: "short",
|
month: "short",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
hour: "2-digit",
|
hour: "2-digit",
|
||||||
minute: "2-digit",
|
minute: "2-digit",
|
||||||
})}
|
})} — {qSnippet}
|
||||||
</summary>
|
</summary>
|
||||||
<div className="mt-2 space-y-2 text-sm">
|
<div className="mt-2 space-y-2 text-sm">
|
||||||
<p><strong>{turn.question}</strong></p>
|
<p><strong>Question</strong></p>
|
||||||
|
<p>{turn.question}</p>
|
||||||
|
<p><strong>You answered</strong></p>
|
||||||
<p className="text-gray-700">{turn.answer}</p>
|
<p className="text-gray-700">{turn.answer}</p>
|
||||||
{turn.acknowledgement && (
|
{turn.acknowledgement && (
|
||||||
<p className="italic text-gray-500">{turn.acknowledgement}</p>
|
<>
|
||||||
|
<p><strong>What changed</strong></p>
|
||||||
|
<p className="italic text-gray-500">{turn.acknowledgement}</p>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
@@ -230,8 +235,8 @@ function InvestigationHistory({ turns }) {
|
|||||||
Investigation history
|
Investigation history
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{turns.map((turn, idx) => (
|
{turns.map((turn) => (
|
||||||
<InvestigationHistoryCard key={idx} turn={turn} />
|
<InvestigationHistoryCard key={turn.id} turn={turn} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -384,33 +389,42 @@ export default function ReasoningWorkspace({
|
|||||||
lastSubmittedAnswer,
|
lastSubmittedAnswer,
|
||||||
}) {
|
}) {
|
||||||
const [investigationHistory, setInvestigationHistory] = useState([]);
|
const [investigationHistory, setInvestigationHistory] = useState([]);
|
||||||
|
const turnCounter = useRef(0);
|
||||||
|
const pendingTurnRef = useRef(null);
|
||||||
|
|
||||||
// Capture the previous question before each new question is set
|
// Capture the current selected question at submit time (not from a stale ref)
|
||||||
const prevQuestionRef = useRef(null);
|
const capturePendingTurn = (selectedQuestion, answerText) => {
|
||||||
const hasCapturedInitialQuestion = useRef(false);
|
if (!selectedQuestion || !answerText?.trim()) return null;
|
||||||
|
const q = typeof selectedQuestion === "string" ? selectedQuestion : selectedQuestion.question;
|
||||||
|
if (!q) return null;
|
||||||
|
turnCounter.current += 1;
|
||||||
|
return {
|
||||||
|
id: `turn-${turnCounter.current}`,
|
||||||
|
question: q,
|
||||||
|
answer: answerText.trim(),
|
||||||
|
acknowledgement: null,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Append the captured pending turn to history after a successful update only
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (result?.selectedQuestion && !hasCapturedInitialQuestion.current) {
|
const pending = pendingTurnRef.current;
|
||||||
prevQuestionRef.current = result.selectedQuestion;
|
if (!pending || updateStatus !== "success") return;
|
||||||
hasCapturedInitialQuestion.current = true;
|
|
||||||
}
|
|
||||||
}, [result?.selectedQuestion]);
|
|
||||||
|
|
||||||
// Append completed turn to history after a successful update
|
setInvestigationHistory((prev) => [
|
||||||
useEffect(() => {
|
...prev,
|
||||||
if (updateStatus === "success" && lastSubmittedAnswer) {
|
{ ...pending, acknowledgement: result?.summary || null },
|
||||||
const q = prevQuestionRef.current;
|
]);
|
||||||
setInvestigationHistory((prev) => [
|
pendingTurnRef.current = null;
|
||||||
...prev,
|
}, [updateStatus, result]);
|
||||||
{
|
|
||||||
question: typeof q === "string" ? q : q?.question ?? "",
|
const handleUpdateCaptureAndSubmit = async (e) => {
|
||||||
answer: lastSubmittedAnswer,
|
e.preventDefault();
|
||||||
acknowledgement: result?.summary || null,
|
if (!answer?.trim() || !result?.selectedQuestion) return;
|
||||||
timestamp: Date.now(),
|
pendingTurnRef.current = capturePendingTurn(result.selectedQuestion, answer);
|
||||||
},
|
await onAnswerSubmit(e);
|
||||||
]);
|
};
|
||||||
}
|
|
||||||
}, [updateStatus, lastSubmittedAnswer, result]);
|
|
||||||
|
|
||||||
const { elapsed: startElapsed, currentMessage: startMsg } = useLoadingStatus(
|
const { elapsed: startElapsed, currentMessage: startMsg } = useLoadingStatus(
|
||||||
INITIAL_MESSAGES,
|
INITIAL_MESSAGES,
|
||||||
@@ -494,7 +508,7 @@ export default function ReasoningWorkspace({
|
|||||||
|
|
||||||
{/* ── Answer form ──────────────────────────────── */}
|
{/* ── Answer form ──────────────────────────────── */}
|
||||||
{canAnswer && (
|
{canAnswer && (
|
||||||
<form onSubmit={onAnswerSubmit} className="space-y-4 rounded-lg border border-gray-200 bg-white p-5">
|
<form onSubmit={handleUpdateCaptureAndSubmit} className="space-y-4 rounded-lg border border-gray-200 bg-white p-5">
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="rw-answer" className="mb-2 block text-sm font-medium text-gray-700">
|
<label htmlFor="rw-answer" className="mb-2 block text-sm font-medium text-gray-700">
|
||||||
Your response
|
Your response
|
||||||
|
|||||||
Reference in New Issue
Block a user