fix(rectification): server-append spoken collect stems after free-text turns
Independent Staging Quality Gate / validate (push) Successful in 10m43s
Independent Staging Quality Gate / publish (push) Successful in 9m8s

Choice path already wrote spokenFollowupForUser into the body; free-text
dropped that stem and only filled empty answers, so a new collect_spoken
focus stayed invisible after “记下了”.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-08-30 15:49:20 +08:00
co-authored by Cursor
parent 04b2ccdbe2
commit 52477306cc
12 changed files with 485 additions and 70 deletions
@@ -14,7 +14,7 @@ import {
applyRectificationChoice,
applyCollectFocusDenial,
persistNextInterviewIfIdle,
persistEmptyCollectSpokenAssistant,
persistCollectSpokenAssistantIfNew,
} from "@/lib/rectification-agentic/v9/answer-choice";
import { mapRectificationRpcError } from "@/lib/rectification-agentic/v9/case-service";
import { CHOICE_ACTION, STOP_ACTION } from "@/lib/rectification-agentic/v9/choice-action";
@@ -668,32 +668,35 @@ export async function POST(request: Request) {
send({ type: "error", message: "生时校正暂时不可用,请稍后重试。" });
} else {
if (action === "message") {
let idleHostNarration: string | null = null;
try {
await persistNextInterviewIfIdle({
const idle = await persistNextInterviewIfIdle({
accounting: accounting as never,
userId,
caseId,
});
idleHostNarration = idle.hostNarration;
} catch (error) {
console.warn(
`[rectification-v9] persist next interview after turn failed case=${caseId} reason=${error instanceof Error ? error.name : "Unknown"}`,
);
}
if (!result.answerText.trim()) {
try {
const fallback = await persistEmptyCollectSpokenAssistant({
accounting: accounting as never,
userId,
caseId,
requestId: crypto.randomUUID(),
answerText: result.answerText,
});
if (fallback) send({ type: "answer.delta", text: fallback });
} catch (error) {
console.warn(
`[rectification-v9] empty collect spoken fallback failed case=${caseId} reason=${error instanceof Error ? error.name : "Unknown"}`,
);
}
try {
const fallback = await persistCollectSpokenAssistantIfNew({
accounting: accounting as never,
userId,
caseId,
requestId: crypto.randomUUID(),
answerText: result.answerText,
previousFocusId: result.previousFocusId,
alreadyEmitted: result.collectSpokenEmitted,
hostNarration: idleHostNarration,
});
if (fallback) send({ type: "answer.delta", text: fallback });
} catch (error) {
console.warn(
`[rectification-v9] collect spoken visibility fallback failed case=${caseId} reason=${error instanceof Error ? error.name : "Unknown"}`,
);
}
}
send({ type: "done", emitted: true });