NousResearch/hermes-agent

Desktop app auto-previews links causing unwanted side effects

Cluster 47500 · apps/desktop. Data as of

1 open item. Fix merged in #49515 on 20 Jun 2026. Check it covers your case.

The short answer

1open issue
0open PRs
3items grouped
Known?
Yes. 3 related items grouped here, 1 issue already closed.
Fix event
A fix was merged to main in PR #49515. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this partially resolved, deciding PR #49515. That is separate from the fix event.
Still open
1 issue and 0 PRs.

These issues concern the desktop app automatically previewing or loading external links in chat, which can trigger unintended behaviors such as audio autoplay and custom protocol handler activation.

Open issues1

  • Issue#47500[Bug]: Desktop app auto-previews external links in chat, triggering custom protocol handlers (e.g. bitbrowser://)

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #49515 fix(desktop): mute hidden link-title window so historical links don't autoplay audio

    merged to main

    files: apps/desktop/electron/link-title-window.cjs, apps/desktop/electron/link-title-window.test.cjs, apps/desktop/electron/main.cjs, apps/desktop/package.json

    Show the diff
    • apps/desktop/electron/link-title-window.cjs
      @@ -0,0 +1,42 @@
      +'use strict'
      +
      +// Hidden BrowserWindow used by tier-2 link-title resolution: when curl can't
      +// read a page <title> (bot walls, JS-rendered pages), we briefly load the URL
      +// in an offscreen window and read its title. That window loads arbitrary
      +// user-linked pages — including YouTube/`watch` URLs that autoplay — so it must
      +// never be allowed to emit sound.
      +
      +function linkTitleWindowOptions(partitionSession) {
      +  return {
      +    show: false,
      +    width: 1280,
      +    height: 800,
      +    webPreferences: {
      +      backgroundThrottling: false,
      +      contextIsolation: true,
      +      javascript: true,
      +      nodeIntegration: false,
      +      sandbox: true,
      +      session: partitionSession,
      +      webSecurity: true
      +    }
      +  }
      +}
      +
      +// Create the offscreen title-fetch window and immediately mute it. Without the
      +// mute, autoplaying media on the loaded page (e.g. a YouTube link) leaks ~2s of
      +// audio every time a session containing such links is re-rendered. See #49505.
      +function createLinkTitleWindow(BrowserWindow, partitionSession) {
      +  const window = new BrowserWindow(linkTitleWindowOptions(partitionSession))
      +
      +  try {
      +    window.webContents.setAudioMuted(true)
      +  } catch {
      +    // webContents may be unavailable in degraded/headless environments; muting
      +    // is best-effort and the window is destroyed within a few seconds anyway.
      +  }
      +
      +  return window
      +}
      +
      +module.exports = { createLinkTitleWindow, linkTitleWindowOptions }
    • apps/desktop/electron/link-title-window.test.cjs
      @@ -0,0 +1,56 @@
      +const assert = require('node:assert/strict')
      +const test = require('node:test')
      +
      +const { createLinkTitleWindow, linkTitleWindowOptions } = require('./link-title-window.cjs')
      +
      +function makeFakeBrowserWindow() {
      +  const calls = { audioMuted: [] }
      +  const FakeBrowserWindow = function (options) {
      +    this.options = options
      +    this.webContents = {
      +      setAudioMuted(value) {
      +        calls.audioMuted.push(value)
      +      }
      +    }
      +  }
      +
      +  return { FakeBrowserWindow, calls }
      +}
      +
      +test('linkTitleWindowOptions keeps the offscreen, hardened defaults', () => {
      +  const session = { id: 'link-titles' }
      +  const options = linkTitleWindowOptions(session)
      +
      +  assert.equal(options.show, false)
      +  assert.equal(options.webPreferences.session, session)
      +  assert.equal(options.webPreferences.contextIsolation, true)
      +  assert.equal(options.webPreferences.sandbox, true)
      +  assert.equal(options.webPreferences.nodeIntegration, false)
      +})
      +
      +test('createLinkTitleWindow mutes audio so historical links never autoplay sound', () => {
      +  // Regression for #49505: the hidden title-fetch window loaded YouTube/watch
      +  // URLs (to read their <title>) without muting, leaking ~2s of audio on every
      +  // history re-render.
      +  const { FakeBrowserWindow, calls } = makeFakeBrowserWindow()
      +
      +  const window = createLinkTitleWindow(FakeBrowserWindow, { id: 'link-titles' })
      +
      +  assert.ok(window instanceof FakeBrowserWindow)
      +  assert.deepEqual(calls.audioMuted, [true])
      +})
      +
      +test('createLinkTitleWindow still returns the window if muting throws', () => {
      +  const ThrowingBrowserWindow = function (options) {
      +    this.options = options
      +    this.webContents = {
      +      setAudioMuted() {
      +        throw new Error('webContents unavailable')
      +      }
      +    }
      +  }
      +
      +  const window = createLinkTitleWindow(ThrowingBrowserWindow, { id: 'link-titles' })
      +
      +  assert.ok(window instanceof ThrowingBrowserWindow)
      +})
    • apps/desktop/electron/main.cjs
      @@ -34,6 +34,7 @@ const {
         SESSION_WINDOW_MIN_WIDTH
       } = require('./session-windows.cjs')
       const { canImportHermesCli, verifyHermesCli } = require('./backend-probes.cjs')
      +const { createLinkTitleWindow } = require('./link-title-window.cjs')
       const { probeGatewayWebSocket } = require('./gateway-ws-probe.cjs')
       const { adoptServedDashboardToken } = require('./dashboard-token.cjs')
       const { waitForDashboardPort } = require('./backend-ready.cjs')
      @@ -2980,20 +2981,7 @@ function runRenderTitleJob(rawUrl) {
           }
       
           try {
      -      window = new BrowserWindow({
      -        show: false,
      -        width: 1280,
      -        height: 800,
      -        webPreferences: {
      -          backgroundThrottling: false,
      -          contextIsolation: true,
      -          javascript: true,
      -          nodeIntegration: false,
      -          sandbox: true,
      -          session: partitionSession,
      -          webSecurity: true
      -        }
      -      })
      +      window = createLinkTitleWindow(BrowserWindow, partitionSession)
           } catch {
             return finish('')
           }
    • apps/desktop/package.json
      @@ -37,7 +37,7 @@
           "test:desktop:nsis": "node scripts/test-desktop.mjs nsis",
           "test:desktop:existing": "node scripts/test-desktop.mjs existing",
           "test:desktop:fresh": "node scripts/test-desktop.mjs fresh",
      -    "test:desktop:platforms": "node --test electron/bootstrap-platform.test.cjs electron/hardening.test.cjs electron/backend-env.test.cjs electron/backend-probes.test.cjs electron/bootstrap-runner.test.cjs electron/connection-config.test.cjs electron/dashboard-token.test.cjs electron/gateway-ws-probe.test.cjs electron/oauth-net-request.test.cjs electron/desktop-uninstall.test.cjs electron/session-windows.test.cjs electron/workspace-cwd.test.cjs electron/fs-read-dir.test.cjs electron/git-root.test.cjs electron/windows-child-process.test.cjs electron/update-remote.test.cjs electron/update-rebuild.test.cjs electron/windows-user-env.test.cjs",
      +    "test:desktop:platforms": "node --test electron/bootstrap-platform.test.cjs electron/hardening.test.cjs electron/backend-env.test.cjs electron/backend-probes.test.cjs electron/bootstrap-runner.test.cjs electron/connection-config.test.cjs electron/dashboard-token.test.cjs electron/gateway-ws-probe.test.cjs electron/oauth-net-request.test.cjs electron/desktop-uninstall.test.cjs electron/session-windows.test.cjs electron/link-title-window.test.cjs electron/workspace-cwd.test.cjs electron/fs-read-dir.test.cjs electron/git-root.test.cjs electron/windows-child-process.test.cjs electron/update-remote.test.cjs electron/update-rebuild.test.cjs electron/windows-user-env.test.cjs",
           "typecheck": "tsc -p . --noEmit",
           "lint": "eslint src/ electron/",
           "lint:fix": "eslint src/ electron/ --fix",
Closed issues · 1
  • Closed#49505[Bug]: Desktop app auto-plays audio from YouTube links
How this cluster was grouped
  • Link evidence (partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
  • Members outside that structural core may belong to a different underlying problem.
  • Pairwise link density 0.45: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
apps/desktop
Merged PRs
#49515
Closed issues
1
Attempts
0 PRs closed without merging
Fix link checked
20 Jun 2026

For your agent

The same facts, typed.