fix(desktop): scope pluginSocket's connection to the active profile - #73044
fix(desktop): scope pluginSocket's connection to the active profile#73044pierrenode wants to merge 1 commit into
Conversation
|
Thanks for identifying the profile-routing gap. The Problems
Suggested changes
This is an automated hermes-sweeper review. |
pluginSocket (hermes.ts) is documented as "the live twin of pluginRest, scoped the same way", but it calls window.hermesDesktop.getConnection() with no profile argument, while pluginRest passes the active profile via profileScoped(). getConnection's IPC handler (ensureBackend in electron/main.ts) falls back to the primary profile whenever the profile argument is empty, so an unscoped call always resolves to the primary profile's backend regardless of which profile is actually active. For a plugin used from a non-primary profile (e.g. kanban), this means REST calls go to the correct pooled backend while the plugin's WebSocket silently connects to the wrong one — a multi-profile user sees one profile's data with another profile's live events. Fix: pass the active profile through, mirroring the sibling call sites that already do this correctly (use-gateway-request.ts, use-gateway-boot.ts, store/gateway.ts, store/profile.ts). voice-playback.ts's resolveSpeakStreamUrl had the same gap originally, but main has since fixed it independently (via the getApiRequestProfile() getter rather than direct store access) — dropped from this PR as redundant, keeping only the still-open pluginSocket gap.
0100cb9 to
aab2cd9
Compare
|
Rebased onto current `upstream/main` and dropped the `voice-playback.ts`/TTS half — `main` fixed that gap independently in the meantime (via the `getApiRequestProfile()` getter rather than direct `$activeGatewayProfile` store access), so this PR's version of that hunk was fully redundant (zero net diff against current `main`). Removed both the stale fix and its now-superfluous test file, kept only the still-open `pluginSocket` gap in `hermes.ts`. Retitled the PR to match the narrowed scope. Mutation-verified: temporarily reverted `pluginSocket`'s `getConnection(_apiProfile)` back to the unscoped `getConnection()`, confirmed both new tests fail (asserting the mock was called with `[]` instead of `['work']`/`[null]`), restored, confirmed green. Full desktop `src/` vitest suite (413 files, 3682 tests) passes, `tsc --noEmit` clean. Fresh competitor search: no overlap. Squashed to a single commit on top of current `upstream/main`. |
|
Merged via #87874 with your commit cherry-picked onto current main — authorship preserved. The voice-playback hunk was dropped (already fixed on main independently); the pluginSocket fix was adapted to resolve through the post-#87600 connection source of truth so it also follows registry-agent activations, with mutation-verified regression tests. Thank you! |
Summary
pluginSocket(apps/desktop/src/hermes.ts) is documented in its own docstring as "the live twin ofpluginRest, scoped the same way". It isn't:pluginRestsends...profileScoped()→window.hermesDesktop.api({..., profile})→hermes:apiIPC →ensureBackend(profile)→ the profile-routing table.pluginSocketcallswindow.hermesDesktop.getConnection()with no argument →hermes:connectionIPC →ensureBackend(undefined).ensureBackend's own comment says "an empty / unknown profile resolves to the primary" — confirmed inelectron/main.ts:So an unscoped
getConnection()call always resolves to the primary profile's backend, regardless of which profile is actually active.apps/desktop/src/lib/voice-playback.ts'sresolveSpeakStreamUrlhas the identical gap (desktop.getConnection(), no profile).Impact
For a plugin used from a non-primary profile (e.g.
kanban), REST calls (pluginRest) go to the correct pooled backend while the plugin's live WebSocket (pluginSocket) silently connects to the primary profile's backend instead. A multi-profile user opening the kanban board on a secondary profile sees that profile's board data (frompluginRest) but live events sourced from the wrong (primary) profile's backend — updates never arrive, and the primary profile's unrelated events may leak in.TTS streaming (
startSpeechStream→resolveSpeakStreamUrl) has the same misrouting: voice playback always uses the primary profile's TTS configuration/backend even when speaking from a secondary profile's session.Fix
Both call sites now pass the active profile through to
getConnection, mirroring the sibling call sites that already do this correctly (use-gateway-request.ts,use-gateway-boot.ts,store/gateway.ts,store/profile.ts):hermes.ts::pluginSocket— passes the module-local_apiProfile(the same stateprofileScoped()reads forpluginRest, kept module-private specifically to avoid a store import cycle per its own comment).voice-playback.ts::resolveSpeakStreamUrl— imports$activeGatewayProfilefrom@/store/profileand passes.get(), matching the pattern already used inuse-gateway-request.ts.No behavior change for single-profile users (both resolve to
'default', matchingprimaryProfileKey()'s own fallback).Test plan
New tests in
hermes.test.ts(pluginSocketdescribe block): assertsgetConnectionis called with the scoped profile when one is set viasetApiRequestProfile, and withnullwhen none is scoped.New
voice-playback.test.ts: assertsgetConnectionis called with$activeGatewayProfile's current value.Mutation-verify: stashed both production fixes and reran the new tests against pre-fix code — all 4 new assertions fail as expected (
getConnectioncalled with no arguments instead of the profile).Broader regression sweep:
npx vitest run src/store src/lib src/hermes.test.ts— 1020 tests pass, no regressions.npx tsc --noEmitclean (no circular-import issue from the new@/store/profileimport invoice-playback.ts).npx eslintandnpx prettier --checkclean on all changed files.New regression tests pass against the fix
New regression tests fail against pre-fix code (mutation-verify)
Broader
src/store+src/libsuite passes (1020 passed)tsc --noEmit,eslint,prettier --checkall clean