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>
This commit is contained in:
David Kaya
2026-04-02 16:30:32 +02:00
co-authored by Copilot
parent a18803758b
commit 10316a2871
2 changed files with 40 additions and 3 deletions
+10 -3
View File
@@ -2944,9 +2944,16 @@ export class AryxAppService extends EventEmitter<AppServiceEvents> {
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;
+30
View File
@@ -195,6 +195,30 @@ export function ChatPane({
const promptFiles = useMemo(() => project.customization?.promptFiles ?? [], [project.customization?.promptFiles]);
const [armedPrompt, setArmedPrompt] = useState<ArmedPrompt | null>(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<string, string>();
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}
</span>
)}
{modelOverride && (
<span className="inline-flex items-center gap-1 rounded-full bg-[var(--color-accent-purple)]/10 px-2 py-0.5 text-[10px] font-medium text-[var(--color-accent-purple)]">
{modelOverride}
</span>
)}
{showActions && (
<div className="ml-auto">
<MessageActions