@@ -4,10 +4,10 @@ import {CodexEventHandler} from "./CodexEventHandler";
44import { CodexApprovalHandler } from "./CodexApprovalHandler" ;
55import { CodexElicitationHandler } from "./CodexElicitationHandler" ;
66import { type CodexAuthRequest , getCodexAuthMethods } from "./CodexAuthMethod" ;
7- import { CodexAcpClient , type ModelSelection , type SessionMetadata , type SessionMetadataWithThread } from "./CodexAcpClient" ;
7+ import { CodexAcpClient , type SessionMetadata , type SessionMetadataWithThread } from "./CodexAcpClient" ;
88import type { McpStartupResult } from "./CodexAppServerClient" ;
99import { ACPSessionConnection , type UpdateSessionEvent } from "./ACPSessionConnection" ;
10- import type { InputModality , ReasoningEffort , ServiceTier } from "./app-server" ;
10+ import type { InputModality , ReasoningEffort } from "./app-server" ;
1111import type {
1212 Account ,
1313 CollabAgentToolCallStatus ,
@@ -18,6 +18,7 @@ import type {
1818 UserInput
1919} from "./app-server/v2" ;
2020import type { RateLimitsMap } from "./RateLimitsMap" ;
21+ import { ModelId } from "./ModelId" ;
2122import { AgentMode } from "./AgentMode" ;
2223import type { TokenCount } from "./TokenCount" ;
2324import { toPromptUsage } from "./TokenCount" ;
@@ -35,8 +36,6 @@ import {
3536export interface SessionState {
3637 sessionId : string ,
3738 currentModelId : string ,
38- currentReasoningEffort : ReasoningEffort ,
39- currentServiceTier : ServiceTier | null ,
4039 supportedReasoningEfforts : Array < ReasoningEffortOption > ,
4140 supportedInputModalities : Array < InputModality > ,
4241 agentMode : AgentMode ,
@@ -161,14 +160,12 @@ export class CodexAcpServer implements acp.Agent {
161160 }
162161
163162 const account = await this . getActiveAccount ( ) ;
164- const { sessionId, currentModelId, currentReasoningEffort , currentServiceTier , models} = sessionMetadata ;
163+ const { sessionId, currentModelId, models} = sessionMetadata ;
165164 const sessionMcpServers = this . resolveSessionMcpServers ( requestedMcpServers , "sessionId" in request ) ;
166165 const currentModel = this . findCurrentModel ( models , currentModelId ) ;
167166 const sessionState : SessionState = {
168167 sessionId : sessionId ,
169168 currentModelId : currentModelId ,
170- currentReasoningEffort : currentReasoningEffort ,
171- currentServiceTier : currentServiceTier ,
172169 supportedReasoningEfforts : currentModel ?. supportedReasoningEfforts ?? [ ] ,
173170 supportedInputModalities : currentModel ?. inputModalities ?? [ "text" , "image" ] ,
174171 agentMode : AgentMode . getInitialAgentMode ( ) ,
@@ -192,7 +189,7 @@ export class CodexAcpServer implements acp.Agent {
192189 }
193190
194191 this . publishAvailableCommandsAsync ( sessionId ) ;
195- const sessionModelState : SessionModelState = this . createModelState ( models , sessionState ) ;
192+ const sessionModelState : SessionModelState = this . createModelState ( models , currentModelId ) ;
196193 const sessionModeState : SessionModeState = sessionState . agentMode . toSessionModeState ( ) ;
197194
198195 return [ sessionId , sessionModelState , sessionModeState ] ;
@@ -313,32 +310,34 @@ export class CodexAcpServer implements acp.Agent {
313310 const sessionState = this . sessions . get ( params . sessionId ) ;
314311 if ( ! sessionState ) throw new Error ( `Session ${ params . sessionId } not found` ) ;
315312
313+ const requestedModelId = ModelId . fromString ( params . modelId ) ;
314+ const requestedModelName = requestedModelId . model ;
315+ const requestedEffort = requestedModelId . effort ;
316+
316317 const models = await this . codexAcpClient . fetchAvailableModels ( ) ;
317- const model = models . find ( m => m . id === params . modelId ) ;
318+ const model = models . find ( m => m . id === requestedModelName ) ;
318319 if ( ! model ) throw new Error ( `Unknown model ${ params . modelId } ` ) ;
320+ if ( requestedModelId . serviceTier === "fast" && ! model . additionalSpeedTiers . includes ( "fast" ) ) {
321+ throw new Error ( `Unsupported service tier fast for model ${ requestedModelName } ` ) ;
322+ }
319323
320- const requestedEffort = readStringMeta ( params . _meta , "reasoningEffort" ) as ReasoningEffort | null ;
321- let reasoningEffort = model . defaultReasoningEffort ;
322- if ( requestedEffort !== null ) {
324+ const requestedEffortValue = requestedEffort as ReasoningEffort | undefined ;
325+ let reasoningEffort : ReasoningEffort ;
326+ if ( requestedEffortValue ) {
323327 const matchedEffort = model . supportedReasoningEfforts . find (
324- ( option ) => option . reasoningEffort === requestedEffort
328+ ( option ) => option . reasoningEffort === requestedEffortValue
325329 ) ?. reasoningEffort ;
326330
327331 if ( ! matchedEffort ) {
328- throw new Error ( `Unsupported reasoning effort ${ requestedEffort } for model ${ model . id } ` ) ;
332+ throw new Error ( `Unsupported reasoning effort ${ requestedEffortValue } for model ${ requestedModelName } ` ) ;
329333 }
330334
331335 reasoningEffort = matchedEffort ;
336+ } else {
337+ reasoningEffort = model . defaultReasoningEffort ;
332338 }
333339
334- const requestedServiceTier = readStringMeta ( params . _meta , "serviceTier" ) as ServiceTier | null ;
335- if ( requestedServiceTier !== null && ! model . additionalSpeedTiers . includes ( requestedServiceTier ) ) {
336- throw new Error ( `Unsupported service tier ${ requestedServiceTier } for model ${ model . id } ` ) ;
337- }
338-
339- sessionState . currentModelId = model . id ;
340- sessionState . currentReasoningEffort = reasoningEffort ;
341- sessionState . currentServiceTier = requestedServiceTier ;
340+ sessionState . currentModelId = ModelId . fromComponents ( model , reasoningEffort , requestedModelId . serviceTier ) . toString ( ) ;
342341 sessionState . supportedReasoningEfforts = model . supportedReasoningEfforts ;
343342 sessionState . supportedInputModalities = model . inputModalities ;
344343
@@ -350,28 +349,35 @@ export class CodexAcpServer implements acp.Agent {
350349 }
351350
352351 private findCurrentModel ( models : Model [ ] , currentModelId : string ) : Model | undefined {
353- return models . find ( m => m . id === currentModelId ) ;
352+ const modelId = ModelId . fromString ( currentModelId ) ;
353+ return models . find ( m => m . id === modelId . model ) ;
354354 }
355355
356- private createModelState ( availableModels : Model [ ] , selection : ModelSelection ) : SessionModelState {
356+ private createModelState ( availableModels : Model [ ] , selectedModelId : string ) : SessionModelState {
357357 const allowedModels = availableModels
358- . map ( ( model ) => ( {
359- modelId : model . id ,
360- name : model . displayName ,
361- description : model . description ,
362- _meta : {
363- supportedReasoningEfforts : model . supportedReasoningEfforts ,
364- defaultReasoningEffort : model . defaultReasoningEffort ,
365- serviceTiers : model . additionalSpeedTiers ,
366- } ,
367- } ) ) ;
358+ . flatMap ( ( model ) =>
359+ model . supportedReasoningEfforts . flatMap ( ( effort ) => {
360+ const standardModel = {
361+ modelId : ModelId . fromComponents ( model , effort . reasoningEffort ) . toString ( ) ,
362+ name : `${ model . displayName } (${ effort . reasoningEffort } )` ,
363+ description : `${ model . description } ${ effort . description } ` ,
364+ } ;
365+ if ( ! model . additionalSpeedTiers . includes ( "fast" ) ) {
366+ return [ standardModel ] ;
367+ }
368+ return [
369+ standardModel ,
370+ {
371+ modelId : ModelId . fromComponents ( model , effort . reasoningEffort , "fast" ) . toString ( ) ,
372+ name : `${ model . displayName } (${ effort . reasoningEffort } , fast)` ,
373+ description : `${ model . description } ${ effort . description } Fast service tier.` ,
374+ } ,
375+ ] ;
376+ } )
377+ ) ;
368378 return {
369379 availableModels : allowedModels ,
370- currentModelId : selection . currentModelId ,
371- _meta : {
372- currentReasoningEffort : selection . currentReasoningEffort ,
373- currentServiceTier : selection . currentServiceTier ,
374- } ,
380+ currentModelId : selectedModelId ,
375381 }
376382 }
377383
@@ -395,14 +401,12 @@ export class CodexAcpServer implements acp.Agent {
395401 ) ;
396402
397403 const account = await this . getActiveAccount ( ) ;
398- const { sessionId, currentModelId, currentReasoningEffort , currentServiceTier , models, thread} = sessionMetadata ;
404+ const { sessionId, currentModelId, models, thread} = sessionMetadata ;
399405 const sessionMcpServers = this . resolveSessionMcpServers ( requestedMcpServers , true ) ;
400406 const currentModel = this . findCurrentModel ( models , currentModelId ) ;
401407 const sessionState : SessionState = {
402408 sessionId : sessionId ,
403409 currentModelId : currentModelId ,
404- currentReasoningEffort : currentReasoningEffort ,
405- currentServiceTier : currentServiceTier ,
406410 supportedReasoningEfforts : currentModel ?. supportedReasoningEfforts ?? [ ] ,
407411 supportedInputModalities : currentModel ?. inputModalities ?? [ "text" , "image" ] ,
408412 agentMode : AgentMode . getInitialAgentMode ( ) ,
@@ -426,7 +430,7 @@ export class CodexAcpServer implements acp.Agent {
426430 }
427431
428432 await this . availableCommands . publish ( sessionId ) ;
429- const sessionModelState : SessionModelState = this . createModelState ( models , sessionState ) ;
433+ const sessionModelState : SessionModelState = this . createModelState ( models , currentModelId ) ;
430434 const sessionModeState : SessionModeState = sessionState . agentMode . toSessionModeState ( ) ;
431435
432436 return {
@@ -763,6 +767,7 @@ export class CodexAcpServer implements acp.Agent {
763767 } ;
764768 }
765769
770+ const modelId = ModelId . fromString ( sessionState . currentModelId ) ;
766771 const modelLacksReasoning = sessionState . supportedReasoningEfforts . length > 0
767772 && sessionState . supportedReasoningEfforts . every ( e => e . reasoningEffort === "none" ) ;
768773
@@ -779,7 +784,7 @@ export class CodexAcpServer implements acp.Agent {
779784 }
780785 const agentMode = sessionState . agentMode ;
781786 const turnCompleted = await this . runWithProcessCheck (
782- ( ) => this . codexAcpClient . sendPrompt ( params , agentMode , sessionState , disableSummary , sessionState . cwd ) ) ;
787+ ( ) => this . codexAcpClient . sendPrompt ( params , agentMode , modelId , disableSummary , sessionState . cwd ) ) ;
783788
784789 // Check if turn was interrupted (cancelled)
785790 if ( turnCompleted . turn . status === "interrupted" ) {
@@ -823,7 +828,7 @@ export class CodexAcpServer implements acp.Agent {
823828 private buildQuotaMeta ( sessionState : SessionState ) : { quota : QuotaMeta } {
824829 const lastTokenUsage = sessionState . lastTokenUsage ;
825830
826- const modelName = sessionState . currentModelId ;
831+ const modelName = ModelId . fromString ( sessionState . currentModelId ) . model ;
827832
828833 // FIXME: currently all tokens are reported for the current model
829834 const modelUsage = ( lastTokenUsage != null )
@@ -896,14 +901,3 @@ export class CodexAcpServer implements acp.Agent {
896901function getRequestedMcpServerNames ( mcpServers : Array < acp . McpServer > ) : Array < string > {
897902 return Array . from ( new Set ( mcpServers . map ( server => server . name ) ) ) ;
898903}
899-
900- function readStringMeta ( meta : { [ key : string ] : unknown } | null | undefined , key : string ) : string | null {
901- const value = meta ?. [ key ] ;
902- if ( value === undefined || value === null ) {
903- return null ;
904- }
905- if ( typeof value !== "string" ) {
906- throw RequestError . invalidParams ( `Expected _meta.${ key } to be a string` ) ;
907- }
908- return value ;
909- }
0 commit comments