hermes-agent/apps/desktop/electron/link-title-window.cjs
Brooklyn Nicholson 42bc07d107 fix(desktop): cancel downloads triggered by link-title fetch window
The hidden BrowserWindow used by fetchLinkTitle to scrape page titles
had no will-download handler on its session.  When a link artifact URL
responds with Content-Disposition: attachment, Electron fires will-download
and the file is saved for real — explaining the spurious download on the
Artifacts page.

Add guardLinkTitleSession() (parallel to the existing audio-mute guard for
#49505) that installs a will-download handler which immediately cancels
every download item on the hermes:link-titles session.  Call it from
getLinkTitleSession() right after the request-type blocklist is wired up.
2026-07-03 01:46:27 -05:00

52 lines
1.8 KiB
JavaScript

'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, so it must never emit sound or trigger real downloads.
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
}
// Cancel any download the title-fetch window triggers. Without this, a link
// artifact URL served with Content-Disposition: attachment auto-downloads every
// time the Artifacts page renders and fetchLinkTitle loads it.
function guardLinkTitleSession(partitionSession) {
try {
partitionSession.on('will-download', (_event, item) => item.cancel())
} catch {
// best-effort; worst case is a spurious download
}
}
module.exports = { createLinkTitleWindow, guardLinkTitleSession, linkTitleWindowOptions }