fix(desktop): deduplicate Bots roster and reuse canonical chats - #88341
fix(desktop): deduplicate Bots roster and reuse canonical chats#88341frizikk wants to merge 1 commit into
Conversation
|
Good catch on the UI half — this pairs with #88331, which fixes the other half of #88268 at the DB layer (per-job run-history pruning so per-tick cron rows stop growing state.db and feeding the roster). Different layers, no overlap: yours dedups what's shown, ours bounds what's stored. Reviewers can take them independently. |
0282895 to
a7bfe98
Compare
|
Current-main overlap check with #88328 and #88380: This PR contains two independently useful pieces, but its plugin-side roster merge now overlaps #88328. I applied #88328 and #88380 to current main The non-overlapping parts here still add value:
Recommendation: rebase after #88328, drop this PRs competing |
…source-qualified row keys Layers on the salvaged #88489 (@29206394) and #88341 (@frizikk): - sdk: host.activeConnectionId() — registry id of the LIVE active gateway. The salvaged fix classifies against the registry primary; after the user activates a non-primary source's agent, profiles.list answers from THAT source and primary-based matching would duplicate the active source's agents again. Live id wins, primaryConnectionId is the fallback, the legacy kind==='local' rule covers older desktops. - plugin: roster/chip/picker list keys are botRowKey(bot) — source-qualified (connectionId, name) — so same-named agents on two genuine sources can never collide as duplicate React keys (the render half of the dupe-bots smear: name-keyed rows + duplicate names = repeated blocks every poll). Annotated active-source rows keep the plain-name key, so nothing remounts when a desktop gains the union roster. - tests: live-id-beats-primary regression, botRowKey stability, source-shape anchor refresh.
…source-qualified row keys Layers on the salvaged #88489 (@29206394) and #88341 (@frizikk): - sdk: host.activeConnectionId() — registry id of the LIVE active gateway. The salvaged fix classifies against the registry primary; after the user activates a non-primary source's agent, profiles.list answers from THAT source and primary-based matching would duplicate the active source's agents again. Live id wins, primaryConnectionId is the fallback, the legacy kind==='local' rule covers older desktops. - plugin: roster/chip/picker list keys are botRowKey(bot) — source-qualified (connectionId, name) — so same-named agents on two genuine sources can never collide as duplicate React keys (the render half of the dupe-bots smear: name-keyed rows + duplicate names = repeated blocks every poll). Annotated active-source rows keep the plain-name key, so nothing remounts when a desktop gains the union roster. - tests: live-id-beats-primary regression, botRowKey stability, source-shape anchor refresh.
|
Merged via #88523 — your commit was cherry-picked with authorship preserved (7dd945b on main): the electron-side |
…source-qualified row keys Layers on the salvaged NousResearch#88489 (@29206394) and NousResearch#88341 (@frizikk): - sdk: host.activeConnectionId() — registry id of the LIVE active gateway. The salvaged fix classifies against the registry primary; after the user activates a non-primary source's agent, profiles.list answers from THAT source and primary-based matching would duplicate the active source's agents again. Live id wins, primaryConnectionId is the fallback, the legacy kind==='local' rule covers older desktops. - plugin: roster/chip/picker list keys are botRowKey(bot) — source-qualified (connectionId, name) — so same-named agents on two genuine sources can never collide as duplicate React keys (the render half of the dupe-bots smear: name-keyed rows + duplicate names = repeated blocks every poll). Annotated active-source rows keep the plain-name key, so nothing remounts when a desktop gains the union roster. - tests: live-id-beats-primary regression, botRowKey stability, source-shape anchor refresh.
…source-qualified row keys Layers on the salvaged NousResearch#88489 (@29206394) and NousResearch#88341 (@frizikk): - sdk: host.activeConnectionId() — registry id of the LIVE active gateway. The salvaged fix classifies against the registry primary; after the user activates a non-primary source's agent, profiles.list answers from THAT source and primary-based matching would duplicate the active source's agents again. Live id wins, primaryConnectionId is the fallback, the legacy kind==='local' rule covers older desktops. - plugin: roster/chip/picker list keys are botRowKey(bot) — source-qualified (connectionId, name) — so same-named agents on two genuine sources can never collide as duplicate React keys (the render half of the dupe-bots smear: name-keyed rows + duplicate names = repeated blocks every poll). Annotated active-source rows keep the plain-name key, so nothing remounts when a desktop gains the union roster. - tests: live-id-beats-primary regression, botRowKey stability, source-shape anchor refresh.
…source-qualified row keys Layers on the salvaged NousResearch#88489 (@29206394) and NousResearch#88341 (@frizikk): - sdk: host.activeConnectionId() — registry id of the LIVE active gateway. The salvaged fix classifies against the registry primary; after the user activates a non-primary source's agent, profiles.list answers from THAT source and primary-based matching would duplicate the active source's agents again. Live id wins, primaryConnectionId is the fallback, the legacy kind==='local' rule covers older desktops. - plugin: roster/chip/picker list keys are botRowKey(bot) — source-qualified (connectionId, name) — so same-named agents on two genuine sources can never collide as duplicate React keys (the render half of the dupe-bots smear: name-keyed rows + duplicate names = repeated blocks every poll). Annotated active-source rows keep the plain-name key, so nothing remounts when a desktop gains the union roster. - tests: live-id-beats-primary regression, botRowKey stability, source-shape anchor refresh.
What does this PR do?
Fixes the Desktop BOTS pane creating repeated rows for one agent identity and avoids creating an extra canonical session on first open.
The roster is now deduplicated by
(connectionId, profile)in both the Electron source and the plugin merger. When a bot has no stored Desktop pin, the canonical opener first adopts an existingBot Chatcreated through CLI/A2A instead of immediately minting another session.Related Issue
Fixes #88340
Related: #88200 (adjacent preview/click routing issue; not closed by this PR).
Type of Change
Changes Made
Bot Chatbefore creating a new canonical chat without a saved UI pin.How to Test
npm --workspace apps/desktop exec vitest run electron/connection-registry.test.ts.npm --workspace apps/desktop run check:test:plugins.defaultreports for the same connection and verify a single@default-this-devicerow remains.Bot Chatwithout Desktopui_meta.chat, then open that bot from BOTS and verify the existing session is opened rather than a new one created.Checklist
Verification
npm --workspace apps/desktop exec vitest run electron/connection-registry.test.ts— 44 passed.npm --workspace apps/desktop run check:test:plugins— 151 passed.npm --workspace apps/desktop run typecheck— passed.git diff --check— passed.npm --workspace apps/desktop run test:uihas four unrelated failures on this branch, reproduced unchanged on cleanorigin/main: locale formatting expectations inuse-prompt-actions/utils.test.tsandbilling/index.test.tsx, plus a truncation-label expectation infallback-model.test.ts.