c5627ebb14
After running /logout and entering a new API key, subsequent requests could still be sent with the old key because the resolution path checked the OS keyring before the in-memory override. The keyring still held the old credential, so it shadowed the freshly-typed one. Three changes: 1. **`Config::deepseek_api_key()` — explicit override is now path 0.** When `self.api_key` is explicitly set (non-empty, non-sentinel), it wins over keyring/env/provider-config. This is what the user just typed, so it should be authoritative. Existing keyring-based flows are unaffected: users who store their key via `auth set` have `self.api_key = None`, so path 1 (keyring) still wins for them. 2. **`clear_api_key()` now wipes the keyring + provider-scoped keys.** Previously only the legacy root `api_key = ...` line was stripped from config.toml. Now every known provider slot in the OS keyring (deepseek, nvidia-nim, openrouter, novita, fireworks, sglang) is deleted, and every `api_key` line nested in a `[providers.<name>]` table is also stripped. 3. **`/logout` clears the in-memory `Config` too.** The dispatcher handler in ui.rs::execute_command_input wipes `config.api_key` and every `config.providers.*.api_key` so a future clone of the long-lived Config doesn't leak the stale value. The companion onboarding flow in ui.rs also stamps the new key onto `config` itself rather than only on a one-shot clone, so subsequent /provider switches see the new credential. Test coverage: - `clear_api_key_strips_root_and_provider_scoped_keys` — verifies all three credential locations get wiped from a fixture config.toml. - `deepseek_api_key_prefers_explicit_in_memory_override` — guards the precedence flip. - `deepseek_api_key_ignores_sentinel_placeholder` — confirms the legacy `KEYRING_SENTINEL` placeholder still falls through. Closes #343. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>