feat: unify Codex third-party providers into stable "custom" history bucket

Codex filters resume history by `model_provider`, so switching between
provider-specific ids like `rightcode` and `aihubmix` made past sessions
appear to vanish. Collapse all third-party providers into a single
stable bucket so cross-switch history stays visible.

- Normalize live `model_provider` to "custom" on every Codex write
  (reserved built-in ids like openai/ollama are preserved).
- Add device-level one-shot migration that rewrites historical JSONL
  session files and the `state_5.sqlite` threads table from legacy
  provider ids into the "custom" bucket. Backs up originals under
  `~/.cc-switch/backups/codex-history-provider-migration-v1/` and uses
  the SQLite Backup API for the state DB.
- Record completion in `settings.json` under `localMigrations` so the
  migration is strictly idempotent across launches.
- Update Codex provider preset templates to emit `model_provider = "custom"`
  out of the box.
This commit is contained in:
Jason
2026-05-20 17:10:38 +08:00
parent 2a4651a21e
commit b44f83f7c5
11 changed files with 771 additions and 121 deletions
+26
View File
@@ -5,6 +5,7 @@ mod claude_desktop_config;
mod claude_mcp;
mod claude_plugin;
mod codex_config;
mod codex_history_migration;
mod commands;
mod config;
mod database;
@@ -535,6 +536,31 @@ pub fn run() {
Err(e) => log::warn!("✗ Failed to seed official providers: {e}"),
}
{
let db_for_codex_history_migration = app_state.db.clone();
tauri::async_runtime::spawn_blocking(move || {
match crate::codex_history_migration::maybe_migrate_codex_third_party_history_provider_bucket(
&db_for_codex_history_migration,
) {
Ok(outcome) => {
if let Some(reason) = outcome.skipped_reason {
log::debug!("○ Codex history provider bucket migration skipped: {reason}");
} else {
log::info!(
"✓ Codex history provider bucket migration completed: sources={}, jsonl_files={}, state_rows={}",
outcome.source_provider_ids.len(),
outcome.migrated_jsonl_files,
outcome.migrated_state_rows
);
}
}
Err(e) => {
log::warn!("✗ Codex history provider bucket migration failed: {e}");
}
}
});
}
// 老用户 / 已确认的路径由 `fresh_install_at_startup` 自行拦截,这里不做写入。
// 字段只由前端在用户点击"我知道了"时 save_settings 回写,语义是"用户显式确认过"。
if !first_run_already_confirmed && fresh_install_at_startup {