f09c56c003
Each mid-session `/model` switch appended a `[System: The active model for this chat has changed to …]` user-role marker to the live conversation history and never removed the prior one. N switches left N stale markers, all re-sent to the provider on every subsequent turn — the issue measured ~80-120 tokens each (Chinese+English), so 5 MoA-preset switches burned ~400-600 context tokens per API call, permanently. Only the newest marker is meaningful (it names the currently-active model); the rest are pure waste. Fixes #65891. `_append_model_switch_marker` now strips any earlier markers from `session["history"]` (in place, under the existing history_lock) before appending the new one, so the live payload carries exactly one marker. This is the payload re-sent each turn, and it's self-healing across resumes: whatever markers a history reload brings back, the next switch collapses to one. A stable `_MODEL_SWITCH_MARKER_PREFIX` constant is shared by the builder and the `_is_model_switch_marker` predicate so the two can't drift; the marker string itself is byte-for-byte unchanged. Existing behavior (role='user' per the single db.append_message persistence) is preserved. Scope note: this dedups the in-memory history (the per-turn cost the issue quantifies). The DB still persists one row per switch for audit/resume; I did NOT soft-archive prior marker rows because `active=0, compacted=0` is the repo's rewind/undo sentinel (hermes_state.py get_messages docs), so deactivating a marker naively would make it look rewound — a follow-up that needs its own archive semantics. Tests (tests/tui_gateway/test_model_switch_marker_role.py, +5): second switch replaces (not stacks) the marker; the issue's exact 5-MoA-switch sequence leaves one marker naming the last model; real conversation turns are preserved in order; a stale marker sitting between turns is stripped; history_version increments once per switch. `pytest test_model_switch_marker_role.py` → 13 passed (8 existing unchanged). Authored on Windows. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013b1XyXitAxV7phGmKWigJX