diff --git a/ui-tui/src/__tests__/activeSessionSwitcher.test.ts b/ui-tui/src/__tests__/activeSessionSwitcher.test.ts index bf409e95b354..d598f6a834c6 100644 --- a/ui-tui/src/__tests__/activeSessionSwitcher.test.ts +++ b/ui-tui/src/__tests__/activeSessionSwitcher.test.ts @@ -86,10 +86,10 @@ describe('session orchestrator helpers', () => { it('turns model picker values into session-scoped draft model args', () => { expect(draftModelArgFromPickerValue('kimi-k2.6 --provider ollama-cloud --tui-session')).toBe( - 'kimi-k2.6 --provider ollama-cloud' + 'kimi-k2.6 --provider ollama-cloud --session' ) expect(draftModelArgFromPickerValue('openai/gpt-5.5 --provider openai-codex --global')).toBe( - 'openai/gpt-5.5 --provider openai-codex' + 'openai/gpt-5.5 --provider openai-codex --session' ) }) diff --git a/ui-tui/src/__tests__/createSlashHandler.test.ts b/ui-tui/src/__tests__/createSlashHandler.test.ts index ca1af4cd9abe..e5f97c7e3562 100644 --- a/ui-tui/src/__tests__/createSlashHandler.test.ts +++ b/ui-tui/src/__tests__/createSlashHandler.test.ts @@ -209,7 +209,7 @@ describe('createSlashHandler', () => { confirm_expensive_model: false, key: 'model', session_id: 'sid-abc', - value: 'anthropic/claude-sonnet-4.6 --provider openrouter' + value: 'anthropic/claude-sonnet-4.6 --provider openrouter --session' }) }) diff --git a/ui-tui/src/__tests__/orchestratorPromptSession.test.ts b/ui-tui/src/__tests__/orchestratorPromptSession.test.ts index f9ff16f34a5c..2d670c4a5b8d 100644 --- a/ui-tui/src/__tests__/orchestratorPromptSession.test.ts +++ b/ui-tui/src/__tests__/orchestratorPromptSession.test.ts @@ -32,7 +32,7 @@ describe('startPromptLiveSession', () => { 'rpc', { method: 'config.set', - params: { key: 'model', session_id: 'abc123', value: 'kimi-k2.6 --provider ollama-cloud' } + params: { key: 'model', session_id: 'abc123', value: 'kimi-k2.6 --provider ollama-cloud --session' } } ], ['sys', 'model → kimi-k2.6'], diff --git a/ui-tui/src/app/slash/commands/session.ts b/ui-tui/src/app/slash/commands/session.ts index 6b1fe55481bc..ce1c4ead945d 100644 --- a/ui-tui/src/app/slash/commands/session.ts +++ b/ui-tui/src/app/slash/commands/session.ts @@ -1,5 +1,5 @@ import { attachedImageNotice, introMsg, toTranscriptMessages } from '../../../domain/messages.js' -import { TUI_SESSION_MODEL_FLAG } from '../../../domain/slash.js' +import { sessionScopedModelArg, TUI_SESSION_MODEL_FLAG } from '../../../domain/slash.js' import type { BackgroundStartResponse, ConfigGetValueResponse, @@ -20,10 +20,6 @@ import { patchUiState } from '../../uiStore.js' import type { SlashCommand } from '../types.js' const TUI_SESSION_MODEL_RE = new RegExp(`(?:^|\\s)${TUI_SESSION_MODEL_FLAG}(?:\\s|$)`) -const TUI_SESSION_STRIP_RE = new RegExp(`\\s*${TUI_SESSION_MODEL_FLAG}\\b\\s*`, 'g') - -const stripTuiSessionFlag = (trimmed: string) => trimmed.replace(TUI_SESSION_STRIP_RE, ' ').replace(/\s+/g, ' ').trim() - const modelValueForConfigSet = (arg: string) => { const trimmed = arg.trim() @@ -32,7 +28,7 @@ const modelValueForConfigSet = (arg: string) => { } if (TUI_SESSION_MODEL_RE.test(trimmed)) { - return stripTuiSessionFlag(trimmed) + return sessionScopedModelArg(trimmed) } return trimmed diff --git a/ui-tui/src/app/useMainApp.ts b/ui-tui/src/app/useMainApp.ts index 50a94c9d45a9..761f4a851afb 100644 --- a/ui-tui/src/app/useMainApp.ts +++ b/ui-tui/src/app/useMainApp.ts @@ -9,6 +9,7 @@ import { hasLeadGap, prevRenderedMsg } from '../domain/blockLayout.js' import { SECTION_NAMES, sectionMode } from '../domain/details.js' import { attachedImageNotice, imageTokenMeta } from '../domain/messages.js' import { composeTabTitle, fmtCwdBranch, shortCwd } from '../domain/paths.js' +import { sessionScopedModelArg } from '../domain/slash.js' import { type GatewayClient } from '../gatewayClient.js' import type { ClarifyRespondResponse, @@ -116,7 +117,7 @@ export async function startPromptLiveSession({ return null } - const requestedModel = modelArg?.trim() + const requestedModel = modelArg ? sessionScopedModelArg(modelArg) : '' if (requestedModel) { const result = await rpc('config.set', { key: 'model', session_id: sid, value: requestedModel }) diff --git a/ui-tui/src/components/activeSessionSwitcher.tsx b/ui-tui/src/components/activeSessionSwitcher.tsx index af4fbbb27fb1..4ac3385420c1 100644 --- a/ui-tui/src/components/activeSessionSwitcher.tsx +++ b/ui-tui/src/components/activeSessionSwitcher.tsx @@ -1,7 +1,7 @@ import { Box, Text, useInput, useStdout } from '@hermes/ink' import { useCallback, useEffect, useRef, useState } from 'react' -import { TUI_SESSION_MODEL_FLAG } from '../domain/slash.js' +import { sessionScopedModelArg } from '../domain/slash.js' import type { GatewayClient } from '../gatewayClient.js' import type { SessionActiveItem, @@ -205,18 +205,7 @@ export const closeFallbackAfterClose = ( } export const draftModelArgFromPickerValue = (value: string) => { - const parts = value.trim().split(/\s+/).filter(Boolean) - const kept: string[] = [] - - for (const part of parts) { - if (part === TUI_SESSION_MODEL_FLAG || part === '--global') { - continue - } - - kept.push(part) - } - - return kept.join(' ') + return sessionScopedModelArg(value) } export const draftModelNameFromArg = (value: string) => { diff --git a/ui-tui/src/domain/slash.ts b/ui-tui/src/domain/slash.ts index 42962ae69d4c..b86c34d134cc 100644 --- a/ui-tui/src/domain/slash.ts +++ b/ui-tui/src/domain/slash.ts @@ -1,6 +1,13 @@ -/** Appended to `/model` args from the TUI picker for session scope; stripped in `session` slash before `config.set`. */ +/** Appended by TUI pickers; converted to the backend's `--session` flag before `config.set`. */ export const TUI_SESSION_MODEL_FLAG = '--tui-session' +export const sessionScopedModelArg = (value: string) => { + const parts = value.trim().split(/\s+/).filter(Boolean) + const kept = parts.filter(part => part !== TUI_SESSION_MODEL_FLAG && part !== '--global' && part !== '--session') + + return kept.length ? `${kept.join(' ')} --session` : '' +} + export const looksLikeSlashCommand = (text: string) => /^\/[^\s/]*(?:\s|$)/.test(text) export const parseSlashCommand = (cmd: string) => {