per-session models: every session owns its model + thinking level from birth
The reported bug (2026-09-28): every chat ran glm-5.3-flash regardless of any pick, while composer and settings UIs agreed with each other and lied about the run. Root cause: a fossil default-settings record (settings.json chat section, frozen pre-slots-wave) was stamped verbatim into every new chat at birth by the new-session flow, and resolveConversationSpec prefers the per-chat record over the strong slot forever. Owner redesign — models are per session, always; the strong slot is the new-session default: - POST /api/chats stamps the newborn verbatim from the strong slot (model + thinking level); no client settings PUT on create anymore - the composer control is dual-bound: in-session it reads/writes the session's own record (GET /api/chats/:id, PUT .../settings); on the draft surfaces it binds the strong slot (picking there = changing the default) - thinking levels follow the identical mechanics (confirmed by owner) - the zombie default-settings system dies whole: useDefaultSettings, /api/settings/defaults, src/store/default-settings.ts, the chat section of UserSettings (sanitizer strips fossil copies), the legacy default-settings.json fold, and DEFAULT_SETTINGS_FILE - catalogDefaultsFor collapses to the strong slot alone - thinking display for a legacy unstamped session resolves exactly like the engine (session ?? strong ?? model ladder) Tests: new chat-birth-stamp suite pins the stamp, the unconfigured boot, the unset level, and the copy-not-live-view regression; defaults pins rewritten strong-only; legacy-migration suite deleted.
This commit is contained in:
+6
-11
@@ -333,9 +333,6 @@ export interface GraphSettings {
|
||||
export interface UserSettings {
|
||||
version: number;
|
||||
general?: GeneralSettings;
|
||||
/** Today's default-settings.json content — the new-session surfaces'
|
||||
* controls. NOT shown in the settings panel (owner decision). */
|
||||
chat?: ChatSettings;
|
||||
agent?: AgentSettings;
|
||||
/** The strong/cheap/vision model slots (model slots wave, 2026-09-20). */
|
||||
models?: ModelsSettings;
|
||||
@@ -570,13 +567,6 @@ export function sanitizeUserSettings(raw: unknown): UserSettings | null {
|
||||
if (r.general !== null && typeof r.general === "object" && !Array.isArray(r.general)) {
|
||||
out.general = sanitizeGeneral(r.general as Record<string, unknown>);
|
||||
}
|
||||
if (r.chat !== null && typeof r.chat === "object" && !Array.isArray(r.chat)) {
|
||||
// The chat section is ChatSettings — its fields are plain and the
|
||||
// section is written exclusively through the validated route
|
||||
// (parseSettingsBody + validateChatSettings), so pass through as-is;
|
||||
// a hand-corrupted value fails loudly downstream, same as today.
|
||||
out.chat = r.chat as ChatSettings;
|
||||
}
|
||||
if (r.agent !== null && typeof r.agent === "object" && !Array.isArray(r.agent)) {
|
||||
out.agent = sanitizeAgent(r.agent as Record<string, unknown>);
|
||||
}
|
||||
@@ -590,8 +580,13 @@ export function sanitizeUserSettings(raw: unknown): UserSettings | null {
|
||||
if (r.graph !== null && typeof r.graph === "object" && !Array.isArray(r.graph)) {
|
||||
out.graph = sanitizeGraph(r.graph as Record<string, unknown>);
|
||||
}
|
||||
// Unknown top-level keys ride along untouched (forward-compat).
|
||||
// Unknown top-level keys ride along untouched (forward-compat). One
|
||||
// known-DEAD key is stripped instead of riding: `chat` (the old
|
||||
// default-settings section) died with per-session models (owner,
|
||||
// 2026-09-28) — a fossil copy on disk must never resurrect through
|
||||
// reads (and any later write flushes it from the file for good).
|
||||
for (const [key, value] of Object.entries(r)) {
|
||||
if (key === "chat") continue;
|
||||
if (!(key in out)) out[key] = value;
|
||||
}
|
||||
return out;
|
||||
|
||||
Reference in New Issue
Block a user