fix(tui): preserve picker session scope across all paths

Fold in the TUI direction from #61192 and cover the remaining new-live-session picker path with one shared session-argument normalizer.

Co-authored-by: DatTheMaster <hermesagent424@gmail.com>
This commit is contained in:
teknium1 2026-07-11 07:28:14 -07:00 committed by Teknium
parent ce5c1f9f79
commit aac77f1686
7 changed files with 18 additions and 25 deletions

View file

@ -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'
)
})

View file

@ -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'
})
})

View file

@ -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'],

View file

@ -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

View file

@ -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<ConfigSetResponse>('config.set', { key: 'model', session_id: sid, value: requestedModel })

View file

@ -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) => {

View file

@ -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) => {