| 1126 | } |
| 1127 | |
| 1128 | async saveConfig(config: ProjectsConfig): Promise<void> { |
| 1129 | try { |
| 1130 | if (!fs.existsSync(this.rootDir)) { |
| 1131 | ensurePrivateDirSync(this.rootDir); |
| 1132 | } |
| 1133 | |
| 1134 | const data: Partial<Record<keyof AppConfigOnDisk, unknown>> & { |
| 1135 | projects: Array<[string, ProjectConfig]>; |
| 1136 | } = { |
| 1137 | projects: Array.from(config.projects.entries()).map( |
| 1138 | ([projectPath, projectConfig]) => |
| 1139 | [projectPath, normalizeProjectRuntimeSettings(projectConfig)] as [string, ProjectConfig] |
| 1140 | ), |
| 1141 | taskSettings: config.taskSettings ?? DEFAULT_TASK_SETTINGS, |
| 1142 | }; |
| 1143 | |
| 1144 | const muxGatewayEnabled = parseOptionalBoolean(config.muxGatewayEnabled); |
| 1145 | if (muxGatewayEnabled !== undefined) { |
| 1146 | data.muxGatewayEnabled = muxGatewayEnabled; |
| 1147 | } |
| 1148 | |
| 1149 | const chatTranscriptFullWidth = parseOptionalBoolean(config.chatTranscriptFullWidth); |
| 1150 | if (chatTranscriptFullWidth === true) { |
| 1151 | data.chatTranscriptFullWidth = true; |
| 1152 | } |
| 1153 | |
| 1154 | const llmDebugLogs = parseOptionalBoolean(config.llmDebugLogs); |
| 1155 | if (llmDebugLogs !== undefined) { |
| 1156 | data.llmDebugLogs = llmDebugLogs; |
| 1157 | } |
| 1158 | |
| 1159 | const heartbeatDefaultPrompt = parseOptionalNonEmptyString(config.heartbeatDefaultPrompt); |
| 1160 | if (heartbeatDefaultPrompt) { |
| 1161 | data.heartbeatDefaultPrompt = heartbeatDefaultPrompt; |
| 1162 | } |
| 1163 | |
| 1164 | const heartbeatDefaultIntervalMs = parseOptionalHeartbeatIntervalMs( |
| 1165 | config.heartbeatDefaultIntervalMs |
| 1166 | ); |
| 1167 | if (heartbeatDefaultIntervalMs !== undefined) { |
| 1168 | data.heartbeatDefaultIntervalMs = heartbeatDefaultIntervalMs; |
| 1169 | } |
| 1170 | |
| 1171 | if (config.goalDefaults) { |
| 1172 | data.goalDefaults = normalizeGoalDefaults(config.goalDefaults); |
| 1173 | } |
| 1174 | |
| 1175 | const muxGatewayModels = parseOptionalStringArray(config.muxGatewayModels); |
| 1176 | if (muxGatewayModels !== undefined) { |
| 1177 | data.muxGatewayModels = muxGatewayModels; |
| 1178 | } |
| 1179 | |
| 1180 | const defaultModel = normalizeOptionalModelString(config.defaultModel); |
| 1181 | if (defaultModel !== undefined) { |
| 1182 | data.defaultModel = defaultModel; |
| 1183 | } |
| 1184 | |
| 1185 | const advisorModelString = parseOptionalNonEmptyString(config.advisorModelString); |