Conversation
This was referenced Sep 2, 2026
8 tasks
2 tasks
… on a local backend The install dialog gated the package path on the agent install succeeding in the same click (`agentInstalled && desktopHalfFromPackage`). A package already on disk answers "Plugin '<name>' already exists. Use force reinstall" without Force, so the retry fell through to installDesktopPlugin and cloned desktop-plugins/<git-name>/ beside the package copy the app had already materialised as desktop-plugins/<manifest-name>/. Two folders, one plugin id: the on-disk duplicate from NousResearch#100412 (since e1a6679 an error row instead of a competing live instance, but still created by the install flow). The package path now applies whenever the repo is a unified package, the backend is local and the Agent box is ticked, whatever the agent install returned. reconcileDesktopPlugins() is idempotent, so a retry touches nothing; the desktop success toast is raised only when the agent half landed or a copy was actually materialised, so a refused install shows the agent error alone. A failed fresh install no longer leaves a standalone desktop clone behind for the next successful install to duplicate. Remote backends keep the separate clone (their plugins/ folder is not readable from this machine); the Desktop-UI-only tick is unchanged. Fixes NousResearch#100412 (install-time half; runtime half landed in NousResearch#118902). Tests: two invariants in plugin-install-modal.test.tsx, the first red on main (installDesktopPlugin called on the refused retry), the second guarding the remote-backend clone.
ocuclaw
force-pushed
the
fix/desktop-hybrid-install-skip-dup
branch
from
September 22, 2026 09:25
6120f49 to
9fbd3a9
Compare
Author
|
Re-cut on main @ 836b5f8, one commit, force-pushed to the same branch; the 09-02 diff ( |
Merged
1 task
19 tasks
Author
|
Landed via #121475 ( |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
On a local backend a unified package's desktop half is served from the package copy on every install path, including the already-installed retry. The install flow no longer creates the second on-disk copy from #100412.
What main still did.
plugin-install-modal.tsx::handleInstallgates the package path onagentInstalled && desktopHalfFromPackage(main :250). On a no-Force retry of a package already on disk the agent install returnsPlugin '<name>' already exists. Use force reinstall(hermes_cli/plugins_cmd.py:789),agentInstalledstays false, and the modal falls through toinstallDesktopPlugin(main :263), which clonesdesktop-plugins/<git-name>/next to thedesktop-plugins/<manifest-name>/copy thatreconcileDesktopPlugins()already materialised. Two folders, one plugin id. Since #118902 (e1a6679399) the loader shows the second as an error row instead of running both, but the install flow still creates it whenever the repo's git name differs from its manifestname:(same-name retries are refused bydesktop-plugin-install.ts:410-414and create nothing). The comment at main :176-179 already states the invariant this violates: only a remote backend or a desktop-only repo needs a separate desktop clone.desktop-plugins/<git-name>/cloned beside<manifest-name>/Related Issue
Fixes #100412 (install-time half; the runtime half landed in #118902). Supersedes the install-side scope of #100485.
Type of Change
Changes Made
apps/desktop/src/app/settings/plugin-install-modal.tsx::handleInstall: the package path is taken wheneverdesktopHalfFromPackageholds, whatever the agent install returned;reconcileDesktopPlugins()is idempotent so a retry touches nothing. The desktop success toast is raised only when the agent half landed or a copy was actually materialised, so a refused install shows the agent error alone.apps/desktop/src/app/settings/plugin-install-modal.test.tsx: two invariant tests, see below.Validation
A/B against
origin/mainby swappingplugin-install-modal.tsx(cpswap, same test file):installDesktopPlugincalled)npx vitest run --project ui src/app/settings/plugin-install-modal.test.tsx: 8 passed.npm run check:lint(tsc x3 + eslint): 0 errors (208 pre-existing warnings, none in the touched files).npm --prefix apps/desktop run build: renderer builds,assert-dist-builtpasses.Live repro
Hermes Desktop built from
836b5f825with a local backend on the sameHERMES_HOME, fixture https://github.com/ocuclaw/hermes-hybrid-dup-repro (git namehermes-hybrid-dup-repro, manifest namehybrid-dup-repro), both boxes ticked, Install, then Install again without Force reinstall.836b5f8Desktop plugin hybrid-dup-repro installed;~/.hermes/desktop-plugins/=hybrid-dup-repro/with.hermes-package.json→~/.hermes/plugins/hybrid-dup-repro/desktopPlugin 'hybrid-dup-repro' already exists. Use force reinstall or run 'hermes plugins update hybrid-dup-repro'.;~/.hermes/desktop-plugins/=hermes-hybrid-dup-repro/(bare clone, no marker) andhybrid-dup-repro/~/.hermes/desktop-plugins/=hybrid-dup-repro/onlyhermes-hybrid-dup-repro[Desktop] [on disk] [failed]:duplicate id "hybrid-dup-repro", already loaded from …/desktop-plugins/hybrid-dup-repro/plugin.jshybrid-dup-repro v0.1.0 Agent + Desktop git~/.hermes/plugins/holds onehybrid-dup-repro/throughout in both runs.How to Test
plugin.yamlname:differs from the repo name (e.g. https://github.com/ocuclaw/hermes-hybrid-dup-repro: repohermes-hybrid-dup-repro, namehybrid-dup-repro). Settings → Plugins → Install from Git, both boxes ticked, Install.ls ~/.hermes/desktop-plugins/: one folder,hybrid-dup-repro/, with.hermes-package.json.hermes-hybrid-dup-repro/appears and Capabilities → Plugins shows aduplicate iderror row. After: still one folder, the dialog shows only the agent's "already exists" error.Not changed (out of scope, deliberately)
plugin.js(notdesktop/plugin.js) is not served by the package door (desktop-plugins-root.ts::materializeDesktopHalf); the dialog's package path was already taken for that shape on main. Separate report if wanted.hermes plugins installfrom the CLI after a differently-named standalone clone still yields a pair at reconcile time; the loader's error row covers it (Desktop plugin loader: hung imports time out, timers die on disable, duplicate ids error, broken reloads retire stale code #118902).6120f4910, still reachable) probed anagentPluginsRootIPC and exportedresolveDiskPluginEntry; both went away in feat(desktop): Plugins is one row per plugin; desktop halves are app-level, never profile-scoped #107314 (248ff2d3e8), which landed the happy path this PR was opened for, so that design is dropped rather than rebased.Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests pass: N/A, renderer-only change (vitest above)Documentation & Housekeeping
cli-config.yaml.exampleif I added/changed config keys: N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows: N/A