From 10316a2871739b7dc4b621814afdcdc65cefad93 Mon Sep 17 00:00:00 2001 From: David Kaya Date: Thu, 2 Apr 2026 16:21:40 +0200 Subject: [PATCH] fix: strip reasoning effort on prompt model override, show actual model badge When a prompt file overrides the turn model, the original agent's reasoning effort was carried through even when the target model does not support it. This caused session.create failures for models like Claude Opus 4.5 which reject reasoning effort configuration. Now: if the target model's reasoning capabilities are unknown (supportedReasoningEfforts undefined) or it's an unresolved model reference, reasoning effort is stripped entirely. Also adds a model override badge on assistant messages when the run used a different model than the session's primary agent. The badge reads the actual model from the run record (SessionRunRecord.agents) rather than inferring from the prompt invocation metadata. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/main/AryxAppService.ts | 13 +++++++++--- src/renderer/components/ChatPane.tsx | 30 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/main/AryxAppService.ts b/src/main/AryxAppService.ts index cd489ed..ded71d3 100644 --- a/src/main/AryxAppService.ts +++ b/src/main/AryxAppService.ts @@ -2944,9 +2944,16 @@ export class AryxAppService extends EventEmitter { let didChange = false; const agents = pattern.agents.map((agent) => { - const reasoningEffort = resolvedModel - ? resolveReasoningEffort(resolvedModel, agent.reasoningEffort) - : agent.reasoningEffort; + // When overriding the model, re-normalize reasoning effort for the target model. + // If the target model's reasoning capabilities are unknown (supportedReasoningEfforts + // is undefined — common for dynamically-discovered models), strip reasoning effort + // entirely to avoid sending it to a model that may not support it. + let reasoningEffort: ReasoningEffort | undefined; + if (resolvedModel?.supportedReasoningEfforts) { + reasoningEffort = resolveReasoningEffort(resolvedModel, agent.reasoningEffort); + } else { + reasoningEffort = undefined; + } if (agent.model === effectiveModelId && agent.reasoningEffort === reasoningEffort) { return agent; diff --git a/src/renderer/components/ChatPane.tsx b/src/renderer/components/ChatPane.tsx index 872aa97..6e0b550 100644 --- a/src/renderer/components/ChatPane.tsx +++ b/src/renderer/components/ChatPane.tsx @@ -195,6 +195,30 @@ export function ChatPane({ const promptFiles = useMemo(() => project.customization?.promptFiles ?? [], [project.customization?.promptFiles]); const [armedPrompt, setArmedPrompt] = useState(null); + // Map assistant message IDs to the actual model that executed the run, but only + // when it differs from the session's primary agent model (i.e. a prompt override). + const modelOverrideByMessageId = useMemo(() => { + const runsByTrigger = new Map(session.runs.map((r) => [r.triggerMessageId, r])); + const map = new Map(); + let activeOverrideModel: string | undefined; + + for (const message of session.messages) { + if (message.role === 'user') { + const run = runsByTrigger.get(message.id); + const runModel = run?.agents[0]?.model; + if (runModel && runModel !== primaryAgent?.model) { + const resolved = findModel(runModel, availableModels); + activeOverrideModel = resolved?.name ?? runModel; + } else { + activeOverrideModel = undefined; + } + } else if (activeOverrideModel && message.messageKind !== 'thinking') { + map.set(message.id, activeOverrideModel); + } + } + return map; + }, [session.messages, session.runs, primaryAgent?.model, availableModels]); + const toolSelection = useMemo(() => resolveSessionToolingSelection(session), [session]); const mcpServers = toolingSettings.mcpServers; const lspProfiles = toolingSettings.lspProfiles; @@ -469,6 +493,7 @@ export function ChatPane({ : 'border-[var(--color-status-success)]/20 bg-[var(--color-status-success)]/10 text-[var(--color-status-success)]'; const phaseLabel = phase === 'thinking' ? 'Thinking' : phase === 'final' ? 'Final' : undefined; + const modelOverride = !isUser ? modelOverrideByMessageId.get(message.id) : undefined; const showActions = !isSessionBusy && !message.pending; return ( @@ -495,6 +520,11 @@ export function ChatPane({ {phaseLabel} )} + {modelOverride && ( + + {modelOverride} + + )} {showActions && (