fix(rectification): put the collect stem in the assistant bubble
The live question slot still rendered the collect prompt below the action icons when the streamed body was only a greeting. Join the GET prompt into that bubble and stop drawing a sibling collect slot. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -323,12 +323,13 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
const previousBoardResult = useRef<CandidateResult>(null);
|
||||
const runAbort = useRef<AbortController | null>(null);
|
||||
const choiceActionIds = useRef(new Map<string, string>());
|
||||
const currentQuestionRef = useRef(currentQuestion);
|
||||
currentQuestionRef.current = currentQuestion;
|
||||
const [compactBoard, setCompactBoard] = useState(false);
|
||||
const [boardOpen, setBoardOpen] = useState(false);
|
||||
const [boardDiff, setBoardDiff] = useState(() => diffRectificationBoard(null, null));
|
||||
const boardId = useId();
|
||||
const boardTitleId = useId();
|
||||
const collectSpokenPromptId = useId();
|
||||
const composerRemainingId = useId();
|
||||
// The same anchor-and-follow the consultation surface uses: streamed tokens and
|
||||
// new cards land the viewport on the bottom only while the reader is there.
|
||||
@@ -427,6 +428,25 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
return () => controller.abort();
|
||||
}, [applyCaseSnapshot, caseId, sessionId]);
|
||||
|
||||
useEffect(() => {
|
||||
const prompt = currentQuestion?.kind === "collect_spoken" ? currentQuestion.prompt : null;
|
||||
if (!prompt || busy || regeneratingMessageKey !== null) return;
|
||||
setMessages((current) => {
|
||||
const last = [...current].reverse().find((message) => (
|
||||
message.role === "assistant"
|
||||
&& message.state === "settled"
|
||||
&& !message.failed
|
||||
&& Boolean(message.text)
|
||||
));
|
||||
if (!last) return current;
|
||||
const combined = composeCollectSpokenAssistantText(last.text, prompt);
|
||||
if (combined === last.text) return current;
|
||||
return current.map((item) => (
|
||||
item.renderKey === last.renderKey ? { ...item, text: combined } : item
|
||||
));
|
||||
});
|
||||
}, [busy, currentQuestion, regeneratingMessageKey]);
|
||||
|
||||
const send = useCallback(async (action: "opening" | "message" | "read_only", messageText: string) => {
|
||||
const trimmed = action === "message" ? messageText.trim() : "";
|
||||
if ((action === "message" && !trimmed) || busy || readonly) return;
|
||||
@@ -647,12 +667,18 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
|
||||
const parsed = completed && !streamFailed ? parseAgentReply(raw) : { text: "", title: undefined };
|
||||
const succeeded = completed && !streamFailed && Boolean(parsed.text);
|
||||
const collectPrompt = currentQuestionRef.current?.kind === "collect_spoken"
|
||||
? currentQuestionRef.current.prompt
|
||||
: null;
|
||||
const settledText = succeeded && collectPrompt
|
||||
? composeCollectSpokenAssistantText(parsed.text, collectPrompt)
|
||||
: parsed.text;
|
||||
setMessages((current) => current.flatMap((message): RenderMessage[] => {
|
||||
if (message.renderKey !== assistantRenderKey) return [message];
|
||||
if (succeeded) {
|
||||
return [{
|
||||
...message,
|
||||
text: parsed.text,
|
||||
text: settledText,
|
||||
activityTrace: completeActivityTrace(activityTrace),
|
||||
state: "settled",
|
||||
completedReceipt,
|
||||
@@ -684,7 +710,7 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
if (succeeded) {
|
||||
onMessagesChange?.([
|
||||
...(action === "message" ? [{ role: "user" as const, text: trimmed }] : []),
|
||||
{ role: "assistant", text: parsed.text },
|
||||
{ role: "assistant", text: settledText },
|
||||
]);
|
||||
onCompleted?.();
|
||||
await loadCaseSnapshot();
|
||||
@@ -1048,20 +1074,6 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
const collectSpokenPrompt = currentQuestion?.kind === "collect_spoken"
|
||||
? currentQuestion.prompt
|
||||
: null;
|
||||
const latestCollectBody = latestSettledAssistant?.text ?? "";
|
||||
const collectStemAlreadyInLatestBody = Boolean(
|
||||
collectSpokenPrompt
|
||||
&& latestSettledAssistant
|
||||
&& composeCollectSpokenAssistantText(latestCollectBody, collectSpokenPrompt) === latestCollectBody.trim(),
|
||||
);
|
||||
const showCollectSpokenPrompt = Boolean(
|
||||
collectSpokenPrompt
|
||||
&& !collectStemAlreadyInLatestBody
|
||||
&& !showLiveChoiceCard
|
||||
&& !readonly
|
||||
&& !busy
|
||||
&& regeneratingMessageKey === null,
|
||||
);
|
||||
const resumableCase = caseStatus !== null && isResumableStatus(caseStatus);
|
||||
const showMissingQuestion = Boolean(
|
||||
caseSnapshotLoaded
|
||||
@@ -1081,7 +1093,6 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
);
|
||||
const showQuestionSlot = Boolean(
|
||||
showLiveChoiceCard
|
||||
|| showCollectSpokenPrompt
|
||||
|| showMissingQuestion
|
||||
|| showUnavailableQuestion,
|
||||
);
|
||||
@@ -1164,12 +1175,21 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
? vargaSentenceFromMethods(message.completedReceipt?.methods)
|
||||
: null;
|
||||
const isLatestMessage = message.renderKey === messages[messages.length - 1]?.renderKey;
|
||||
const bubbleText = !regenerating
|
||||
&& isLatestMessage
|
||||
&& message.role === "assistant"
|
||||
&& collectSpokenPrompt
|
||||
? composeCollectSpokenAssistantText(displayedMessage.text, collectSpokenPrompt)
|
||||
: displayedMessage.text;
|
||||
const bubbleMessage = bubbleText === displayedMessage.text
|
||||
? displayedMessage
|
||||
: { ...displayedMessage, text: bubbleText };
|
||||
return (
|
||||
<div key={message.renderKey} className="rectification-message-wrap rectification-message-entry">
|
||||
{(!message.failed || Boolean(displayedMessage.text) || regenerating) && (
|
||||
{(!message.failed || Boolean(bubbleMessage.text) || regenerating) && (
|
||||
<ChatMessageRow
|
||||
message={displayedMessage}
|
||||
showActivity={displayedMessage.state !== "settled"}
|
||||
message={bubbleMessage}
|
||||
showActivity={bubbleMessage.state !== "settled"}
|
||||
vargaSentence={vargaSentence}
|
||||
/>
|
||||
)}
|
||||
@@ -1182,7 +1202,7 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
...current,
|
||||
[message.renderKey]: toggleRectificationFeedback(current[message.renderKey], requested),
|
||||
}))}
|
||||
onCopy={() => void copyMessage(message)}
|
||||
onCopy={() => void copyMessage({ ...message, text: bubbleText })}
|
||||
onRegenerate={() => void regenerateMessage(message)}
|
||||
/>
|
||||
)}
|
||||
@@ -1220,11 +1240,6 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
onStop={submitStop}
|
||||
/>
|
||||
)}
|
||||
{showCollectSpokenPrompt && (
|
||||
<p id={collectSpokenPromptId} className="rectification-question-slot__prompt">
|
||||
{collectSpokenPrompt}
|
||||
</p>
|
||||
)}
|
||||
{showMissingQuestion && (
|
||||
<p className="rectification-question-slot__status" role="status">
|
||||
当前没有可回答的问题,正在等待服务端更新。
|
||||
@@ -1253,11 +1268,6 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
onStop={submitStop}
|
||||
/>
|
||||
)}
|
||||
{showCollectSpokenPrompt && (
|
||||
<p id={collectSpokenPromptId} className="rectification-question-slot__prompt">
|
||||
{collectSpokenPrompt}
|
||||
</p>
|
||||
)}
|
||||
{showMissingQuestion && (
|
||||
<p className="rectification-question-slot__status" role="status">
|
||||
当前没有可回答的问题,正在等待服务端更新。
|
||||
@@ -1298,9 +1308,6 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
)}
|
||||
<ChatComposer
|
||||
inputRef={composer}
|
||||
describedBy={showCollectSpokenPrompt
|
||||
? collectSpokenPromptId
|
||||
: undefined}
|
||||
value={draft}
|
||||
remainingId={composerRemainingId}
|
||||
inputLabel={readonly ? "该校正已结束,只能查看历史" : "继续描述你的经历或回答"}
|
||||
@@ -1308,7 +1315,7 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
? "该校正已结束,只能查看历史;需要再次校正请新建。"
|
||||
: showLiveChoiceCard
|
||||
? "点上面的选项即可;想补一句细节再写"
|
||||
: showCollectSpokenPrompt || collectStemAlreadyInLatestBody
|
||||
: collectSpokenPrompt
|
||||
? "请回答上面的问题…"
|
||||
: "继续说你记得的人生经历,或回答刚才的问题…"}
|
||||
maxLength={RECTIFICATION_COMPOSER_MAX_LENGTH}
|
||||
|
||||
Reference in New Issue
Block a user