fix(chat): provision the chat identity after an encrypted wallet unlocks

Lite opens an encrypted wallet locked, but the model saying so arrives
a moment later. The first seed export therefore failed with "locked"
and disabled chat for good, so encrypted Lite wallets never got chat.

- While the wallet is locked, a failure recorded before the lock was
  seen is cleared, so provisioning runs again after the unlock (like
  lockLiteWallet()). Nothing is retried while locked: that branch
  returns before any RPC or backend call.
- Lite waits for the first model's lock state before exporting the
  seed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 19:52:10 -05:00
co-authored by Claude Opus 5.5
parent 7fa7c3d6da
commit 4eed4283d6
+6
View File
@@ -3326,6 +3326,11 @@ void App::maybeProvisionChatIdentity()
// isLocked()==false and fall through.
if (state_.isLocked()) {
if (chat_identity_provisioned_ || chat_service_.hasIdentity()) resetChatSession();
// A failure recorded before the lock was seen isn't final: lite opens an encrypted wallet
// LOCKED, but the model saying so arrives a moment later, so the first seed export fails with
// "locked" and used to disable chat for good. Re-arm for the next unlock, like lockLiteWallet().
// Nothing is retried while locked: this returns before any RPC or backend call.
chat_identity_unavailable_ = false;
return;
}
@@ -3338,6 +3343,7 @@ void App::maybeProvisionChatIdentity()
if (supportsLiteBackend()) {
// Lite: the backend's `seed` command returns the 24-word mnemonic synchronously.
if (!lite_wallet_ || !lite_wallet_->walletOpen()) return;
if (!state_.encryption_state_known) return; // wait for the first model's lock state
wallet::LiteSeedResult seed = lite_wallet_->exportSeed();
if (!seed.ok || seed.seedPhrase.empty()) {
wallet::secureWipeLiteSecret(seed.seedPhrase);