Harden the parsing, and mark unread changelog entries

Stress-testing the beta with request shapes we had not tried turned up two
more ways to hit the same class of bug. Roles were compared exactly, so a
client sending "Assistant" instead of "assistant" fell straight into the
dingus error. And content was assumed to be a string, so OpenAI's multipart
array form did the same. Roles are now compared case-insensitively and
content is flattened to text first; messages with no usable text are
skipped rather than mistaken for a slot. The slots are plain strings now,
so no caller can reach past them to a raw field again.

Separately: the Changelog button carries a dot when the current release
has not been opened yet, on the desktop button, the mobile menu item, and
the hamburger that hides it. The marker is the version in localStorage, so
every release brings it back on its own and reading it once clears it. The
version is a single constant now rather than being written in two places.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NX2SsmUqQftWTZAY4BDuFa
This commit is contained in:
Severian
2026-09-11 05:07:19 +08:00
parent ec2a1c672c
commit b479460804
5 changed files with 155 additions and 43 deletions
+5 -5
View File
@@ -121,16 +121,16 @@ function removePersonaTags(content: string): string {
function extractCardData(messages: Message[]): ExtractedCard {
const { system, greeting, nameSlot } = selectMessageSlots(messages);
if (!system?.content) {
if (!system) {
throw new Error("No system message: this request carries no character card.");
}
if (!greeting?.content) {
if (!greeting) {
throw new Error("No assistant message: this chat has no greeting to read.");
}
const first_mes = greeting.content.replace(/{user}/g, "{{user}}");
const first_mes = greeting.replace(/{user}/g, "{{user}}");
const nameContent = nameSlot?.content ?? "";
const nameContent = nameSlot ?? "";
// If the name slot is actually a token, ignore it for naming purposes
const tokenInNameSlot = parseConversationToken(nameContent);
const lastColonIndex = nameContent.lastIndexOf(": ");
@@ -140,7 +140,7 @@ function extractCardData(messages: Message[]): ExtractedCard {
? nameContent.substring(lastColonIndex + 2).trim()
: "";
let content = system.content.replace(/{user}/g, "{{user}}");
let content = system.replace(/{user}/g, "{{user}}");
const inferredName = extractPersonaName(content);
content = removePersonaTags(content);