fix(rectification): show spoken collect questions in the chat UI
Spoken collect prompts lived only in GET current_question. The chat never parsed that field, Agent projections returned null after evidence writes, and active_focus followups collapsed the questionId. Render the parsed prompt, keep choiceReady on real cards, and give collect focuses a stable domain-scoped id. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -59,8 +59,10 @@ import { finalizeRectificationSpokenAndThinking } from "@/lib/rectification-agen
|
||||
import {
|
||||
isPersistedFocusId,
|
||||
parseRectificationChoiceCard,
|
||||
parseRectificationSpokenCollect,
|
||||
type ChoiceKey,
|
||||
type RectificationChoiceCard as ChoiceCardModel,
|
||||
type RectificationSpokenCollect,
|
||||
} from "@/lib/rectification-agentic/v9/choice-card";
|
||||
import type { PublicLanguageModel } from "@/lib/public-models";
|
||||
import { ChatMessageRow } from "./chat-message-row";
|
||||
@@ -286,6 +288,7 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
const [savedStatus, setSavedStatus] = useState<"accepted" | "confirmed" | null>(null);
|
||||
const [candidateResult, setCandidateResult] = useState<CandidateResult>(null);
|
||||
const [choiceCard, setChoiceCard] = useState<ChoiceCardModel | null>(null);
|
||||
const [spokenCollect, setSpokenCollect] = useState<RectificationSpokenCollect | null>(null);
|
||||
const [acceptingCandidateId, setAcceptingCandidateId] = useState<string | null>(null);
|
||||
const [feedback, setFeedback] = useState<Record<string, "up" | "down" | undefined>>({});
|
||||
const [copiedMessageKey, setCopiedMessageKey] = useState<string | null>(null);
|
||||
@@ -388,15 +391,18 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
const applyCaseSnapshot = useCallback((payload: {
|
||||
latest_result?: unknown;
|
||||
choice_card?: unknown;
|
||||
current_question?: unknown;
|
||||
case?: { accepted_time?: unknown; confirmed_time?: unknown };
|
||||
} | null) => {
|
||||
if (!payload) return;
|
||||
const nextCandidate = parseRectificationCandidateResult(payload.latest_result);
|
||||
const nextChoice = parseRectificationChoiceCard(payload.choice_card);
|
||||
const nextSpoken = parseRectificationSpokenCollect(payload.current_question);
|
||||
const acceptedTime = typeof payload.case?.accepted_time === "string" ? payload.case.accepted_time : null;
|
||||
const confirmedTime = typeof payload.case?.confirmed_time === "string" ? payload.case.confirmed_time : null;
|
||||
setCandidateResult(nextCandidate);
|
||||
setChoiceCard(nextChoice);
|
||||
setSpokenCollect(nextSpoken);
|
||||
if (confirmedTime) {
|
||||
setSavedTime(confirmedTime);
|
||||
setSavedStatus("confirmed");
|
||||
@@ -1047,6 +1053,14 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
choiceCardsOpen.current = showLiveChoiceCard;
|
||||
updateFollowState();
|
||||
}, [showLiveChoiceCard, updateFollowState]);
|
||||
const showLiveSpokenCollect = Boolean(
|
||||
!choiceCard
|
||||
&& spokenCollect
|
||||
&& liveChoiceHost
|
||||
&& !busy
|
||||
&& !readonly
|
||||
&& regeneratingMessageKey === null,
|
||||
);
|
||||
const showSelectionCards = Boolean(
|
||||
candidateResult?.selectionAllowed
|
||||
&& offeredSelectionOnce
|
||||
@@ -1061,6 +1075,9 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
const liveChoiceMessageKey = showLiveChoiceCard && liveChoiceHost
|
||||
? liveChoiceHost.renderKey
|
||||
: undefined;
|
||||
const liveSpokenMessageKey = showLiveSpokenCollect && liveChoiceHost
|
||||
? liveChoiceHost.renderKey
|
||||
: undefined;
|
||||
const canSend = !busy && !readonly && !regeneratingMessageKey;
|
||||
|
||||
function submitChoice(key: ChoiceKey) {
|
||||
@@ -1128,6 +1145,9 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
const liveChoice = showLiveChoiceCard && message.renderKey === liveChoiceMessageKey
|
||||
? choiceCard
|
||||
: null;
|
||||
const liveSpoken = showLiveSpokenCollect && message.renderKey === liveSpokenMessageKey
|
||||
? spokenCollect
|
||||
: null;
|
||||
const visibleChoiceCard = settledChoice?.card ?? liveChoice;
|
||||
const vargaSentence = !message.failed
|
||||
? vargaSentenceFromMethods(message.completedReceipt?.methods)
|
||||
@@ -1180,6 +1200,11 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
onStop={submitStop}
|
||||
/>
|
||||
)}
|
||||
{liveSpoken && (
|
||||
<section className="rectification-choice-card" aria-label="口述采集题">
|
||||
<p className="rectification-spoken-collect-prompt">{liveSpoken.prompt}</p>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user