diff --git a/components/reasoning-workspace.jsx b/components/reasoning-workspace.jsx index a947725..5e1d4c7 100644 --- a/components/reasoning-workspace.jsx +++ b/components/reasoning-workspace.jsx @@ -120,18 +120,36 @@ function CurrentUnderstanding({ currentSummary }) { } // ── Current investigation card (prominent) ───────────────────── -function CurrentInvestigationCard({ selectedQuestion }) { +function CurrentInvestigationCard({ selectedQuestion, graph }) { if (!selectedQuestion) return null; const q = typeof selectedQuestion === "string" ? selectedQuestion : selectedQuestion.question; if (!q) return null; + const activeNode = graph?.activeUnknownNodeId + ? graph.nodes.find((n) => n.id === graph.activeUnknownNodeId) + : null; + return ( -
+

Current investigation

{q}

+ {activeNode?.description && activeNode.description !== activeNode.label && ( +
+

Why we are asking this

+

{activeNode.description}

+
+ )} + {graph?.activeUnknownNodeId && activeNode && ( +
+

What we are investigating

+

+ Understanding whether "{activeNode.label}" affects the confidence in this situation. +

+
+ )}
); } @@ -152,49 +170,70 @@ function hasGenuineCompletion(graph) { return true; } -// ── Investigation progress card ────────────────────────────── -function InvestigationProgress({ graph, noQuestionReason: rwNoQuestionReason }) { - if (!graph?.nodes?.length) return null; - - const resolvedIds = new Set(graph.resolvedNodeIds || []); - const unknowns = graph.nodes.filter((n) => n.kind === "unknown"); - const remainingCount = unknowns.filter( - (u) => u.status !== "resolved" && !resolvedIds.has(u.id), - ).length; - const activeNode = graph.activeUnknownNodeId +// ── Current focus card ─────────────────────────────────────── +function CurrentFocusCard({ graph }) { + const activeNode = graph?.activeUnknownNodeId ? graph.nodes.find((n) => n.id === graph.activeUnknownNodeId) : null; - const isComplete = hasGenuineCompletion(graph); + if (!graph || !activeNode) return null; return ( -
- {remainingCount > 0 && !isComplete ? ( -

- We are still building confidence about your situation.{" "} - {remainingCount === 1 - ? "One area remains." - : `${remainingCount} areas remain.`} -

- ) : ( -

- All areas under investigation are now complete. -

- )} - {activeNode && ( - <> -

- Current focus -

-

{activeNode.label}

- {activeNode.description && activeNode.description !== activeNode.label && ( -

Why it matters: {activeNode.description}

- )} - - )} - {!activeNode && remainingCount === 0 && ( -

There is nothing further to investigate at this time.

- )} +
+

+ Current focus +

+

+ We are investigating one part of your situation at a time. + {activeNode && ( + <> +
+ Right now we are trying to understand{" "} + {activeNode.label}. + + )} +

+
+ ); +} + +// ── Investigation history card ──────────────────────────────── +function InvestigationHistoryCard({ turn }) { + return ( +
+ + {new Date(turn.timestamp).toLocaleString(undefined, { + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + })} + +
+

{turn.question}

+

{turn.answer}

+ {turn.engineResponse && ( +

{turn.engineResponse}

+ )} +
+
+ ); +} + +// ── Investigation history section ───────────────────────────── +function InvestigationHistory({ turns }) { + if (!turns || turns.length === 0) return null; + + return ( +
+

+ Investigation history +

