fix(coach): keep SOS out of Coach chat history
SOS (urge.tsx) uses /api/coach/message as a stateless LLM proxy for game comments, share drafts and the stream fallback — sending SOS_BOOT + [INTERN:] prompts. The endpoint persisted the full messages array into coachSession for pro/legend users, so those internal prompts and the raw JSON replies leaked into the Coach chat history as visible bubbles. - Reactivate the sosMode flag (already sent by all three SOS call-sites): when set, the endpoint skips coachSession persistence, memory extraction and feedback detection — pure LLM proxy, no shared state. - Add a defensive filter on /api/coach/history that strips internal messages (SOS_BOOT, [INTERN:], [SYSTEM-HINT], raw JSON / [[CHIPS]] replies) so already-contaminated sessions self-heal on next load. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
848b517d22
commit
ba200d54f4
@ -36,7 +36,30 @@ export default defineEventHandler(async (event) => {
|
|||||||
return { messages: [] };
|
return { messages: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Defensiver Filter: Alt-Sessions können vor dem sosMode-Fix mit SOS-Resten
|
||||||
|
// kontaminiert sein (SOS_BOOT, [INTERN:]/[SYSTEM-HINT]-Prompts, rohe
|
||||||
|
// JSON-/[[CHIPS]]-Antworten). Solche Nachrichten gehören NIE in den Coach-Chat
|
||||||
|
// → beim Laden rausfiltern, damit bestehende Verläufe automatisch heilen.
|
||||||
|
const raw =
|
||||||
|
(session.content as Array<{ role: string; content: string }>) ?? [];
|
||||||
|
const isInternal = (m: { role: string; content: string }) => {
|
||||||
|
const c = (m?.content ?? "").trimStart();
|
||||||
|
if (
|
||||||
|
c.startsWith("[SOS-MODUS") ||
|
||||||
|
c.startsWith("[INTERN:") ||
|
||||||
|
c.startsWith("[SYSTEM-HINT]")
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Rohe Lyra-JSON-Antwort (Game-Kommentar / Share-Draft) oder Chips-Marker
|
||||||
|
if (m?.role === "assistant") {
|
||||||
|
if (c.includes("[[CHIPS]]:")) return true;
|
||||||
|
if (/^\{[\s\S]*"message"\s*:/.test(c)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
messages: session.content as Array<{ role: string; content: string }>,
|
messages: raw.filter((m) => !isInternal(m)),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@ -460,11 +460,17 @@ export default defineEventHandler(async (event) => {
|
|||||||
const user = await requireUser(event);
|
const user = await requireUser(event);
|
||||||
|
|
||||||
const body = await readBody(event);
|
const body = await readBody(event);
|
||||||
// sosMode ist deprecated — Coach-Page sendet es nicht mehr.
|
// sosMode: die SOS-Page (urge.tsx) nutzt diesen Endpoint als zustandslosen
|
||||||
// Wird hier nur noch für Logging akzeptiert, beeinflusst kein Routing.
|
// LLM-Proxy für Game-Kommentar / Share-Draft / Stream-Fallback. Diese Calls
|
||||||
const { messages, locale } = body as {
|
// tragen SOS_BOOT + [INTERN:]-Prompts in der messages-Array und DÜRFEN NICHT
|
||||||
|
// in die Coach-Chat-History (coachSession) geschrieben werden — sonst tauchen
|
||||||
|
// SOS-Reste (z.B. "[INTERN: …Snake…PB 13…]") als Bubbles im Coach-Tab auf.
|
||||||
|
// SOS und Coaching sind strikt getrennt: bei sosMode === true keine
|
||||||
|
// Persistenz, keine Memory-Extraction, keine Feedback-Detection.
|
||||||
|
const { messages, locale, sosMode } = body as {
|
||||||
messages: Array<{ role: "user" | "assistant"; content: string }>;
|
messages: Array<{ role: "user" | "assistant"; content: string }>;
|
||||||
locale?: string;
|
locale?: string;
|
||||||
|
sosMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!messages || !Array.isArray(messages)) {
|
if (!messages || !Array.isArray(messages)) {
|
||||||
@ -705,7 +711,10 @@ export default defineEventHandler(async (event) => {
|
|||||||
|
|
||||||
// Feedback-Detection + LLM parallel starten
|
// Feedback-Detection + LLM parallel starten
|
||||||
// (lastUserMsg ist bereits oben für Sprach-Detection berechnet)
|
// (lastUserMsg ist bereits oben für Sprach-Detection berechnet)
|
||||||
const feedbackPromise = lastUserMsg?.content
|
// Im sosMode ist lastUserMsg ein [INTERN:]-Prompt, kein echtes User-Feedback
|
||||||
|
// → Detection überspringen.
|
||||||
|
const feedbackPromise =
|
||||||
|
!sosMode && lastUserMsg?.content
|
||||||
? detectAndSaveFeedback(lastUserMsg.content, user.id, config)
|
? detectAndSaveFeedback(lastUserMsg.content, user.id, config)
|
||||||
: Promise.resolve(false);
|
: Promise.resolve(false);
|
||||||
|
|
||||||
@ -736,11 +745,13 @@ export default defineEventHandler(async (event) => {
|
|||||||
|
|
||||||
const feedbackSaved = await feedbackPromise;
|
const feedbackSaved = await feedbackPromise;
|
||||||
|
|
||||||
// Memory: markReferenced + Extraction fire-and-forget
|
// Memory: markReferenced + Extraction fire-and-forget.
|
||||||
|
// sosMode überspringt Extraction — die messages-Array enthält SOS_BOOT +
|
||||||
|
// [INTERN:]-Prompts, daraus würden nur Müll-Memories entstehen.
|
||||||
if (loadedMemoryIds.length > 0) {
|
if (loadedMemoryIds.length > 0) {
|
||||||
markReferenced(loadedMemoryIds).catch(() => {});
|
markReferenced(loadedMemoryIds).catch(() => {});
|
||||||
}
|
}
|
||||||
if (text) {
|
if (text && !sosMode) {
|
||||||
const allMessages = [
|
const allMessages = [
|
||||||
...messages,
|
...messages,
|
||||||
{ role: "assistant" as const, content: text },
|
{ role: "assistant" as const, content: text },
|
||||||
@ -751,10 +762,12 @@ export default defineEventHandler(async (event) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chat-Verlauf für Pro/Legend in DB speichern
|
// Chat-Verlauf für Pro/Legend in DB speichern.
|
||||||
|
// sosMode NICHT persistieren — SOS und Coach-Chat sind strikt getrennt;
|
||||||
|
// SOS-Reste dürfen nie in der Coach-History (coachSession) landen.
|
||||||
// `plan` ist bereits oben deklariert (LLM-Routing-Block)
|
// `plan` ist bereits oben deklariert (LLM-Routing-Block)
|
||||||
console.log("[coach/message] plan:", plan, "userId:", user.id);
|
console.log("[coach/message] plan:", plan, "userId:", user.id, "sosMode:", !!sosMode);
|
||||||
if (plan === "pro" || plan === "legend") {
|
if (!sosMode && (plan === "pro" || plan === "legend")) {
|
||||||
const fullHistory = [
|
const fullHistory = [
|
||||||
...messages,
|
...messages,
|
||||||
{ role: "assistant" as const, content: text },
|
{ role: "assistant" as const, content: text },
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user