Repository navigation
fix(native-chat): a resting chat lists its model the way the running agent does, so no stray effort picker appears #24267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fa0ff8d
2cedb9d
c8e532e
c7d6b22
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // The models a session's options answer lists, the same whether a child answers live or the host | ||
| // answers for a chat at rest: the catalog's rows, plus the current model when the catalog does not | ||
| // list it. Nothing says which efforts an unlisted model takes, so its row offers none, and a client | ||
| // shows no effort control for it. | ||
|
|
||
| import type { AgentSessionModelOption } from '../../../shared/agent-session-wire' | ||
|
|
||
| /** `row` builds a provider's own row type from the shared one. */ | ||
| export function structuredAgentSessionOptionModels<TModel extends AgentSessionModelOption>( | ||
| catalog: readonly TModel[], | ||
| current: string | undefined, | ||
| row: (unlisted: AgentSessionModelOption) => TModel | ||
| ): TModel[] { | ||
| const models = [...catalog] | ||
| if (current && !models.some((entry) => entry.id === current)) { | ||
| models.push(row({ id: current, label: current, isDefault: false, efforts: [] })) | ||
| } | ||
| return models | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,20 +7,33 @@ | |
|
|
||
| import { | ||
| refuse, | ||
| type AgentSessionModelOption, | ||
| type AgentSessionOptionResult, | ||
| type AgentSessionOptionsResult | ||
| } from '../../../shared/agent-session-wire' | ||
| import type { AgentSessionRecord } from '../../../shared/agent-session-record' | ||
| import { claudeFallbackModelOptions } from '../../claude/claude-structured-session-options' | ||
| import { decodeStructuredAgentSessionOptionValue } from '../../../shared/structured-agent-session-option-codec' | ||
| import type { AgentSessionRecordStore } from '../../runtime/agent-session-record-store' | ||
| import { journalOpenReadRefusal } from '../agent-session-journal/journal-open-failure' | ||
| import { isClaudeStructuredOptionKey } from '../../claude/claude-structured-options' | ||
| import { isCodexTurnOptionKey } from '../../codex/codex-structured-turn-start' | ||
| import type { StructuredAgentSessionHostDeps } from './structured-agent-session-host-types' | ||
| import { structuredAgentSessionOptionModels } from './structured-agent-session-option-models' | ||
| import type { AgentSessionTurnContext, TurnOutcome } from './structured-agent-session-turns' | ||
| import type { StructuredAgentSessionMutationContext } from './structured-agent-session-host-mutations' | ||
|
|
||
| type RestingOptions = Pick<AgentSessionOptionsResult, 'models' | 'fastModeSupport' | 'current'> | ||
|
|
||
| /** With no catalog for the account, the list a running child falls back to: Claude's built-in | ||
| * models. A Codex child has no such list, so none: the client fills the current model from its | ||
| * own unknown-model defaults, unchanged from before this list was shared. */ | ||
| function restingFallbackModels( | ||
| provider: AgentSessionRecord['provider'] | ||
| ): AgentSessionModelOption[] | null { | ||
| return provider === 'claude' ? claudeFallbackModelOptions() : null | ||
| } | ||
|
|
||
| async function readStructuredAgentSessionOptionsAtRest( | ||
| deps: Pick<StructuredAgentSessionHostDeps, 'store' | 'modelCatalog'>, | ||
| sessionId: string | ||
|
|
@@ -32,14 +45,20 @@ async function readStructuredAgentSessionOptionsAtRest( | |
| const catalog = (await deps.modelCatalog | ||
| ?.read({ agent: record.provider, sessionId }) | ||
| .catch(() => null)) ?? { origin: 'unknown' as const } | ||
| const models = catalog.origin === 'unknown' ? [] : catalog.models | ||
| const listed = | ||
| catalog.origin === 'unknown' ? restingFallbackModels(record.provider) : catalog.models | ||
|
Comment on lines
+48
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Knowledge Base Used: Agent workspaces and sessions |
||
| const models = listed ?? [] | ||
| const saved = record.options ?? {} | ||
| const fastMode = | ||
| saved.fastMode === undefined | ||
| ? null | ||
| : decodeStructuredAgentSessionOptionValue('fastMode', saved.fastMode) | ||
| // An unknown model is one the client already treats as unconfirmed. | ||
| const model = saved.model ?? models.find((entry) => entry.isDefault)?.id ?? '' | ||
| // An unknown model is one the client already treats as unconfirmed. Only a real listing names the | ||
| // account's default; a built-in list's default is a guess, so with none the client keeps its own. | ||
| const model = | ||
| saved.model ?? | ||
| (catalog.origin === 'unknown' ? undefined : models.find((entry) => entry.isDefault)?.id) ?? | ||
| '' | ||
| // As a live child answers: the pick, else what Claude runs for this model when none is sent. | ||
| // A live Codex child answers only the effort its thread reported, never the model's default. | ||
| const effort = | ||
|
|
@@ -48,7 +67,7 @@ async function readStructuredAgentSessionOptionsAtRest( | |
| ? models.find((entry) => entry.id === model)?.defaultEffort | ||
| : undefined) | ||
| return { | ||
| models, | ||
| models: listed ? structuredAgentSessionOptionModels(listed, model, (row) => row) : [], | ||
| ...(catalog.origin !== 'unknown' && catalog.fastModeSupport | ||
| ? { fastModeSupport: catalog.fastModeSupport } | ||
| : {}), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
claudeFallbackModelOptions()keepsisDefault: trueon the seed's default row. The at-rest answer sends this list withcurrent.model: '', and the client'sstructuredAgentSessionOptionCatalogunconditionally setsdefaultModelIsCliDefault: true, so the picker names Sonnet even though the host named no model — exactly the guess the comment above says to avoid. Before this PR the no-catalog list was empty, so nothing was named, and a running agent always reports its real model so it never falls back to the seed default either.Technical details