@@ -174,6 +174,8 @@ public CopilotClient(CopilotClientOptions? options = null)
174174 throw new ArgumentException ( $ "Unsupported RuntimeConnection type: { _connection . GetType ( ) . Name } ", nameof ( options ) ) ;
175175 }
176176
177+ ValidateEnvironmentOptions ( _options , _connection ) ;
178+
177179 _logger = _options . Logger ?? NullLogger . Instance ;
178180 _onListModels = _options . OnListModels ;
179181
@@ -202,6 +204,53 @@ _options.SessionFs is not null ||
202204 }
203205 }
204206
207+ /// <summary>
208+ /// Validates environment-variable options against the resolved transport.
209+ /// Per-client environment is only representable for child-process transports
210+ /// (each client owns its own OS process). The in-process (FFI) transport
211+ /// loads the native runtime into the shared host process, whose single
212+ /// environment block cannot carry per-client values, so environment and
213+ /// telemetry options that lower to environment variables are rejected there.
214+ /// </summary>
215+ private static void ValidateEnvironmentOptions ( CopilotClientOptions options , RuntimeConnection connection )
216+ {
217+ if ( connection is InProcessRuntimeConnection )
218+ {
219+ if ( options . Environment is not null )
220+ {
221+ throw new ArgumentException (
222+ $ "{ nameof ( CopilotClientOptions ) } .{ nameof ( CopilotClientOptions . Environment ) } is not supported with " +
223+ $ "{ nameof ( RuntimeConnection ) } .{ nameof ( RuntimeConnection . ForInProcess ) } (): the in-process transport " +
224+ "loads the native runtime into the shared host process, whose single environment block cannot carry " +
225+ "per-client values. Set the variables on the host process environment instead." ,
226+ nameof ( options ) ) ;
227+ }
228+
229+ if ( options . Telemetry is not null )
230+ {
231+ throw new ArgumentException (
232+ $ "{ nameof ( CopilotClientOptions ) } .{ nameof ( CopilotClientOptions . Telemetry ) } is not supported with " +
233+ $ "{ nameof ( RuntimeConnection ) } .{ nameof ( RuntimeConnection . ForInProcess ) } (): telemetry configuration is " +
234+ "lowered to environment variables read by native runtime code running in the shared host process, so " +
235+ "per-client telemetry cannot be honored in-process. Configure telemetry via the host process " +
236+ "environment, or use a child-process transport." ,
237+ nameof ( options ) ) ;
238+ }
239+
240+ return ;
241+ }
242+
243+ if ( connection is ChildProcessRuntimeConnection { Environment : not null } && options . Environment is not null )
244+ {
245+ throw new ArgumentException (
246+ $ "Set environment variables via either { nameof ( CopilotClientOptions ) } .{ nameof ( CopilotClientOptions . Environment ) } " +
247+ $ "or { nameof ( ChildProcessRuntimeConnection ) } .{ nameof ( ChildProcessRuntimeConnection . Environment ) } , not both. " +
248+ $ "Prefer { nameof ( ChildProcessRuntimeConnection ) } .{ nameof ( ChildProcessRuntimeConnection . Environment ) } for " +
249+ "child-process transports." ,
250+ nameof ( options ) ) ;
251+ }
252+ }
253+
205254 /// <summary>
206255 /// Environment variable that overrides the transport used when the caller does not
207256 /// specify <see cref="CopilotClientOptions.Connection"/>. Accepts <c>"inprocess"</c>
@@ -1943,9 +1992,12 @@ private static void ApplyTelemetryEnvironment(IDictionary<string, string?> envir
19431992 var tcpConnection = _connection as TcpRuntimeConnection ;
19441993 var useStdio = _connection is StdioRuntimeConnection ;
19451994
1946- // Use explicit path, COPILOT_CLI_PATH env var (from options.Environment or process env), or bundled runtime - no PATH fallback
1947- var envCliPath = options . Environment is not null && options . Environment . TryGetValue ( "COPILOT_CLI_PATH" , out var envValue ) ? envValue
1948- : System . Environment . GetEnvironmentVariable ( "COPILOT_CLI_PATH" ) ;
1995+ // Use explicit path, COPILOT_CLI_PATH env var (from the connection's
1996+ // Environment, options.Environment, or process env), or bundled runtime - no PATH fallback
1997+ var envCliPath =
1998+ ( childProcessConnection . Environment is not null && childProcessConnection . Environment . TryGetValue ( "COPILOT_CLI_PATH" , out var connEnvValue ) ? connEnvValue : null )
1999+ ?? ( options . Environment is not null && options . Environment . TryGetValue ( "COPILOT_CLI_PATH" , out var envValue ) ? envValue : null )
2000+ ?? System . Environment . GetEnvironmentVariable ( "COPILOT_CLI_PATH" ) ;
19492001 var cliPath = childProcessConnection . Path
19502002 ?? envCliPath
19512003 ?? GetBundledCliPath ( out var searchedPath )
@@ -2012,10 +2064,11 @@ private static void ApplyTelemetryEnvironment(IDictionary<string, string?> envir
20122064 CreateNoWindow = true
20132065 } ;
20142066
2015- if ( options . Environment != null )
2067+ var childEnvironment = options . Environment ?? childProcessConnection . Environment ;
2068+ if ( childEnvironment != null )
20162069 {
20172070 startInfo . Environment . Clear ( ) ;
2018- foreach ( var ( key , value ) in options . Environment )
2071+ foreach ( var ( key , value ) in childEnvironment )
20192072 {
20202073 startInfo . Environment [ key ] = value ;
20212074 }
0 commit comments