( rawConfig: Record<string, unknown>, recoveredTopLevelKeys: string[] = [], )
| 171 | } |
| 172 | |
| 173 | function parsePiConfig( |
| 174 | rawConfig: Record<string, unknown>, |
| 175 | recoveredTopLevelKeys: string[] = [], |
| 176 | ): { |
| 177 | config: MagicContextConfig; |
| 178 | warnings: string[]; |
| 179 | } { |
| 180 | const preMigrationWarnings: string[] = []; |
| 181 | const agentMigrated = migrateLegacyAgentEnabledInMemory( |
| 182 | rawConfig, |
| 183 | preMigrationWarnings, |
| 184 | ); |
| 185 | // Relocate graduated experimental.* keys (temporal_awareness, caveman → |
| 186 | // top-level; auto_search, git_commit_indexing → memory.*; user_memories, |
| 187 | // pin_key_files → dreamer.*). Shared with OpenCode so both harnesses preserve |
| 188 | // a user's opt-in/opt-out across the upgrade. |
| 189 | const migrated = migrateDreamerV2( |
| 190 | migrateLegacyExperimental(agentMigrated, preMigrationWarnings), |
| 191 | preMigrationWarnings, |
| 192 | ); |
| 193 | const parsed = MagicContextConfigSchema.safeParse(migrated); |
| 194 | if (parsed.success) { |
| 195 | return { config: parsed.data, warnings: preMigrationWarnings }; |
| 196 | } |
| 197 | |
| 198 | const defaults = MagicContextConfigSchema.parse({}); |
| 199 | const errorPaths = new Set<string>(); |
| 200 | // Per top-level key, the FULL error paths — so we can prune only the invalid |
| 201 | // nested leaf instead of the whole block (mirrors OpenCode config recovery). |
| 202 | const issuePathsByKey = new Map<string, PropertyKey[][]>(); |
| 203 | for (const issue of parsed.error.issues) { |
| 204 | const topKey = issue.path[0]; |
| 205 | if (topKey !== undefined) { |
| 206 | const key = String(topKey); |
| 207 | errorPaths.add(key); |
| 208 | const paths = issuePathsByKey.get(key) ?? []; |
| 209 | paths.push([...issue.path]); |
| 210 | issuePathsByKey.set(key, paths); |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | const patched: Record<string, unknown> = { ...migrated }; |
| 215 | const warnings: string[] = [...preMigrationWarnings]; |
| 216 | |
| 217 | for (const key of errorPaths) { |
| 218 | recoveredTopLevelKeys.push(key); |
| 219 | const isAgentConfig = |
| 220 | key === "historian" || key === "dreamer" || key === "sidekick"; |
| 221 | |
| 222 | if (isAgentConfig) { |
| 223 | delete patched[key]; |
| 224 | warnings.push( |
| 225 | `"${key}": invalid agent configuration, ignoring. Check your magic-context.jsonc.`, |
| 226 | ); |
| 227 | continue; |
| 228 | } |
| 229 | |
| 230 | // Object-valued key: prune ONLY invalid nested leaves, keep valid siblings |
no test coverage detected