@@ -25,6 +25,7 @@ final class AppModel: ObservableObject {
2525 private let snapshotStore = SnapshotStore ( )
2626 private let accountManager = CodexAccountManager ( )
2727 private let desktopController = CodexDesktopControl ( )
28+ private var removedAccounts : [ RemovedAccountIdentity ] = [ ]
2829 private let autoRefreshInterval : TimeInterval = 5 * 60
2930 private var autoRefreshTask : Task < Void , Never > ?
3031 private var addAccountTask : Task < Void , Never > ?
@@ -185,8 +186,12 @@ final class AppModel: ObservableObject {
185186 return
186187 }
187188
189+ let snapshot = self . accounts. filter { !self . requiresReauthentication ( accountID: $0. id) }
190+ guard !snapshot. isEmpty else {
191+ return
192+ }
193+
188194 self . isRefreshingAll = true
189- let snapshot = self . accounts
190195 for account in snapshot {
191196 var state = self . runtimeStates [ account. id] ?? AccountRuntimeState ( )
192197 state. isLoading = true
@@ -260,8 +265,9 @@ final class AppModel: ObservableObject {
260265
261266 do {
262267 let account = try await self . accountManager. addManagedAccount ( )
268+ self . restoreRemovedAccount ( account)
263269 self . accounts = self . accountStore. merge ( existing: self . accounts, incoming: [ account] )
264- try self . accountStore. saveAccounts ( self . accounts)
270+ try self . accountStore. saveAccounts ( self . accounts, removedAccounts : self . removedAccounts )
265271 self . selectedAccountID = self . accounts. first ( where: { $0. matches ( account) } ) ? . id ?? account. id
266272 self . statusMessage = " \( account. displayName) added. "
267273 if let selectedAccount = self . selectedAccount {
@@ -287,8 +293,9 @@ final class AppModel: ObservableObject {
287293
288294 do {
289295 let updated = try await self . accountManager. reauthenticate ( account)
296+ self . restoreRemovedAccount ( updated)
290297 self . mergeAccount ( updated)
291- try self . accountStore. saveAccounts ( self . accounts)
298+ try self . accountStore. saveAccounts ( self . accounts, removedAccounts : self . removedAccounts )
292299 self . statusMessage = " \( updated. displayName) reauthenticated. "
293300 if let refreshed = self . accounts. first ( where: { $0. id == account. id } ) {
294301 await self . refresh ( account: refreshed)
@@ -308,7 +315,7 @@ final class AppModel: ObservableObject {
308315 let result = try self . accountManager. switchActiveAccount ( account, existing: self . accounts)
309316 if let materializedAccount = result. materializedAccount {
310317 self . mergeAccount ( materializedAccount)
311- try self . accountStore. saveAccounts ( self . accounts)
318+ try self . accountStore. saveAccounts ( self . accounts, removedAccounts : self . removedAccounts )
312319 }
313320
314321 self . loadInitialAccounts ( )
@@ -395,17 +402,20 @@ final class AppModel: ObservableObject {
395402
396403 private func loadInitialAccounts( ) {
397404 do {
398- let loadedAccounts = try self . accountStore. loadAccounts ( )
405+ let stored = try self . accountStore. loadAccountList ( )
406+ self . removedAccounts = stored. removedAccounts
407+ let loadedAccounts = stored. accounts. filter { !self . isRemoved ( $0) }
399408 let storedAccounts = loadedAccounts. filter { $0. source != . ambient }
400409 let discoveredManagedAccounts = try self . accountManager. discoverManagedAccounts ( existing: loadedAccounts)
401410 var incomingAccounts = discoveredManagedAccounts
402411 if let ambientAccount = try self . accountManager. discoverAmbientAccount ( existing: loadedAccounts) {
403412 incomingAccounts. insert ( ambientAccount, at: 0 )
404413 }
414+ incomingAccounts. removeAll { self . isRemoved ( $0) }
405415
406416 self . accounts = self . accountStore. merge ( existing: storedAccounts, incoming: incomingAccounts)
407- if self . accounts != loadedAccounts {
408- try self . accountStore. saveAccounts ( self . accounts)
417+ if self . accounts != stored . accounts {
418+ try self . accountStore. saveAccounts ( self . accounts, removedAccounts : self . removedAccounts )
409419 }
410420 self . ensureSelection ( )
411421 self . refreshActiveIdentity ( )
@@ -420,12 +430,16 @@ final class AppModel: ObservableObject {
420430 }
421431
422432 private func remove( _ account: StoredAccount ) {
423- self . accounts. removeAll { $0. id == account. id }
433+ let removedIdentity = RemovedAccountIdentity ( account: account)
434+ self . removedAccounts. removeAll { $0. matches ( account) }
435+ self . removedAccounts. append ( removedIdentity)
436+
437+ self . accounts. removeAll { $0. id == account. id || removedIdentity. matches ( $0) }
424438 self . runtimeStates. removeValue ( forKey: account. id)
425439
426440 do {
427441 try self . accountManager. removeManagedFilesIfOwned ( account)
428- try self . accountStore. saveAccounts ( self . accounts)
442+ try self . accountStore. saveAccounts ( self . accounts, removedAccounts : self . removedAccounts )
429443 self . ensureSelection ( )
430444 self . statusMessage = " \( account. displayName) removed. "
431445 } catch {
@@ -478,12 +492,29 @@ final class AppModel: ObservableObject {
478492
479493 private func persistAccountsSilently( ) {
480494 do {
481- try self . accountStore. saveAccounts ( self . accounts)
495+ try self . accountStore. saveAccounts ( self . accounts, removedAccounts : self . removedAccounts )
482496 } catch {
483497 self . statusMessage = error. localizedDescription
484498 }
485499 }
486500
501+ private func isRemoved( _ account: StoredAccount ) -> Bool {
502+ self . removedAccounts. contains { $0. matches ( account) }
503+ }
504+
505+ private func restoreRemovedAccount( _ account: StoredAccount ) {
506+ self . removedAccounts. removeAll { $0. matches ( account) }
507+ }
508+
509+ private func requiresReauthentication( accountID: UUID ) -> Bool {
510+ guard let message = self . runtimeStates [ accountID] ? . errorMessage? . lowercased ( ) else {
511+ return false
512+ }
513+
514+ return message. contains ( " refresh token " )
515+ && message. contains ( " sign in again " )
516+ }
517+
487518 private func persistSnapshotsSilently( ) {
488519 let snapshots = self . runtimeStates. compactMapValues ( \. snapshot)
489520 do {
0 commit comments