11import { defaultsToThinkingMode } from "./model-capabilities" ;
22
3- export type DeepcodingEnv = {
3+ export type DeepcodingEnv = Record < string , string | undefined > & {
44 MODEL ?: string ;
55 BASE_URL ?: string ;
66 API_KEY ?: string ;
7- THINKING ?: string ;
7+ THINKING_ENABLED ?: string ;
8+ REASONING_EFFORT ?: string ;
9+ DEBUG_LOG_ENABLED ?: string ;
810} ;
911
1012export type ReasoningEffort = "high" | "max" ;
@@ -27,6 +29,7 @@ export type DeepcodingSettings = {
2729} ;
2830
2931export type ResolvedDeepcodingSettings = {
32+ env : Record < string , string > ;
3033 apiKey ?: string ;
3134 baseURL : string ;
3235 model : string ;
@@ -44,48 +47,203 @@ export type ModelConfigSelection = {
4447 reasoningEffort : ReasoningEffort ;
4548} ;
4649
47- function resolveReasoningEffort ( value : unknown ) : ReasoningEffort {
48- return value === "high" || value === "max" ? value : "max" ;
50+ export type SettingsProcessEnv = Record < string , string | undefined > ;
51+
52+ function resolveReasoningEffort ( value : unknown ) : ReasoningEffort | undefined {
53+ return value === "high" || value === "max" ? value : undefined ;
4954}
5055
51- function resolveThinkingEnabled ( settings : DeepcodingSettings | null | undefined , model : string ) : boolean {
52- if ( typeof settings ?. thinkingEnabled === "boolean" ) {
53- return settings . thinkingEnabled ;
56+ function parseBoolean ( value : unknown ) : boolean | undefined {
57+ if ( typeof value === "boolean" ) {
58+ return value ;
59+ }
60+ if ( typeof value !== "string" ) {
61+ return undefined ;
5462 }
5563
56- const legacyThinking = settings ?. env ?. THINKING ;
57- if ( typeof legacyThinking === "string" && legacyThinking . trim ( ) ) {
58- return legacyThinking . trim ( ) . toLowerCase ( ) === "enabled" ;
64+ const normalized = value . trim ( ) . toLowerCase ( ) ;
65+ if ( [ "1" , "true" , "enabled" , "yes" , "on" ] . includes ( normalized ) ) {
66+ return true ;
67+ }
68+ if ( [ "0" , "false" , "disabled" , "no" , "off" ] . includes ( normalized ) ) {
69+ return false ;
5970 }
71+ return undefined ;
72+ }
6073
61- return defaultsToThinkingMode ( model ) ;
74+ function trimString ( value : unknown ) : string {
75+ return typeof value === "string" ? value . trim ( ) : "" ;
6276}
6377
64- export function resolveSettings (
65- settings : DeepcodingSettings | null | undefined ,
66- defaults : { model : string ; baseURL : string }
78+ function normalizeEnv ( env : DeepcodingSettings [ "env" ] ) : Record < string , string > {
79+ const result : Record < string , string > = { } ;
80+ if ( ! env ) {
81+ return result ;
82+ }
83+
84+ for ( const [ key , value ] of Object . entries ( env ) ) {
85+ if ( typeof value === "string" ) {
86+ result [ key ] = value ;
87+ }
88+ }
89+ return result ;
90+ }
91+
92+ export function collectDeepcodeEnv ( processEnv : SettingsProcessEnv = process . env ) : Record < string , string > {
93+ const result : Record < string , string > = { } ;
94+ for ( const [ key , value ] of Object . entries ( processEnv ) ) {
95+ if ( ! key . startsWith ( "DEEPCODE_" ) || typeof value !== "string" ) {
96+ continue ;
97+ }
98+ const strippedKey = key . slice ( "DEEPCODE_" . length ) ;
99+ if ( strippedKey ) {
100+ result [ strippedKey ] = value ;
101+ }
102+ }
103+ return result ;
104+ }
105+
106+ function extractMcpEnv ( env : Record < string , string > ) : Record < string , string > {
107+ const result : Record < string , string > = { } ;
108+ for ( const [ key , value ] of Object . entries ( env ) ) {
109+ if ( ! key . startsWith ( "MCP_" ) ) {
110+ continue ;
111+ }
112+ const strippedKey = key . slice ( "MCP_" . length ) ;
113+ if ( strippedKey ) {
114+ result [ strippedKey ] = value ;
115+ }
116+ }
117+ return result ;
118+ }
119+
120+ function mergeMcpServers (
121+ userSettings : DeepcodingSettings | null | undefined ,
122+ projectSettings : DeepcodingSettings | null | undefined ,
123+ userEnv : Record < string , string > ,
124+ projectEnv : Record < string , string > ,
125+ systemEnv : Record < string , string >
126+ ) : Record < string , McpServerConfig > | undefined {
127+ const userServers = userSettings ?. mcpServers ?? { } ;
128+ const projectServers = projectSettings ?. mcpServers ?? { } ;
129+ const serverNames = new Set ( [ ...Object . keys ( userServers ) , ...Object . keys ( projectServers ) ] ) ;
130+ if ( serverNames . size === 0 ) {
131+ return undefined ;
132+ }
133+
134+ const userMcpEnv = extractMcpEnv ( userEnv ) ;
135+ const projectMcpEnv = extractMcpEnv ( projectEnv ) ;
136+ const systemMcpEnv = extractMcpEnv ( systemEnv ) ;
137+ const merged : Record < string , McpServerConfig > = { } ;
138+
139+ for ( const name of serverNames ) {
140+ const userConfig = userServers [ name ] ;
141+ const projectConfig = projectServers [ name ] ;
142+ const command = projectConfig ?. command ?? userConfig ?. command ;
143+ if ( ! command ) {
144+ continue ;
145+ }
146+
147+ const env = {
148+ ...userEnv ,
149+ ...( userConfig ?. env ?? { } ) ,
150+ ...userMcpEnv ,
151+ ...projectEnv ,
152+ ...( projectConfig ?. env ?? { } ) ,
153+ ...projectMcpEnv ,
154+ ...systemEnv ,
155+ ...systemMcpEnv ,
156+ } ;
157+ const config : McpServerConfig = {
158+ command,
159+ args : projectConfig ?. args ?? userConfig ?. args ,
160+ } ;
161+ if ( Object . keys ( env ) . length > 0 ) {
162+ config . env = env ;
163+ }
164+ merged [ name ] = config ;
165+ }
166+
167+ return Object . keys ( merged ) . length > 0 ? merged : undefined ;
168+ }
169+
170+ export function resolveSettingsSources (
171+ userSettings : DeepcodingSettings | null | undefined ,
172+ projectSettings : DeepcodingSettings | null | undefined ,
173+ defaults : { model : string ; baseURL : string } ,
174+ processEnv : SettingsProcessEnv = process . env
67175) : ResolvedDeepcodingSettings {
68- const env = settings ?. env ?? { } ;
69- const topLevelModel = typeof settings ?. model === "string" ? settings . model . trim ( ) : "" ;
70- const model = topLevelModel || env . MODEL ?. trim ( ) || defaults . model ;
71- const notify = typeof settings ?. notify === "string" ? settings . notify . trim ( ) : "" ;
72- const webSearchTool = typeof settings ?. webSearchTool === "string" ? settings . webSearchTool . trim ( ) : "" ;
176+ const userEnv = normalizeEnv ( userSettings ?. env ) ;
177+ const projectEnv = normalizeEnv ( projectSettings ?. env ) ;
178+ const systemEnv = collectDeepcodeEnv ( processEnv ) ;
179+ const env = {
180+ ...userEnv ,
181+ ...projectEnv ,
182+ ...systemEnv ,
183+ } ;
184+
185+ const model =
186+ trimString ( systemEnv . MODEL ) ||
187+ trimString ( projectSettings ?. model ) ||
188+ trimString ( projectEnv . MODEL ) ||
189+ trimString ( userSettings ?. model ) ||
190+ trimString ( userEnv . MODEL ) ||
191+ defaults . model ;
73192
74- const mcpServers = settings ?. mcpServers ;
193+ const thinkingEnabled =
194+ parseBoolean ( systemEnv . THINKING_ENABLED ) ??
195+ parseBoolean ( projectSettings ?. thinkingEnabled ) ??
196+ parseBoolean ( projectEnv . THINKING_ENABLED ) ??
197+ parseBoolean ( userSettings ?. thinkingEnabled ) ??
198+ parseBoolean ( userEnv . THINKING_ENABLED ) ??
199+ defaultsToThinkingMode ( model ) ;
200+
201+ const reasoningEffort =
202+ resolveReasoningEffort ( systemEnv . REASONING_EFFORT ) ??
203+ resolveReasoningEffort ( projectSettings ?. reasoningEffort ) ??
204+ resolveReasoningEffort ( projectEnv . REASONING_EFFORT ) ??
205+ resolveReasoningEffort ( userSettings ?. reasoningEffort ) ??
206+ resolveReasoningEffort ( userEnv . REASONING_EFFORT ) ??
207+ "max" ;
208+
209+ const debugLogEnabled =
210+ parseBoolean ( systemEnv . DEBUG_LOG_ENABLED ) ??
211+ parseBoolean ( projectSettings ?. debugLogEnabled ) ??
212+ parseBoolean ( projectEnv . DEBUG_LOG_ENABLED ) ??
213+ parseBoolean ( userSettings ?. debugLogEnabled ) ??
214+ parseBoolean ( userEnv . DEBUG_LOG_ENABLED ) ??
215+ false ;
216+
217+ const notify =
218+ trimString ( systemEnv . NOTIFY ) || trimString ( projectSettings ?. notify ) || trimString ( userSettings ?. notify ) || "" ;
219+ const webSearchTool =
220+ trimString ( systemEnv . WEB_SEARCH_TOOL ) ||
221+ trimString ( projectSettings ?. webSearchTool ) ||
222+ trimString ( userSettings ?. webSearchTool ) ||
223+ "" ;
75224
76225 return {
77- apiKey : env . API_KEY ?. trim ( ) ,
78- baseURL : env . BASE_URL ?. trim ( ) || defaults . baseURL ,
226+ env,
227+ apiKey : trimString ( env . API_KEY ) || undefined ,
228+ baseURL : trimString ( env . BASE_URL ) || defaults . baseURL ,
79229 model,
80- thinkingEnabled : resolveThinkingEnabled ( settings , model ) ,
81- reasoningEffort : resolveReasoningEffort ( settings ?. reasoningEffort ) ,
82- debugLogEnabled : settings ?. debugLogEnabled === true ,
230+ thinkingEnabled,
231+ reasoningEffort,
232+ debugLogEnabled,
83233 notify : notify || undefined ,
84234 webSearchTool : webSearchTool || undefined ,
85- mcpServers,
235+ mcpServers : mergeMcpServers ( userSettings , projectSettings , userEnv , projectEnv , systemEnv ) ,
86236 } ;
87237}
88238
239+ export function resolveSettings (
240+ settings : DeepcodingSettings | null | undefined ,
241+ defaults : { model : string ; baseURL : string } ,
242+ processEnv : SettingsProcessEnv = process . env
243+ ) : ResolvedDeepcodingSettings {
244+ return resolveSettingsSources ( settings , null , defaults , processEnv ) ;
245+ }
246+
89247export function modelConfigKey ( config : Pick < ModelConfigSelection , "thinkingEnabled" | "reasoningEffort" > ) : string {
90248 return config . thinkingEnabled ? `thinking:${ config . reasoningEffort } ` : "thinking:none" ;
91249}
0 commit comments