Files
Jyotisha/frontend/src/lib/rectification-agentic/v9/collect-prompt.ts
T
Jesse_ChenandCursor 9d1c08cab1
Independent Staging Quality Gate / validate (push) Successful in 9m48s
Independent Staging Quality Gate / publish (push) Successful in 7m33s
fix(rectification): keep one spoken paragraph and strip body questions (BUG-584, BUG-585)
Set-focus must not append a second narration, and a server-owned stem must not also appear as a question in the assistant body.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 09:05:01 +08:00

66 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Server-owned collect stems join the same assistant turn. Comparison is
* exact identity of the prompt string being attached, never a semantic
* “did this body already ask”.
*/
const SENTENCE_SPLIT = /(?<=[。!??\n])/;
const NARRATIVE_SENTENCE = /范围|记下|对照|\d{1,2}:\d{2}/;
function isQuestionSentence(text: string, stem: string): boolean {
if (!text) return false;
if (stem && text === stem) return true;
const prefix = stem.slice(0, 12);
if (prefix && text.startsWith(prefix)) return true;
return /[??]$/.test(text);
}
export function stripQuestionSentences(body: string, stem: string): string {
const prompt = stem.trim();
const spoken = body.trim();
if (!spoken) return "";
const kept: string[] = [];
let dropContinuation = false;
for (const part of spoken.split(SENTENCE_SPLIT)) {
const text = part.trim();
if (!text) continue;
if (isQuestionSentence(text, prompt)) {
dropContinuation = true;
continue;
}
if (dropContinuation && !NARRATIVE_SENTENCE.test(text)) {
dropContinuation = false;
continue;
}
dropContinuation = false;
kept.push(part);
}
return kept.join("").trim();
}
export function composeCollectSpokenAssistantText(body: string, prompt: string): string {
const stem = prompt.trim();
const spoken = body.trim();
if (!stem) return spoken;
if (!spoken || spoken === stem) return stem;
const stripped = stripQuestionSentences(spoken, stem);
if (!stripped) return stem;
const suffix = `\n\n${stem}`;
if (stripped.includes(stem)) return stripped;
if (stripped.length >= suffix.length && stripped.slice(stripped.length - suffix.length) === suffix) {
return stripped;
}
return `${stripped}${suffix}`;
}
export function detachCollectSpokenAssistantText(body: string, prompt: string): string {
const stem = prompt.trim();
const spoken = body.trim();
if (!stem || !spoken || spoken === stem) return spoken;
const suffix = `\n\n${stem}`;
if (spoken.length >= suffix.length && spoken.slice(spoken.length - suffix.length) === suffix) {
return spoken.slice(0, spoken.length - suffix.length).trim();
}
return spoken;
}