@@ -12,15 +12,21 @@ import {
1212 type ListSessionsRequest ,
1313 type ListSessionsResponse ,
1414 type LoadSessionRequest ,
15+ type LoadSessionResponse ,
1516 type NewSessionRequest ,
17+ type NewSessionResponse ,
1618 type PermissionOption ,
1719 type PlanEntry ,
1820 type PromptRequest ,
1921 type ResumeSessionRequest ,
2022 type ResumeSessionResponse ,
23+ type SessionConfigOption ,
2124 type Role ,
2225 type SessionInfo ,
26+ type SetSessionConfigOptionRequest ,
27+ type SetSessionConfigOptionResponse ,
2328 type SetSessionModelRequest ,
29+ type SetSessionModelResponse ,
2430 type SetSessionModeRequest ,
2531 type SetSessionModeResponse ,
2632 type ToolCallContent ,
@@ -48,8 +54,13 @@ import { applyPatch } from "diff"
4854
4955type ModeOption = { id : string ; name : string ; description ?: string }
5056type ModelOption = { modelId : string ; name : string }
57+ type ProviderEntry = { id : string ; name : string ; models : Record < string , any > }
58+ type VariantEntry = { id : string ; models : Record < string , { variants ?: Record < string , any > } > }
5159
5260const DEFAULT_VARIANT_VALUE = "default"
61+ const MODE_CONFIG_ID = "mode"
62+ const MODEL_CONFIG_ID = "model"
63+ const REASONING_CONFIG_ID = "reasoning_effort"
5364
5465export namespace ACP {
5566 const log = Log . create ( { service : "acp-agent" } )
@@ -567,7 +578,7 @@ export namespace ACP {
567578 throw new Error ( "Authentication not implemented" )
568579 }
569580
570- async newSession ( params : NewSessionRequest ) {
581+ async newSession ( params : NewSessionRequest ) : Promise < NewSessionResponse > {
571582 const directory = params . cwd
572583 try {
573584 const model = await defaultModel ( this . config , directory )
@@ -586,6 +597,7 @@ export namespace ACP {
586597
587598 return {
588599 sessionId,
600+ configOptions : load . configOptions ,
589601 models : load . models ,
590602 modes : load . modes ,
591603 _meta : load . _meta ,
@@ -601,7 +613,7 @@ export namespace ACP {
601613 }
602614 }
603615
604- async loadSession ( params : LoadSessionRequest ) {
616+ async loadSession ( params : LoadSessionRequest ) : Promise < LoadSessionResponse > {
605617 const directory = params . cwd
606618 const sessionId = params . sessionId
607619
@@ -635,12 +647,29 @@ export namespace ACP {
635647 } )
636648
637649 const lastUser = messages ?. findLast ( ( m ) => m . info . role === "user" ) ?. info
638- if ( lastUser ?. role === "user" ) {
650+ if ( lastUser ?. role === "user" && result . models ) {
639651 result . models . currentModelId = `${ lastUser . model . providerID } /${ lastUser . model . modelID } `
640652 this . sessionManager . setModel ( sessionId , {
641653 providerID : ProviderID . make ( lastUser . model . providerID ) ,
642654 modelID : ModelID . make ( lastUser . model . modelID ) ,
643655 } )
656+ this . sessionManager . setVariant ( sessionId , lastUser . variant )
657+ result . configOptions = buildConfigOptions ( {
658+ providers : await this . sdk . config
659+ . providers ( { directory } , { throwOnError : true } )
660+ . then ( ( x ) => sortProvidersByName ( x . data ! . providers ) ) ,
661+ modes : ( result . modes ?. availableModes ?? [ ] ) . map ( ( mode ) => ( {
662+ id : mode . id ,
663+ name : mode . name ,
664+ description : mode . description ?? undefined ,
665+ } ) ) ,
666+ currentModeId : result . modes ?. currentModeId ,
667+ model : {
668+ providerID : ProviderID . make ( lastUser . model . providerID ) ,
669+ modelID : ModelID . make ( lastUser . model . modelID ) ,
670+ } ,
671+ variant : lastUser . variant ,
672+ } )
644673 if ( result . modes ?. availableModes . some ( ( m ) => m . id === lastUser . agent ) ) {
645674 result . modes . currentModeId = lastUser . agent
646675 this . sessionManager . setMode ( sessionId , lastUser . agent )
@@ -1147,7 +1176,7 @@ export namespace ACP {
11471176 return { availableModes, currentModeId }
11481177 }
11491178
1150- private async loadSessionMode ( params : LoadSessionRequest ) {
1179+ private async loadSessionMode ( params : LoadSessionRequest ) : Promise < LoadSessionResponse & { sessionId : string } > {
11511180 const directory = params . cwd
11521181 const model = await defaultModel ( this . config , directory )
11531182 const sessionId = params . sessionId
@@ -1156,10 +1185,11 @@ export namespace ACP {
11561185 const entries = sortProvidersByName ( providers )
11571186 const availableVariants = modelVariantsFromProviders ( entries , model )
11581187 const currentVariant = this . sessionManager . getVariant ( sessionId )
1159- if ( currentVariant && ! availableVariants . includes ( currentVariant ) ) {
1188+ const variant = currentVariant && availableVariants . includes ( currentVariant ) ? currentVariant : undefined
1189+ if ( currentVariant && ! variant ) {
11601190 this . sessionManager . setVariant ( sessionId , undefined )
11611191 }
1162- const availableModels = buildAvailableModels ( entries , { includeVariants : true } )
1192+ const availableModels = buildAvailableModels ( entries )
11631193 const modeState = await this . resolveModeState ( directory , sessionId )
11641194 const currentModeId = modeState . currentModeId
11651195 const modes = currentModeId
@@ -1168,6 +1198,13 @@ export namespace ACP {
11681198 currentModeId,
11691199 }
11701200 : undefined
1201+ const configOptions = buildConfigOptions ( {
1202+ providers : entries ,
1203+ modes : modeState . availableModes ,
1204+ currentModeId,
1205+ model,
1206+ variant,
1207+ } )
11711208
11721209 const commands = await this . config . sdk . command
11731210 . list (
@@ -1241,20 +1278,21 @@ export namespace ACP {
12411278
12421279 return {
12431280 sessionId,
1281+ configOptions,
12441282 models : {
1245- currentModelId : formatModelIdWithVariant ( model , currentVariant , availableVariants , true ) ,
1283+ currentModelId : formatModelIdWithVariant ( model , undefined , availableVariants , false ) ,
12461284 availableModels,
12471285 } ,
12481286 modes,
12491287 _meta : buildVariantMeta ( {
12501288 model,
1251- variant : this . sessionManager . getVariant ( sessionId ) ,
1289+ variant,
12521290 availableVariants,
12531291 } ) ,
12541292 }
12551293 }
12561294
1257- async unstable_setSessionModel ( params : SetSessionModelRequest ) {
1295+ async unstable_setSessionModel ( params : SetSessionModelRequest ) : Promise < SetSessionModelResponse > {
12581296 const session = this . sessionManager . get ( params . sessionId )
12591297 const providers = await this . sdk . config
12601298 . providers ( { directory : session . cwd } , { throwOnError : true } )
@@ -1266,16 +1304,110 @@ export namespace ACP {
12661304
12671305 const entries = sortProvidersByName ( providers )
12681306 const availableVariants = modelVariantsFromProviders ( entries , selection . model )
1307+ const variant = selection . variant && availableVariants . includes ( selection . variant ) ? selection . variant : undefined
1308+
1309+ if ( selection . variant && ! variant ) {
1310+ this . sessionManager . setVariant ( session . id , undefined )
1311+ }
1312+
1313+ setTimeout ( ( ) => {
1314+ this . connection . sessionUpdate ( {
1315+ sessionId : session . id ,
1316+ update : {
1317+ sessionUpdate : "config_option_update" ,
1318+ configOptions : buildConfigOptions ( {
1319+ providers : entries ,
1320+ modes : [ ] ,
1321+ currentModeId : this . sessionManager . get ( session . id ) . modeId ,
1322+ model : selection . model ,
1323+ variant,
1324+ } ) ,
1325+ } ,
1326+ } )
1327+ } , 0 )
12691328
12701329 return {
12711330 _meta : buildVariantMeta ( {
12721331 model : selection . model ,
1273- variant : selection . variant ,
1332+ variant,
12741333 availableVariants,
12751334 } ) ,
12761335 }
12771336 }
12781337
1338+ async setSessionConfigOption ( params : SetSessionConfigOptionRequest ) : Promise < SetSessionConfigOptionResponse > {
1339+ const session = this . sessionManager . get ( params . sessionId )
1340+ const current = session . model ?? ( await defaultModel ( this . config , session . cwd ) )
1341+ if ( ! session . model ) {
1342+ this . sessionManager . setModel ( session . id , current )
1343+ }
1344+
1345+ const providers = await this . sdk . config
1346+ . providers ( { directory : session . cwd } , { throwOnError : true } )
1347+ . then ( ( x ) => x . data ! . providers )
1348+ const entries = sortProvidersByName ( providers )
1349+ const modes = await this . loadAvailableModes ( session . cwd )
1350+ const modeId = session . modeId ?? ( await AgentModule . defaultAgent ( ) )
1351+
1352+ if ( params . configId === MODE_CONFIG_ID ) {
1353+ if ( ! modes . some ( ( mode ) => mode . id === params . value ) ) {
1354+ throw RequestError . invalidParams ( JSON . stringify ( { error : `Invalid config value: ${ params . value } ` } ) )
1355+ }
1356+ this . sessionManager . setMode ( session . id , params . value )
1357+ return {
1358+ configOptions : buildConfigOptions ( {
1359+ providers : entries ,
1360+ modes,
1361+ currentModeId : params . value ,
1362+ model : current ,
1363+ variant : this . sessionManager . getVariant ( session . id ) ,
1364+ } ) ,
1365+ }
1366+ }
1367+
1368+ if ( params . configId === MODEL_CONFIG_ID ) {
1369+ const selection = parseModelSelection ( params . value , entries )
1370+ const variants = modelVariantsFromProviders ( entries , selection . model )
1371+ const variant = this . sessionManager . getVariant ( session . id )
1372+ const next = variant && variants . includes ( variant ) ? variant : undefined
1373+ this . sessionManager . setModel ( session . id , selection . model )
1374+ this . sessionManager . setVariant ( session . id , next )
1375+ return {
1376+ configOptions : buildConfigOptions ( {
1377+ providers : entries ,
1378+ modes,
1379+ currentModeId : session . modeId ?? modeId ,
1380+ model : selection . model ,
1381+ variant : next ,
1382+ } ) ,
1383+ }
1384+ }
1385+
1386+ if ( params . configId !== REASONING_CONFIG_ID ) {
1387+ throw RequestError . invalidParams ( JSON . stringify ( { error : `Unknown config option: ${ params . configId } ` } ) )
1388+ }
1389+
1390+ const variants = [
1391+ DEFAULT_VARIANT_VALUE ,
1392+ ...modelVariantsFromProviders ( entries , current ) . filter ( ( variant ) => variant !== DEFAULT_VARIANT_VALUE ) ,
1393+ ]
1394+ if ( ! variants . includes ( params . value ) ) {
1395+ throw RequestError . invalidParams ( JSON . stringify ( { error : `Invalid config value: ${ params . value } ` } ) )
1396+ }
1397+
1398+ this . sessionManager . setVariant ( session . id , params . value === DEFAULT_VARIANT_VALUE ? undefined : params . value )
1399+
1400+ return {
1401+ configOptions : buildConfigOptions ( {
1402+ providers : entries ,
1403+ modes,
1404+ currentModeId : session . modeId ?? modeId ,
1405+ model : current ,
1406+ variant : this . sessionManager . getVariant ( session . id ) ,
1407+ } ) ,
1408+ }
1409+ }
1410+
12791411 async setSessionMode ( params : SetSessionModeRequest ) : Promise < SetSessionModeResponse | void > {
12801412 const session = this . sessionManager . get ( params . sessionId )
12811413 const availableModes = await this . loadAvailableModes ( session . cwd )
@@ -1648,7 +1780,7 @@ export namespace ACP {
16481780 }
16491781
16501782 function modelVariantsFromProviders (
1651- providers : Array < { id : string ; models : Record < string , { variants ?: Record < string , any > } > } > ,
1783+ providers : VariantEntry [ ] ,
16521784 model : { providerID : ProviderID ; modelID : ModelID } ,
16531785 ) : string [ ] {
16541786 const provider = providers . find ( ( entry ) => entry . id === model . providerID )
@@ -1658,11 +1790,7 @@ export namespace ACP {
16581790 return Object . keys ( modelInfo . variants )
16591791 }
16601792
1661- function buildAvailableModels (
1662- providers : Array < { id : string ; name : string ; models : Record < string , any > } > ,
1663- options : { includeVariants ?: boolean } = { } ,
1664- ) : ModelOption [ ] {
1665- const includeVariants = options . includeVariants ?? false
1793+ function buildAvailableModels ( providers : ProviderEntry [ ] ) : ModelOption [ ] {
16661794 return providers . flatMap ( ( provider ) => {
16671795 const unsorted : Array < { id : string ; name : string ; variants ?: Record < string , any > } > = Object . values (
16681796 provider . models ,
@@ -1673,17 +1801,86 @@ export namespace ACP {
16731801 modelId : `${ provider . id } /${ model . id } ` ,
16741802 name : `${ provider . name } /${ model . name } ` ,
16751803 }
1676- if ( ! includeVariants || ! model . variants ) return [ base ]
1677- const variants = Object . keys ( model . variants ) . filter ( ( variant ) => variant !== DEFAULT_VARIANT_VALUE )
1678- const variantOptions = variants . map ( ( variant ) => ( {
1679- modelId : `${ provider . id } /${ model . id } /${ variant } ` ,
1680- name : `${ provider . name } /${ model . name } (${ variant } )` ,
1681- } ) )
1682- return [ base , ...variantOptions ]
1804+ return [ base ]
16831805 } )
16841806 } )
16851807 }
16861808
1809+ function buildReasoning ( input : {
1810+ providers : VariantEntry [ ]
1811+ model : { providerID : ProviderID ; modelID : ModelID }
1812+ variant ?: string
1813+ } ) : SessionConfigOption [ ] {
1814+ const base = modelVariantsFromProviders ( input . providers , input . model )
1815+ if ( ! base . length ) return [ ]
1816+ const variants = [ DEFAULT_VARIANT_VALUE , ...base . filter ( ( variant ) => variant !== DEFAULT_VARIANT_VALUE ) ]
1817+ const current = input . variant && variants . includes ( input . variant ) ? input . variant : DEFAULT_VARIANT_VALUE
1818+ return [
1819+ {
1820+ type : "select" ,
1821+ id : REASONING_CONFIG_ID ,
1822+ name : "Reasoning effort" ,
1823+ category : "thought_level" ,
1824+ currentValue : current ,
1825+ options : variants . map ( ( variant ) => ( {
1826+ value : variant ,
1827+ name : variant === DEFAULT_VARIANT_VALUE ? "Default" : variant ,
1828+ } ) ) ,
1829+ } ,
1830+ ]
1831+ }
1832+
1833+ function buildModeConfig ( input : { modes : ModeOption [ ] ; currentModeId ?: string } ) : SessionConfigOption [ ] {
1834+ if ( ! input . modes . length || ! input . currentModeId ) return [ ]
1835+ return [
1836+ {
1837+ type : "select" ,
1838+ id : MODE_CONFIG_ID ,
1839+ name : "Mode" ,
1840+ category : "mode" ,
1841+ currentValue : input . currentModeId ,
1842+ options : input . modes . map ( ( mode ) => ( {
1843+ value : mode . id ,
1844+ name : mode . name ,
1845+ description : mode . description ,
1846+ } ) ) ,
1847+ } ,
1848+ ]
1849+ }
1850+
1851+ function buildModelConfig ( input : {
1852+ providers : ProviderEntry [ ]
1853+ model : { providerID : ProviderID ; modelID : ModelID }
1854+ } ) : SessionConfigOption [ ] {
1855+ return [
1856+ {
1857+ type : "select" ,
1858+ id : MODEL_CONFIG_ID ,
1859+ name : "Model" ,
1860+ category : "model" ,
1861+ currentValue : `${ input . model . providerID } /${ input . model . modelID } ` ,
1862+ options : buildAvailableModels ( input . providers ) . map ( ( model ) => ( {
1863+ value : model . modelId ,
1864+ name : model . name ,
1865+ } ) ) ,
1866+ } ,
1867+ ]
1868+ }
1869+
1870+ function buildConfigOptions ( input : {
1871+ providers : ProviderEntry [ ]
1872+ modes : ModeOption [ ]
1873+ currentModeId ?: string
1874+ model : { providerID : ProviderID ; modelID : ModelID }
1875+ variant ?: string
1876+ } ) {
1877+ return [
1878+ ...buildModeConfig ( { modes : input . modes , currentModeId : input . currentModeId } ) ,
1879+ ...buildModelConfig ( { providers : input . providers , model : input . model } ) ,
1880+ ...buildReasoning ( { providers : input . providers , model : input . model , variant : input . variant } ) ,
1881+ ]
1882+ }
1883+
16871884 function formatModelIdWithVariant (
16881885 model : { providerID : ProviderID ; modelID : ModelID } ,
16891886 variant : string | undefined ,
@@ -1711,8 +1908,11 @@ export namespace ACP {
17111908
17121909 function parseModelSelection (
17131910 modelId : string ,
1714- providers : Array < { id : string ; models : Record < string , { variants ?: Record < string , any > } > } > ,
1715- ) : { model : { providerID : ProviderID ; modelID : ModelID } ; variant ?: string } {
1911+ providers : VariantEntry [ ] ,
1912+ ) : {
1913+ model : { providerID : ProviderID ; modelID : ModelID }
1914+ variant ?: string
1915+ } {
17161916 const parsed = Provider . parseModel ( modelId )
17171917 const provider = providers . find ( ( p ) => p . id === parsed . providerID )
17181918 if ( ! provider ) {
0 commit comments