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>
66 lines
1.9 KiB
TypeScript
66 lines
1.9 KiB
TypeScript
import { usePrisma } from "../../utils/prisma";
|
|
|
|
/**
|
|
* GET /api/coach/history
|
|
* Lädt den gespeicherten Chat-Verlauf (nur Pro/Legend).
|
|
* Gibt das neueste CoachSession-Dokument zurück.
|
|
*/
|
|
export default defineEventHandler(async (event) => {
|
|
const user = await requireUser(event);
|
|
console.log("[coach/history] userId:", user.id);
|
|
|
|
const db = usePrisma();
|
|
|
|
const profile = await db.profile.findUnique({
|
|
where: { id: user.id },
|
|
select: { plan: true },
|
|
});
|
|
console.log("[coach/history] plan:", profile?.plan);
|
|
|
|
if (!profile || !["pro", "legend"].includes(profile.plan)) {
|
|
return { messages: [] };
|
|
}
|
|
|
|
const session = await db.coachSession.findFirst({
|
|
where: { userId: user.id },
|
|
orderBy: { createdAt: "desc" },
|
|
});
|
|
console.log(
|
|
"[coach/history] session found:",
|
|
!!session,
|
|
"msgs:",
|
|
Array.isArray(session?.content) ? (session.content as any[]).length : 0,
|
|
);
|
|
|
|
if (!session) {
|
|
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 {
|
|
messages: raw.filter((m) => !isInternal(m)),
|
|
};
|
|
});
|