+
+ {turns.map((turn, idx) => ( + + ))} +
); } @@ -251,13 +290,12 @@ function LoadingOverlay({ isLoading, elapsed, currentMessage, variant }) { } // ── Update acknowledgement ──────────────────────────────────── -function UpdateAcknowledgement({ answer, updateResult }) { - if (!updateResult || !answer?.trim()) return null; +function UpdateAcknowledgement({ updateResult }) { + if (!updateResult) return null; - const hasResolvedNodes = - updateResult.resolvedUnknownNodeIds && updateResult.resolvedUnknownNodeIds.length > 0; - const hasAffectedNodes = - updateResult.affectedNodeIds && updateResult.affectedNodeIds.length > 0; + const summary = updateResult.summary; + const hasResolvedNodes = updateResult.resolvedUnknownNodeIds?.length > 0; + const hasAffectedNodes = updateResult.affectedNodeIds?.length > 0; const graph = updateResult.updatedSituationGraph; function getNodeText(nodeId) { @@ -274,34 +312,24 @@ function UpdateAcknowledgement({ answer, updateResult }) { } let changedText; - if (hasResolvedNodes || hasAffectedNodes) { + if (hasResolvedNodes) { const items = []; - if (hasResolvedNodes) { - for (const id of updateResult.resolvedUnknownNodeIds.slice(0, 5)) { - items.push(getNodeText(id)); - } - if (updateResult.resolvedUnknownNodeIds.length > 5) { - items.push(`and ${updateResult.resolvedUnknownNodeIds.length - 5} more resolved`); - } + for (const id of updateResult.resolvedUnknownNodeIds.slice(0, 3)) { + items.push(getNodeText(id)); } - if (hasAffectedNodes && !hasResolvedNodes) { - for (const id of updateResult.affectedNodeIds.slice(0, 5)) { - items.push(getNodeText(id)); - } - if (updateResult.affectedNodeIds.length > 5) { - items.push(`and ${updateResult.affectedNodeIds.length - 5} more affected`); - } + if (updateResult.resolvedUnknownNodeIds.length > 3) { + items.push(`and ${updateResult.resolvedUnknownNodeIds.length - 3} more resolved`); } - if (items.length === 0 && updateResult.changesApplied) { - const parts = []; - const ca = updateResult.changesApplied; - if (ca.addedNodeCount) parts.push(`${ca.addedNodeCount} node(s) added`); - if (ca.updatedNodeCount) parts.push(`${ca.updatedNodeCount} node(s) updated`); - if (ca.resolvedUnknownCount) parts.push(`${ca.resolvedUnknownCount} unknown(s) resolved`); - changedText = parts.join(", "); - } else { - changedText = items.join(". ") + "."; + changedText = items.join(". ") + "."; + } else if (hasAffectedNodes) { + const items = []; + for (const id of updateResult.affectedNodeIds.slice(0, 3)) { + items.push(getNodeText(id)); } + if (updateResult.affectedNodeIds.length > 3) { + items.push(`and ${updateResult.affectedNodeIds.length - 3} more affected`); + } + changedText = items.join(". ") + "."; } else if (updateResult.changesApplied) { const ca = updateResult.changesApplied; const parts = []; @@ -312,23 +340,11 @@ function UpdateAcknowledgement({ answer, updateResult }) { changedText = parts.length > 0 ? parts.join(", ") : null; } - const summary = updateResult.summary || null; - const displayChanged = summary || changedText || "Your answer has been added to the investigation."; + const displayMessage = summary || changedText || "Your answer has been added to the investigation."; return ( -
-
-

- You told us -

-

{answer}

-
-
-

- What changed -

-

{displayChanged}

-
+
+ {displayMessage}
); } @@ -367,6 +383,35 @@ export default function ReasoningWorkspace({ onAnswerSubmit, lastSubmittedAnswer, }) { + const [investigationHistory, setInvestigationHistory] = useState([]); + + // Capture the previous question before each new question is set + const prevQuestionRef = useRef(null); + const hasCapturedInitialQuestion = useRef(false); + + useEffect(() => { + if (result?.selectedQuestion && !hasCapturedInitialQuestion.current) { + prevQuestionRef.current = result.selectedQuestion; + hasCapturedInitialQuestion.current = true; + } + }, [result?.selectedQuestion]); + + // Append completed turn to history after a successful update + useEffect(() => { + if (updateStatus === "success" && lastSubmittedAnswer) { + const q = prevQuestionRef.current; + setInvestigationHistory((prev) => [ + ...prev, + { + question: typeof q === "string" ? q : q?.question ?? "", + answer: lastSubmittedAnswer, + engineResponse: result?.summary || null, + timestamp: Date.now(), + }, + ]); + } + }, [updateStatus, lastSubmittedAnswer]); + const { elapsed: startElapsed, currentMessage: startMsg } = useLoadingStatus( INITIAL_MESSAGES, status === "loading" @@ -392,12 +437,7 @@ export default function ReasoningWorkspace({ const newlySurfacedNodeIds = result?.newlySurfacedNodeIds || []; const noQuestionReason = diagnostics?.noQuestionReason ?? null; - const remainingUnknowns = graph?.nodes?.filter( - (n) => n.kind === "unknown" && n.status !== "resolved" && !(graph.resolvedNodeIds || []).includes(n.id), - ); - const genuineCompletion = hasGenuineCompletion(graph); - const unresolvedRemaining = !genuineCompletion && remainingUnknowns ? remainingUnknowns.length > 0 : false; return (
@@ -427,26 +467,27 @@ export default function ReasoningWorkspace({
) : ( <> - {/* ── Post-update acknowledgement ─────────────── */} - {updateStatus === "success" && graph && ( - - )} + {/* Post-update acknowledgement */} + {updateStatus === "success" && canAnswer && } - {/* Completion state (only when there is no next question and nothing remains) */} + {/* Completion state */} {status === "success" && !canAnswer && graph && genuineCompletion && ( )} - {status === "success" && !canAnswer && graph && unresolvedRemaining && updateStatus !== "success" && ( + {status === "success" && !canAnswer && graph && !genuineCompletion && updateStatus !== "success" && (

There is no further question the engine can justify at the moment.

More evidence may be needed before a next step is clear.

)} + {/* Situation context */} {graph && } {graph && } - {canAnswer && } - {graph && } + + {/* Active investigation (only when we have a question to answer) */} + {canAnswer && } + {canAnswer && } {/* ── Answer form ──────────────────────────────── */} {canAnswer && ( @@ -480,6 +521,9 @@ export default function ReasoningWorkspace({ )} + {/* ── Investigation history (below the answer form) ─ */} + + {/* ── Developer details (collapsed by default) ─── */} {(status === "success" || status === "error") && graph && (