Skip to content

fix(desktop): isolate runtime plugin render hooks - #82763

Merged
teknium1 merged 1 commit into
NousResearch:mainfrom
embwl0x:agent/plugin-hook-boundary
Aug 10, 2026
Merged

teknium1 merged 1 commit into
NousResearch:mainfrom
embwl0x:agent/plugin-hook-boundary

Conversation

@embwl0x

@embwl0x embwl0x commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • mount runtime contribution render callbacks as real React child components
  • apply the boundary consistently to slots, chat routes, route tiles, panes, and statusbar contributions
  • add hot-registration and render-failure regressions

Root cause

Desktop hosts called plugin render() callbacks while rendering the host component. A hook used by a plugin therefore became a hook of the host. Loading or replacing a plugin changed the host's hook count and could trigger React error #310. It also meant a render-time plugin exception occurred before ContribBoundary received its child, so the boundary could not contain the failure.

ContribRender passes the callback to createElement, giving each contribution its own component boundary. Replacing a callback now remounts that child instead of changing the host's hook order, and plugin exceptions occur below ContribBoundary.

I audited every production .render() contribution call under apps/desktop/src; no direct callback invocation remains.

Fixes #80560

Verification

  • npx vitest run --project ui src/contrib/react/slot.test.tsx - 2 passed
  • npm run typecheck - passed
  • npm run lint - 0 errors (89 pre-existing warnings)
  • npm run build - passed, including assert-dist-built
  • git diff --check origin/main...HEAD - passed
  • contribution publish gate and gitleaks - passed

The full Desktop UI run completed with 3,624 passing tests and 8 aggregate timeout/order failures in four unrelated existing files. Each affected file passes in a fresh process (19/19 total); the intermittent messaging file also passes on untouched exact-base main (7/7).

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) comp/plugins Plugin system and bundled plugins platform/windows Native Windows-specific behavior or breakage sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows labels Aug 9, 2026
@teknium1
teknium1 merged commit 8edcdd1 into NousResearch:main Aug 10, 2026
38 checks passed
teknium1 added a commit that referenced this pull request Aug 11, 2026
…nder() isolation class

Three-part class closure for the React #310 / lost-renderer-crash family
(#79428, follow-up to #80560 / #82763):

1. Diagnosability (#79428 defect B): error-boundary catches now persist to
   desktop.log with their component stack via a new fire-and-forget
   hermes:logs:renderer-error IPC (synchronous flush — the window may be
   dying). Every renderer-content window (main, secondary session, instance,
   HUD, quick entry, pet overlay) gets the error-level console capture that
   previously only the main window had, labeled per window. 'Open logs' on
   the crash dialog now reveals a file that actually contains the crash.

2. Recurrence guard: eslint no-restricted-syntax rule banning inline
   render() calls in JSX — the mechanism behind #80560. The rule
   immediately caught two live sites #82763's audit missed (floating
   panes, narrow-overlay reveal), both hosting plugin panes.

3. Fix those two missed sites with the same ContribRender mount.

extracted console-capture/report formatting to electron/renderer-log.ts
with unit tests; renderer console lines now carry the window label.
blut-agent pushed a commit to blut-agent/hermes-agent-fork that referenced this pull request Aug 11, 2026
…nder() isolation class

Three-part class closure for the React NousResearch#310 / lost-renderer-crash family
(NousResearch#79428, follow-up to NousResearch#80560 / NousResearch#82763):

1. Diagnosability (NousResearch#79428 defect B): error-boundary catches now persist to
   desktop.log with their component stack via a new fire-and-forget
   hermes:logs:renderer-error IPC (synchronous flush — the window may be
   dying). Every renderer-content window (main, secondary session, instance,
   HUD, quick entry, pet overlay) gets the error-level console capture that
   previously only the main window had, labeled per window. 'Open logs' on
   the crash dialog now reveals a file that actually contains the crash.

2. Recurrence guard: eslint no-restricted-syntax rule banning inline
   render() calls in JSX — the mechanism behind NousResearch#80560. The rule
   immediately caught two live sites NousResearch#82763's audit missed (floating
   panes, narrow-overlay reveal), both hosting plugin panes.

3. Fix those two missed sites with the same ContribRender mount.

extracted console-capture/report formatting to electron/renderer-log.ts
with unit tests; renderer console lines now carry the window label.
skappafrost pushed a commit to skappafrost/hermes-agent that referenced this pull request Aug 15, 2026
…nder() isolation class

Three-part class closure for the React NousResearch#310 / lost-renderer-crash family
(NousResearch#79428, follow-up to NousResearch#80560 / NousResearch#82763):

1. Diagnosability (NousResearch#79428 defect B): error-boundary catches now persist to
   desktop.log with their component stack via a new fire-and-forget
   hermes:logs:renderer-error IPC (synchronous flush — the window may be
   dying). Every renderer-content window (main, secondary session, instance,
   HUD, quick entry, pet overlay) gets the error-level console capture that
   previously only the main window had, labeled per window. 'Open logs' on
   the crash dialog now reveals a file that actually contains the crash.

2. Recurrence guard: eslint no-restricted-syntax rule banning inline
   render() calls in JSX — the mechanism behind NousResearch#80560. The rule
   immediately caught two live sites NousResearch#82763's audit missed (floating
   panes, narrow-overlay reveal), both hosting plugin panes.

3. Fix those two missed sites with the same ContribRender mount.

extracted console-capture/report formatting to electron/renderer-log.ts
with unit tests; renderer console lines now carry the window label.
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
…nder() isolation class

Three-part class closure for the React NousResearch#310 / lost-renderer-crash family
(NousResearch#79428, follow-up to NousResearch#80560 / NousResearch#82763):

1. Diagnosability (NousResearch#79428 defect B): error-boundary catches now persist to
   desktop.log with their component stack via a new fire-and-forget
   hermes:logs:renderer-error IPC (synchronous flush — the window may be
   dying). Every renderer-content window (main, secondary session, instance,
   HUD, quick entry, pet overlay) gets the error-level console capture that
   previously only the main window had, labeled per window. 'Open logs' on
   the crash dialog now reveals a file that actually contains the crash.

2. Recurrence guard: eslint no-restricted-syntax rule banning inline
   render() calls in JSX — the mechanism behind NousResearch#80560. The rule
   immediately caught two live sites NousResearch#82763's audit missed (floating
   panes, narrow-overlay reveal), both hosting plugin panes.

3. Fix those two missed sites with the same ContribRender mount.

extracted console-capture/report formatting to electron/renderer-log.ts
with unit tests; renderer console lines now carry the window label.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) comp/plugins Plugin system and bundled plugins P2 Medium — degraded but workaround exists platform/windows Native Windows-specific behavior or breakage sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plugin SDK crashes with React #310 when any desktop plugin is loaded on Windows (v0.20.0 / v2026.8.3)

3 participants