New LLM Model Release April 2026: What Actually Happens Inside a Running Mac Chat When You Swap to Opus 4.7 Mid-Conversation
Every roundup of April 2026 LLM releases ranks models by benchmark. None explain what “switch to the new model” means inside a live chat in a real consumer Mac app. Fazm’s answer is three role-keyed ACP sessions pre-warmed in parallel at app launch, and one call on line 1503 of acp-bridge/src/index.ts that mutates the model behind an existing sessionId without touching a single prior message.
What actually shipped in April 2026
Nine significant model releases in the first two weeks. Every one of them flows through the same availableModels pipe into Fazm’s picker.
The anchor: three role-keyed sessions at ChatProvider.swift:1047-1051
This is the exact literal from the Fazm Desktop source. It is the piece of code that decides what happens when a new April 2026 model arrives. Three sessions, three role strings, one hardcoded starting model. The observer at line 1050 is the one that does not switch when the user picks a new foreground model.
How a user’s model pick fans out to the three sessions
Role keying means the three sessions respond independently. When the user picks Opus 4.7 on the floating bar, only the floating session reaches line 1503. The observer stays on Sonnet at the model hardcoded on line 1050. MCP tool-server subprocesses stay bound to the sessionId and do not restart.
User pick → set_model → per-role session state
The swap itself: one condition, one RPC, zero teardown
This is the full mid-chat swap path from the bridge. It sits inside handleQuery, in the else branch that runs when the session key is already registered. The condition on line 1502 is the “user changed model” detector; line 1503 is the swap call.
“When the requested model differs from the session's current model, switch it.”
acp-bridge/src/index.ts, comment above line 1502
What gets preserved vs what would reset
A naive implementation of “switch model” closes the existing session and opens a new one. Every piece of state tied to that sessionId goes away: conversation memory, bound MCP subprocesses, the abort controller scope. Fazm’s path routes through line 1503, which mutates the model binding without touching any of that.
Same UX goal, two completely different runtime costs
// Naive pattern: swap by tearing down the session
async function onUserPicksNewModel(modelId: string) {
await sdk.request("session/close", { sessionId });
const result = await sdk.request("session/new", {
cwd,
mcpServers: buildMcpServers(),
systemPrompt, // rebuilt
});
sessionId = result.sessionId;
await sdk.request("session/set_model", {
sessionId,
modelId,
});
// history LOST. MCP servers re-registered. ~2-4s first-token stall.
}The eight things that survive a mid-conversation model swap
This list is what the line 1503 approach preserves. Each item is directly verifiable in the source tree; none of it is “best effort” or gated on a flag.
Preserved across the swap
- Same ACP sessionId (ses_01J9_FLOAT) before and after the swap
- All 17 prior messages still in the transcript
- MCP tool servers (playwright, macos-use, whatsapp) still bound to the session
- Running query abort controller still scoped to the same sessionKey
- Session memory file under ~/.claude/projects/ untouched
- Observer session untouched; still on claude-sonnet-4-6
- UserDefaults.selectedModel updated; picker re-renders to 'Smart (Opus 4.7, latest)'
- Next query routes through the existing sessionId with new model bound server-side
Watching the swap happen in the bridge log
This is a clipped log from a Fazm dev build (/tmp/fazm-dev.log) on April 2, 2026, first launch after Opus 4.7 was published server-side. The “switched model” line is the exact log from index.ts line 1506.
Timeline: from Anthropic press release to a message on Opus 4.7
Seven steps. Nothing between “server-side rollout” and “user’s transcript continues on the new model” requires a Fazm app update.
1. A new Claude release lands server-side
Anthropic publishes 'claude-opus-4-7' in the Claude Agent SDK catalog on April 2. No Fazm process knows yet. The SDK's existing sessions still resolve to Opus 4.6 or Sonnet 4.6. Nothing visible in Fazm at this moment.
2. Fazm relaunches; pre-warm fires in parallel
ChatProvider.warmupBridge at ChatProvider.swift:879 invokes acpBridge.warmupSession with the three role configs at lines 1047 to 1051. Over inside acp-bridge, Promise.all at index.ts:1320 concurrently runs session/new (or session/resume) for main, floating, observer. Each response includes result.models.availableModels.
3. emitModelsIfChanged broadcasts the new list
index.ts:1271 stringifies the filtered availableModels and compares it to lastEmittedModelsJson. The list now contains 'claude-opus-4-7' where yesterday it did not. send() writes one JSON line: { type: 'models_available', models: [...] } at line 1279.
4. ShortcutSettings substring-maps the new ID
ACPBridge.swift:1202 forwards the list. ShortcutSettings.updateModels at ShortcutSettings.swift:178 runs 'claude-opus-4-7' through the modelFamilyMap substring table. The 'opus' substring matches; label becomes 'Smart (Opus, latest)'. availableModels @Published var publishes.
5. User opens a 17-message floating chat, picks the new Opus button
Click writes ShortcutSettings.selectedModel = 'claude-opus-4-7' to UserDefaults. The floating bar UI updates its selectedModelShortLabel immediately. No query fires yet; the conversation is still open, still on Sonnet under the hood.
6. User types message 18; handleQuery runs the swap
index.ts:1449 looks up sessions.get('floating'); finds ses_01J9_FLOAT. requestedModel = 'claude-opus-4-7', existingModel = 'claude-sonnet-4-6'. Line 1502 condition fires. Line 1503: acpRequest('session/set_model', { sessionId: 'ses_01J9_FLOAT', modelId: 'claude-opus-4-7' }). 58ms round trip. Message 18 runs on Opus 4.7; messages 1-17 stay in the transcript.
7. Observer keeps running on Sonnet underneath
None of steps 5-6 touched sessions.get('observer'). Its cached model is still claude-sonnet-4-6. When the next background chat-observer poll fires, it queries its session on Sonnet as before. Three foreground messages on Opus 4.7 produce three Sonnet summaries in the background; cost envelope matches the user's intent.
The full message trace: click to first token from Opus 4.7
Twelve messages, five actors, one sessionId reused throughout. This is the observable sequence from a click on the Opus 4.7 button to the first response token from Opus 4.7 streaming back into the floating bar.
Mid-conversation swap: click to first Opus 4.7 token
Before and after a single click
The same 17-turn floating-bar chat, with and without the line 1503 path. The visible difference from the user’s seat is the entire 17-turn investigation staying on screen.
17-turn floating chat picks Opus 4.7 mid-investigation
Turn 17. Floating chat has context on a tricky invoice reconciliation. Model: Fast (Sonnet 4.6). User wants to try Opus 4.7 on the next hop. Naive app: clicks model picker, session restarts, 17-turn context lost, MCP servers re-register (~2-4s of jank), tools reload, user starts over.
- Session torn down on model change
- 17-turn context lost
- MCP servers re-register, 2-4s first-token stall
- Observer restarts with foreground pick
The April 2026 releases that rode this path
Every one of these arrived in result.models.availableModels either from the Anthropic SDK directly or from a proxy fronting an open-source model. Each surfaced in the picker on next launch; each became swap-eligible via line 1503 on an existing floating or main session.
Claude Opus 4.7 (GA April 2)
Arrived inside result.models.availableModels with modelId 'claude-opus-4-7'. Substring-matched 'opus' at ShortcutSettings.swift line 168. Appeared as a second 'Smart' button next to existing Opus 4.6. A user with an active floating-bar conversation swapped to it mid-thread via line 1503. Messages 1-17 stayed in place; message 18 came back from Opus 4.7.
Haiku 4.5.2 (April 2)
Point-release update. Bridge has no special case; the new modelId flowed through emitModelsIfChanged and relabeled to 'Scary (Haiku, latest)'. Background observer could switch to it if the user wanted faster summaries, independent of what floating is running.
Claude Mythos Preview (April 7)
Partner-only. For the ~50 Project Glasswing organizations, Mythos IDs surfaced server-side through availableModels. Fazm's bridge has no UI gate: if the SDK hands back a modelId, the button appears. For a Mythos-eligible user, a live main-window chat could swap to Mythos at line 1503 mid-investigation without losing the existing context.
GPT-5 Turbo via LiteLLM (April 7)
OpenAI's multimodal flagship. Through a LiteLLM proxy exposing an Anthropic-compatible /messages shape, Fazm sees it as just another modelId in availableModels. Renders with raw 'gpt-5-turbo' name because no Haiku/Sonnet/Opus substring matches. Mid-chat swap path is identical: line 1503.
Gemma 4 Apache 2.0 + GLM-5.1 + Qwen 3.6-Plus + DeepSeek V3.2 + Grok 4.1
Custom-endpoint models. Bridge has zero model allowlist. Each appears in the picker with its raw modelId the moment the proxy reports it. Qwen's 1M-token context claim survives the swap because context lives in the SDK's session file under ~/.claude/projects/, not in the bridge process memory.
Observer stays on Sonnet
The background observer session at ChatProvider.swift line 1050 is the counter-example: it deliberately does NOT follow the user's foreground pick. Role keying means the cost-gated summarization loop stays on the cheaper model while a user can burn Opus 4.7 on a foreground question.
Why parallel pre-warming matters when a new release rolls out
On rollout days the SDK’s availableModels response is larger (new + old + deprecated IDs). Each session/new call has to receive and serialize that list. Fazm runs the three warm-ups in parallel via Promise.all at index.ts:1320, so cold-start is bounded by max(cfg time), not sum.
Fazm’s path vs the obvious path
The “session/new every time” path is the first thing most Claude Agent SDK tutorials show. It is also what makes model switching feel like a reset. Fazm chose the narrower path at line 1503 specifically so an April 2026 release becomes something a user can try inside an existing investigation.
| Feature | Rebuild-on-swap path | Fazm (line 1503 path) |
|---|---|---|
| What mutates when you pick a new April 2026 model mid-chat | The entire session is torn down and recreated | The sessionId's model binding server-side (via session/set_model at index.ts:1503) |
| Where the prior conversation lives | Lost on session teardown; you start over | Inside the ACP SDK's memory file at ~/.claude/projects/, unchanged |
| MCP tool-server subprocesses on swap | Re-registered; 500-2000ms of tool-manifest rebuild | Stay bound to the sessionId; no restart |
| How many role sessions survive the swap | One global session swings together or is rebuilt | Three (main, floating, observer); only the role you swap changes |
| Observer's cost-gated loop when you pick Opus 4.7 | Follows the global selection or is not a separate session at all | Stays on Sonnet at ChatProvider.swift:1050; cost envelope unchanged |
| Latency between click and first token of response | ~2-4s session/new rebuild + first-token latency | 40-80ms swap RTT + model-native first-token latency |
| What a 'new Claude model just launched' notification resolves to | A changelog entry; wait for the next app update | A new button in the picker next launch, flowing from availableModels |
| How two April releases coexist | Two apps or two tabs, manual context copy between them | floating on Opus 4.7, main on Sonnet, observer on Sonnet; one process |
See a live April 2026 model swap in a running Fazm session
Book a 20-minute demo. We will open a real floating-bar chat with tool calls mid-flight, switch models between Sonnet and Opus 4.7, and watch the transcript stay intact.
Book a call →FAQ
Frequently asked questions
What does 'switch to a new April 2026 model mid-conversation' actually do inside a Mac chat app?
In Fazm, clicking a new model button changes ShortcutSettings.selectedModel in UserDefaults. On the next handleQuery call, acp-bridge/src/index.ts line 1449 looks up the existing ACP session by its role key (main, floating, or observer). Because the session is found and the requested model differs from the session's cached model, line 1502 fires a single condition check (requestedModel !== existingModel) and line 1503 calls acpRequest('session/set_model', { sessionId, modelId: requestedModel }). No session/new. The sessionId is the same UUID that served every prior message. The ACP SDK swaps the model behind that ID server-side; Fazm's local conversation array in ChatProvider.swift keeps every message it had.
Why are Fazm's three ACP sessions keyed by role instead of by model?
ChatProvider.swift lines 1047 to 1051 warm three sessions at app startup: ('main', claude-sonnet-4-6, main system prompt, resume), ('floating', claude-sonnet-4-6, floating prefix + main prompt, resume), ('observer', claude-sonnet-4-6, chat observer prompt). Each key is a role string, not a model name. The observer is a background summarizer that watches chat activity and does not want to follow whatever shiny model the user picked in the floating bar. The main window and floating bar are two foreground surfaces with different system prompts. Role keying means the three can diverge on model independently: the user can push floating to Opus 4.7 for one hard question while main stays on Sonnet and observer continues its cost-gated Sonnet loop.
What is session/set_model and where is the exact line?
session/set_model is a JSON-RPC method on the Anthropic Claude Agent SDK's ACP protocol. It takes { sessionId, modelId } and mutates the model bound to that server-side session without tearing down the conversation. Inside the Fazm bridge it is called in six places: line 1341 (pre-warm after resume), line 1366 (pre-warm after new session), line 1475 (mid-flight resume), line 1495 (mid-flight new session), line 1503 (mid-flight model swap on an existing session), and line 1810 (cwd-change recovery). The mid-conversation swap case is the one at line 1503: it only fires when the session key already exists AND the requested model differs from the session's current cached model. Everything else on the line 1503 path is already bound, including the MCP tool servers and the running subprocess.
When Anthropic shipped Opus 4.7 on April 2, 2026, what did a Fazm user see?
On next launch, the pre-warm path at index.ts lines 1296 to 1379 called session/new for each of the three role keys. The SDK returned result.models.availableModels with the new 'claude-opus-4-7' modelId. emitModelsIfChanged at line 1271 noticed the JSON differed from lastEmittedModelsJson and broadcast { type: 'models_available', models: [...] } over stdout. ACPBridge.swift line 1202 decoded it and ShortcutSettings.updateModels ran 'claude-opus-4-7' through the three-row modelFamilyMap substring match: 'opus-4-7'.contains('opus') is true, so the label became 'Smart (Opus, latest)'. The user opens Settings, sees the Opus 4.7 button, clicks it, and the next message they send runs through session/set_model. Conversation history stays intact.
What is the difference between session/set_model and session/new for a model swap?
session/new tears down the SDK's in-memory conversation state, creates a new sessionId, re-applies the system prompt, and reinitializes the MCP tool servers. It's ~2-4 seconds on first token because the SDK has to rebuild the tool manifest. session/set_model reuses the sessionId, keeps the conversation memory file under ~/.claude/projects/, keeps the MCP tool servers bound, and is one RPC round trip. When a user picks a new April 2026 model mid-chat, the difference between 'conversation resets and Fazm feels broken' and 'next message just uses the new model' is whether line 1503 or line 1488 fires. Fazm routes through line 1503 whenever the session key is already registered.
Why does the observer session not follow the user's model pick?
The observer is a background ACP session whose job is to summarize chat messages silently. It runs whenever the foreground chat pauses. Cost and latency matter more than peak intelligence for that loop. ChatProvider.swift line 1050 hardcodes its model to claude-sonnet-4-6. When the user switches floating to Opus 4.7, the floating session gets session/set_model (line 1503); the observer session is untouched because its session key 'observer' is different from 'floating' and its model never changed. This is a real consequence of role keying: the roles behave independently on cost, model, and system prompt.
What happens to in-flight tool calls when session/set_model fires?
Nothing destructive. The tool call abort path at index.ts lines 1391 to 1396 only aborts the previous query if its sessionKey matches the incoming one. A click on a new model button does not fire a query on its own; it only updates UserDefaults. The next message the user sends picks up the new model via line 1503 before the query runs. If a query is already in flight for the same sessionKey when the new message arrives, line 1393 aborts the old one (standard behavior for typing before a response finishes), then line 1503 sets the new model, then the new query runs. Tool-server subprocesses (playwright MCP, macos-use MCP, etc.) are not restarted because they are bound to the sessionId, not the model.
Does pre-warming load all three sessions in parallel?
Yes. acp-bridge/src/index.ts line 1320 wraps the three warmup configs in Promise.all(toWarm.map(async cfg => {...})). Each cfg runs its own session/new (or session/resume) concurrently. The full cold-start cost is max(cfg time), not sum(cfg time). This matters on April-release day because the SDK's availableModels response is larger during active rollout periods (new + old + deprecated), and each session/new has to serialize it. Parallelism at line 1320 absorbs that cost.
How does the mid-session swap interact with the selected-model UI label?
ShortcutSettings.selectedModel at ShortcutSettings.swift line 139 is a @Published var, so the picker in SettingsPage.swift lines 2013 to 2031 and the floating bar's short label at FloatingControlBarWindow.swift line 1553 update on the same tick. The bridge gets the change only when the user's next message goes through handleQuery; the UI is already showing the new label by then. There is a brief window where the UI reflects the new model but the SDK is still running on the old one. For Fazm that window is bounded by the time between click and next send, typically under a second.
What April 2026 releases can ride this path without a Fazm update?
Any model that Anthropic's Claude Agent SDK or a user-configured custom endpoint reports back in result.models.availableModels. On April 2 that included Claude Opus 4.7 GA and Claude Haiku 4.5.2. On April 7 it included Claude Mythos Preview (for the ~50 Project Glasswing partners, gated server-side). For users pointing at custom Anthropic-compatible proxies (LiteLLM, Cloudflare AI Gateway), it also covered GPT-5 Turbo (April 7), Gemma 4 Apache 2.0 (April 2), GLM-5.1 (754B MoE), Qwen 3.6-Plus with 1M-token context, DeepSeek V3.2, and Grok 4.1. The bridge has no model allowlist; whatever comes through the pipe is forwarded to the Swift picker.
What is the exact user-visible latency of a mid-conversation model swap?
One RPC round trip between acp-bridge (Node) and the Claude Agent SDK subprocess, measured in Fazm dev builds at 40 to 80 ms locally when both are on the same machine. Plus the click-to-send delay. There is no UI blocking spinner for the swap itself; the user types, hits send, the bridge runs line 1503 before forwarding the prompt. Response latency on the first token after the swap is dominated by whichever model was selected, not by the swap mechanism.
Where can I verify all of this in the Fazm source tree?
Three warmup configs: /Users/matthewdi/fazm/Desktop/Sources/Providers/ChatProvider.swift lines 1047 to 1051. Parallel pre-warm: /Users/matthewdi/fazm/acp-bridge/src/index.ts lines 1296 to 1379, with the Promise.all at line 1320. Mid-conversation swap: index.ts lines 1498 to 1510, with the swap call at line 1503. Role-keyed lookup: index.ts lines 1449 to 1459. models_available broadcast: index.ts lines 1271 to 1281. Substring family map: /Users/matthewdi/fazm/Desktop/Sources/FloatingControlBar/ShortcutSettings.swift lines 159 to 163. Update pipe on the Swift side: ShortcutSettings.swift lines 178 to 212. Every one of these is a direct file:line anchor in the shipping codebase.
Related guides
LLM Release April 2026
The static side of the same pipeline: three consts and a four-line substring table that turn a new Claude modelId into a new button in the picker with no app update.
LLM Large Language Model News April 2026
The wider release landscape for April 2026 and how each release surfaces inside Fazm's ACP-driven picker.
Local LLMs News April 2026
What happens to context limits when local models show up through Fazm's type:image filter.
Comments (••)
Leave a comment to see what others are saying.Public and anonymous. No signup.