-
Notifications
You must be signed in to change notification settings - Fork 52.3k
fix(desktop): deleted profile stays deleted (closes #95188) #95239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -78,6 +78,48 @@ export function _resetConnectionsForTests(): void { | |
| $pendingConnectionId.set(null) | ||
| } | ||
|
|
||
| /** | ||
| * Forget the cached last-used profile for every connection when the named | ||
| * profile has just been deleted. | ||
| * | ||
| * Regression for issue #95188 (path B): the renderer persisted | ||
| * ``hermes.desktop.lastProfileByConnection`` so the next boot could dial | ||
| * the user's preferred profile per source. The delete dialog only reset | ||
| * that key when the deleted profile was the active foreground one | ||
| * (``if (wasActive)``); deleting from another workspace left the key | ||
| * pointing at the dead profile forever. On the next restart | ||
| * ``selectConnection()`` called ``ensureGatewayAgent()`` with the deleted | ||
| * profile name, which spawned a full backend whose | ||
| * ``ensure_hermes_home()`` rebuilt the entire profile tree. | ||
| * | ||
| * The function is best-effort and idempotent: an entry that doesn't | ||
| * match is left alone, and the change is persisted via the existing | ||
| * ``$lastProfileByConnection`` subscriber (so callers don't need to | ||
| * touch ``localStorage`` themselves). | ||
| */ | ||
| export function forgetLastProfileForAllConnections(profile: string): void { | ||
| const target = normalizeProfileKey(profile) | ||
|
|
||
| if (!target || target === 'default') { | ||
| return | ||
| } | ||
|
|
||
| const current = $lastProfileByConnection.get() | ||
| const next: Record<string, string> = {} | ||
|
|
||
| for (const [connectionId, lastProfile] of Object.entries(current)) { | ||
| if (normalizeProfileKey(lastProfile) === target) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AI-generated comment: This comment was produced automatically by AI and may be misleading. Please independently verify the cited evidence. Matching only by profile name crosses source boundaries. A local researcher delete on this exact head also removed homelab's independent researcher preference, so the next remote switch dialed default. Please scope invalidation by connectionId plus normalized profile and cover same-name profiles on two connections. |
||
| continue | ||
| } | ||
|
|
||
| next[connectionId] = lastProfile | ||
| } | ||
|
|
||
| if (Object.keys(next).length !== Object.keys(current).length) { | ||
| $lastProfileByConnection.set(next) | ||
| } | ||
| } | ||
|
|
||
| export function setConnectionsRegistry(registry: DesktopConnectionsRegistry): void { | ||
| $connectionsRegistry.set(registry) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AI-generated comment: This comment was produced automatically by AI and may be misleading. Please independently verify the cited evidence.
This invalidates persisted state before the DELETE has succeeded. I reproduced a rejected permission-denied delete on this exact head: the next local switch dialed default instead of the still-valid researcher profile. Please move the targeted invalidation after await deleteProfile returns successfully and add a failed-delete regression.