fix(web): keep thinking off the spoken consult and rectification answer

Enumerate evidence kinds so education cannot be proposed as a kind, and stream Chinese thinking on a separate channel that collapses when the reply arrives.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-08-21 21:30:55 +08:00
parent 56577504d8
commit 4e247c112e
27 changed files with 465 additions and 73 deletions
+16
View File
@@ -740,6 +740,22 @@ button:disabled { cursor: default; opacity: .45; }
.message-content { min-width: 0; max-width: min(80%, 680px); }
.message-bubble { overflow: hidden; border: 0; padding: var(--space-3) var(--space-4); border-radius: var(--radius-lg); background: var(--color-canvas-muted); }
.message-assistant .message-bubble { border-radius: 0; background: transparent; padding: var(--space-3) 0; }
.message-thinking {
margin: 0 0 var(--space-3);
}
.message-thinking > summary {
cursor: pointer;
color: var(--color-ink-tertiary);
font-size: 13px;
line-height: 1.5;
}
.message-thinking-body {
margin-top: var(--space-2);
color: var(--color-ink-tertiary);
font-size: 13px;
line-height: 1.55;
white-space: pre-wrap;
}
.rectification-message-entry { min-width: 0; }
.message-actions {
display: flex;
+25 -3
View File
@@ -217,7 +217,7 @@ type ReplyOutcome = {
readonly phase: Extract<ChatReplyPhase, "completed" | "stopped" | "failed">;
readonly replyOrdinal: number;
};
type StreamingReply = { sessionId: string; text: string; activity?: AgentActivityView };
type StreamingReply = { sessionId: string; text: string; activity?: AgentActivityView; thinkingText?: string };
type BirthPlace = {
label: string;
lat: number;
@@ -1169,6 +1169,9 @@ export default function Home() {
const activeStreamingActivity = streamingReply && streamingReply.sessionId === activeSession?.id
? streamingReply.activity
: undefined;
const activeStreamingThinking = streamingReply && streamingReply.sessionId === activeSession?.id
? streamingReply.thinkingText
: undefined;
const activeReplyOutcome = replyOutcome && replyOutcome.sessionId === activeSession?.id ? replyOutcome : null;
const replyPhase: ChatReplyPhase = isLoading
? consultationPhase === "recovering" ? "recovering" : "generating"
@@ -1977,7 +1980,13 @@ export default function Home() {
title: session.title,
theme: session.theme,
model_id: session.modelId,
messages: session.messages,
messages: session.messages.map((message) => ({
role: message.role,
text: message.text,
techniqueTruth: message.techniqueTruth,
agentExecutionReceipt: message.agentExecutionReceipt,
workflowReceipt: message.workflowReceipt,
})),
session_type: session.sessionType,
rectification_case_id: session.rectificationCaseId,
updated_at: new Date(session.updatedAt).toISOString(),
@@ -3230,12 +3239,14 @@ export default function Home() {
const reader = response.body.getReader();
const decoder = new TextDecoder();
let answer = "";
let thinking = "";
const updateStreamingAnswer = (activity?: AgentActivityView) => {
const partialReply = parseAgentReply(answer).text;
latestPartialReply = partialReply;
setStreamingReply((current) => ({
sessionId,
text: partialReply,
thinkingText: current?.sessionId === sessionId ? current.thinkingText : undefined,
activity: activity
? nextActivityView(current?.sessionId === sessionId ? current.activity : undefined, activity)
: current?.sessionId === sessionId ? current.activity : undefined,
@@ -3277,6 +3288,15 @@ export default function Home() {
if ((response.headers.get("content-type") ?? "").includes("application/x-ndjson")) {
const parser = createNdjsonParser((event) => {
if (event.type === "answer.delta") answer += event.text;
if (event.type === "thinking.delta") {
thinking += event.text;
setStreamingReply((current) => ({
sessionId,
text: current?.sessionId === sessionId ? current.text : parseAgentReply(answer).text,
thinkingText: thinking,
activity: current?.sessionId === sessionId ? current.activity : undefined,
}));
}
if (event.type === "run.completed") {
runCompleted = true;
agentExecutionReceipt = event.receipt;
@@ -3312,6 +3332,7 @@ export default function Home() {
messages: [...userSession.messages, {
role: "assistant",
text: reply.text,
thinkingText: thinking || undefined,
techniqueTruth,
workflowReceipt,
agentExecutionReceipt,
@@ -3355,6 +3376,7 @@ export default function Home() {
messages: [...userSession.messages, {
role: "assistant",
text: reply.text,
thinkingText: thinking || undefined,
techniqueTruth,
workflowReceipt,
agentExecutionReceipt,
@@ -3803,7 +3825,7 @@ export default function Home() {
</div>
) : (
<div className="message-list" aria-busy={isLoading}>
{chatMessageViews(activeSession.messages, isLoading, activeStreamingText, activeStreamingActivity).map((message, _, views) => {
{chatMessageViews(activeSession.messages, isLoading, activeStreamingText, activeStreamingActivity, activeStreamingThinking).map((message, _, views) => {
const showActions = message.role === "assistant"
&& message.state === "settled"
&& Boolean(message.text);