@quanta-intellect/vessel-browser 0.1.127 → 0.1.128

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/out/main/index.js CHANGED
@@ -120,6 +120,9 @@ let settings = null;
120
120
  let settingsIssues = [];
121
121
  let saveTimer = null;
122
122
  let saveDirty = false;
123
+ function isMissingFileError(err) {
124
+ return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
125
+ }
123
126
  function getUserDataPath() {
124
127
  if (typeof electron.app?.getPath === "function") {
125
128
  return electron.app.getPath("userData");
@@ -135,7 +138,8 @@ function getChatProviderSecretPath() {
135
138
  function canUseSafeStorage$1() {
136
139
  try {
137
140
  return electron.safeStorage.isEncryptionAvailable();
138
- } catch {
141
+ } catch (err) {
142
+ logger$t.warn("safeStorage.isEncryptionAvailable() failed, assuming unavailable:", err);
139
143
  return false;
140
144
  }
141
145
  }
@@ -143,7 +147,8 @@ function writePrivateFile(filePath2, data) {
143
147
  fs.writeFileSync(filePath2, data, { mode: 384 });
144
148
  try {
145
149
  fs.chmodSync(filePath2, 384);
146
- } catch {
150
+ } catch (err) {
151
+ logger$t.debug("Could not chmod private file (non-POSIX filesystem):", err);
147
152
  }
148
153
  }
149
154
  function assertSafeStorageAvailable() {
@@ -160,7 +165,10 @@ function readStoredProviderSecret() {
160
165
  if (parsed && typeof parsed === "object" && typeof parsed.providerId === "string" && typeof parsed.apiKey === "string") {
161
166
  return parsed;
162
167
  }
163
- } catch {
168
+ } catch (err) {
169
+ if (!isMissingFileError(err)) {
170
+ logger$t.warn("Could not read stored provider secret:", err);
171
+ }
164
172
  }
165
173
  return null;
166
174
  }
@@ -175,7 +183,10 @@ function writeStoredProviderSecret(secret) {
175
183
  function clearStoredProviderSecret() {
176
184
  try {
177
185
  fs.unlinkSync(getChatProviderSecretPath());
178
- } catch {
186
+ } catch (err) {
187
+ if (!isMissingFileError(err)) {
188
+ logger$t.warn("Could not delete provider secret file:", err);
189
+ }
179
190
  }
180
191
  }
181
192
  function getCodexTokensPath() {
@@ -190,7 +201,10 @@ function readStoredCodexTokens() {
190
201
  if (parsed && typeof parsed === "object" && typeof parsed.accessToken === "string" && typeof parsed.refreshToken === "string") {
191
202
  return parsed;
192
203
  }
193
- } catch {
204
+ } catch (err) {
205
+ if (!isMissingFileError(err)) {
206
+ logger$t.warn("Could not read stored Codex tokens:", err);
207
+ }
194
208
  }
195
209
  return null;
196
210
  }
@@ -205,7 +219,10 @@ function writeStoredCodexTokens(tokens) {
205
219
  function clearStoredCodexTokens() {
206
220
  try {
207
221
  fs.unlinkSync(getCodexTokensPath());
208
- } catch {
222
+ } catch (err) {
223
+ if (!isMissingFileError(err)) {
224
+ logger$t.warn("Could not delete Codex token file:", err);
225
+ }
209
226
  }
210
227
  }
211
228
  function mergeChatProviderSecret(provider) {
@@ -494,6 +511,15 @@ function loadTrustedAppURL(wc, url) {
494
511
  }
495
512
  return wc.loadURL(parsed.toString());
496
513
  }
514
+ const urlSafety = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
515
+ __proto__: null,
516
+ assertPermittedNavigationURL,
517
+ assertSafeURL,
518
+ isSafeNavigationURL,
519
+ loadInternalDataURL,
520
+ loadPermittedNavigationURL,
521
+ loadTrustedAppURL
522
+ }, Symbol.toStringTag, { value: "Module" }));
497
523
  const MAX_CUSTOM_HISTORY = 50;
498
524
  const READER_MODE_DATA_URL_PREFIX = "data:text/html;charset=utf-8,";
499
525
  const logger$s = createLogger("Tab");
@@ -2915,13 +2941,8 @@ function destroySession(tabId) {
2915
2941
  }
2916
2942
  }
2917
2943
  const logger$q = createLogger("TabManager");
2918
- function sanitizePdfFilename(title) {
2944
+ function sanitizeFilename(title, ext) {
2919
2945
  const clean = title.replace(/[<>:"/\\|?*\x00-\x1f]/g, " ").replace(/\s+/g, " ").trim();
2920
- const base = (clean || "Vessel Page").replace(/\.pdf$/i, "");
2921
- return `${base}.pdf`;
2922
- }
2923
- function sanitizePageFilename(title, ext) {
2924
- const clean = title.replace(/[<>:\"/\\|?*\x00-\x1f]/g, " ").replace(/\s+/g, " ").trim();
2925
2946
  const escapedExt = ext.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2926
2947
  const regex = new RegExp(`\\.${escapedExt}$`, "i");
2927
2948
  const base = (clean || "Vessel Page").replace(regex, "");
@@ -3150,7 +3171,7 @@ class TabManager {
3150
3171
  if (!tab) return null;
3151
3172
  const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
3152
3173
  title: "Save Page as PDF",
3153
- defaultPath: sanitizePdfFilename(tab.state.title || "Vessel Page"),
3174
+ defaultPath: sanitizeFilename(tab.state.title || "Vessel Page", "pdf"),
3154
3175
  filters: [{ name: "PDF", extensions: ["pdf"] }]
3155
3176
  });
3156
3177
  if (canceled || !filePath2) return null;
@@ -3166,7 +3187,7 @@ class TabManager {
3166
3187
  const ext = format === "MHTML" ? "mhtml" : "html";
3167
3188
  const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
3168
3189
  title: "Save Page As",
3169
- defaultPath: sanitizePageFilename(
3190
+ defaultPath: sanitizeFilename(
3170
3191
  tab.state.title || "Vessel Page",
3171
3192
  ext
3172
3193
  ),
@@ -5321,7 +5342,7 @@ function getOfferPrice(offers) {
5321
5342
  const offer = asStructuredObject(offers);
5322
5343
  return firstStructuredString(offer?.price);
5323
5344
  }
5324
- function extractPrimaryEntity(pageType, structuredData, metaTags) {
5345
+ function extractPrimaryEntity(pageType, structuredData, _metaTags) {
5325
5346
  if (pageType === "product") {
5326
5347
  const product = structuredData?.find(
5327
5348
  (e) => e.types.some((t) => /^product$/i.test(t))
@@ -6406,7 +6427,7 @@ async function extractContentInner(webContents) {
6406
6427
  webContents
6407
6428
  );
6408
6429
  }
6409
- async function extractContent(webContents) {
6430
+ async function extractContent$1(webContents) {
6410
6431
  const cacheKey = `${webContents.id}:${webContents.getURL() || ""}`;
6411
6432
  const cached = extractionCache.get(cacheKey);
6412
6433
  if (cached) {
@@ -6624,7 +6645,7 @@ async function capturePageSnapshot(url, wc, sendToRendererViews) {
6624
6645
  if (!shouldTrackSnapshotUrl(url)) return;
6625
6646
  const key2 = normalizeUrl(url);
6626
6647
  const oldSnap = getSnapshot(key2);
6627
- const content = await extractContent(wc);
6648
+ const content = await extractContent$1(wc);
6628
6649
  const textContent = content.content || "";
6629
6650
  const title = content.title || "";
6630
6651
  const headings = content.headings || [];
@@ -6699,6 +6720,60 @@ function schedulePageSnapshotCapture(wc, sendToRendererViews, delayMs = 0, optio
6699
6720
  }
6700
6721
  scheduleTimerAt(wc, sendToRendererViews, nextDueAt, options);
6701
6722
  }
6723
+ const trustedIpcSenderIds = /* @__PURE__ */ new Set();
6724
+ function registerTrustedIpcSender(wc) {
6725
+ trustedIpcSenderIds.add(wc.id);
6726
+ wc.once("destroyed", () => trustedIpcSenderIds.delete(wc.id));
6727
+ }
6728
+ function assertTrustedIpcSender(event) {
6729
+ if (!trustedIpcSenderIds.has(event.sender.id)) {
6730
+ throw new Error("Blocked IPC from untrusted renderer");
6731
+ }
6732
+ }
6733
+ function isManagedTabIpcSender(event, tabManager) {
6734
+ return Boolean(tabManager.findTabByWebContentsId(event.sender.id));
6735
+ }
6736
+ function parseIpc(schema, value, label) {
6737
+ const result = schema.safeParse(value);
6738
+ if (!result.success) {
6739
+ const message = result.error.issues.map((i) => i.message).join("; ");
6740
+ throw new Error(
6741
+ label ? `Invalid ${label}: ${message}` : `Invalid input: ${message}`
6742
+ );
6743
+ }
6744
+ return result.data;
6745
+ }
6746
+ function assertString(value, name) {
6747
+ if (typeof value !== "string") throw new Error(`${name} must be a string`);
6748
+ }
6749
+ function assertOptionalString(value, name) {
6750
+ if (value !== void 0 && typeof value !== "string") {
6751
+ throw new Error(`${name} must be a string`);
6752
+ }
6753
+ }
6754
+ function assertNumber(value, name) {
6755
+ if (typeof value !== "number" || Number.isNaN(value)) {
6756
+ throw new Error(`${name} must be a number`);
6757
+ }
6758
+ }
6759
+ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
6760
+ function isValidEmail$1(value) {
6761
+ return EMAIL_RE.test(value.trim());
6762
+ }
6763
+ function getActiveTabInfo(tabManager) {
6764
+ const tab = tabManager.getActiveTab();
6765
+ if (!tab) return null;
6766
+ const wc = tab.view.webContents;
6767
+ if (wc.isDestroyed()) return null;
6768
+ return { tab, wc };
6769
+ }
6770
+ function sendSafe(wc, channel, ...args) {
6771
+ if (!wc || wc.isDestroyed()) return;
6772
+ try {
6773
+ wc.send(channel, ...args);
6774
+ } catch {
6775
+ }
6776
+ }
6702
6777
  function setSidebarPanelMode(state2, mode, reason = "user") {
6703
6778
  state2.uiState.sidebarPanelMode = mode;
6704
6779
  if (reason === "user") {
@@ -6741,15 +6816,8 @@ function getSidebarPanelState(state2) {
6741
6816
  }
6742
6817
  function emitSidebarPanelState(state2) {
6743
6818
  const panelState = getSidebarPanelState(state2);
6744
- if (!state2.chromeView.webContents.isDestroyed()) {
6745
- state2.chromeView.webContents.send(Channels.SIDEBAR_STATE_UPDATE, panelState);
6746
- }
6747
- if (!state2.sidebarView.webContents.isDestroyed()) {
6748
- state2.sidebarView.webContents.send(
6749
- Channels.SIDEBAR_STATE_UPDATE,
6750
- panelState
6751
- );
6752
- }
6819
+ sendSafe(state2.chromeView.webContents, Channels.SIDEBAR_STATE_UPDATE, panelState);
6820
+ sendSafe(state2.sidebarView.webContents, Channels.SIDEBAR_STATE_UPDATE, panelState);
6753
6821
  return panelState;
6754
6822
  }
6755
6823
  function isSidebarAttached(state2) {
@@ -7059,8 +7127,8 @@ function createMainWindow(onTabStateChange) {
7059
7127
  };
7060
7128
  const tabManager = new TabManager(mainWindow, onTabStateChange);
7061
7129
  const sendToRendererViews = (channel, ...args) => {
7062
- chromeView.webContents.send(channel, ...args);
7063
- sidebarView.webContents.send(channel, ...args);
7130
+ sendSafe(chromeView.webContents, channel, ...args);
7131
+ sendSafe(sidebarView.webContents, channel, ...args);
7064
7132
  };
7065
7133
  tabManager.onPageLoad((url, wc) => {
7066
7134
  const activeWc = tabManager.getActiveTab()?.view.webContents;
@@ -9681,60 +9749,6 @@ function createProvider(config) {
9681
9749
  }
9682
9750
  return new OpenAICompatProvider(normalized);
9683
9751
  }
9684
- const trustedIpcSenderIds = /* @__PURE__ */ new Set();
9685
- function registerTrustedIpcSender(wc) {
9686
- trustedIpcSenderIds.add(wc.id);
9687
- wc.once("destroyed", () => trustedIpcSenderIds.delete(wc.id));
9688
- }
9689
- function assertTrustedIpcSender(event) {
9690
- if (!trustedIpcSenderIds.has(event.sender.id)) {
9691
- throw new Error("Blocked IPC from untrusted renderer");
9692
- }
9693
- }
9694
- function isManagedTabIpcSender(event, tabManager) {
9695
- return Boolean(tabManager.findTabByWebContentsId(event.sender.id));
9696
- }
9697
- function parseIpc(schema, value, label) {
9698
- const result = schema.safeParse(value);
9699
- if (!result.success) {
9700
- const message = result.error.issues.map((i) => i.message).join("; ");
9701
- throw new Error(
9702
- label ? `Invalid ${label}: ${message}` : `Invalid input: ${message}`
9703
- );
9704
- }
9705
- return result.data;
9706
- }
9707
- function assertString(value, name) {
9708
- if (typeof value !== "string") throw new Error(`${name} must be a string`);
9709
- }
9710
- function assertOptionalString(value, name) {
9711
- if (value !== void 0 && typeof value !== "string") {
9712
- throw new Error(`${name} must be a string`);
9713
- }
9714
- }
9715
- function assertNumber(value, name) {
9716
- if (typeof value !== "number" || Number.isNaN(value)) {
9717
- throw new Error(`${name} must be a number`);
9718
- }
9719
- }
9720
- const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
9721
- function isValidEmail$1(value) {
9722
- return EMAIL_RE.test(value.trim());
9723
- }
9724
- function getActiveTabInfo(tabManager) {
9725
- const tab = tabManager.getActiveTab();
9726
- if (!tab) return null;
9727
- const wc = tab.view.webContents;
9728
- if (wc.isDestroyed()) return null;
9729
- return { tab, wc };
9730
- }
9731
- function sendSafe(wc, channel, ...args) {
9732
- if (!wc || wc.isDestroyed()) return;
9733
- try {
9734
- wc.send(channel, ...args);
9735
- } catch {
9736
- }
9737
- }
9738
9752
  function buildSubAgentSystemPrompt(thread) {
9739
9753
  const domainBlock = thread.blockedDomains.length > 0 ? `
9740
9754
  BLOCKED DOMAINS (never visit): ${thread.blockedDomains.join(", ")}` : "";
@@ -11740,7 +11754,7 @@ async function resolveSelector(wc, index, selector) {
11740
11754
  if (typeof fallbackSelector === "string" && fallbackSelector) {
11741
11755
  return fallbackSelector;
11742
11756
  }
11743
- const page = await extractContent(wc);
11757
+ const page = await extractContent$1(wc);
11744
11758
  const extractedSelector = findSelectorByIndex(page, index);
11745
11759
  if (extractedSelector) return extractedSelector;
11746
11760
  return null;
@@ -14318,6 +14332,312 @@ function cartOrigin(url) {
14318
14332
  return null;
14319
14333
  }
14320
14334
  }
14335
+ class TabMutex {
14336
+ queue = Promise.resolve();
14337
+ enqueue(fn) {
14338
+ const run = this.queue.then(fn, fn);
14339
+ this.queue = run.then(
14340
+ () => void 0,
14341
+ () => void 0
14342
+ );
14343
+ return run;
14344
+ }
14345
+ }
14346
+ const logger$j = createLogger("PageActions");
14347
+ const DEFAULT_PAGE_SCRIPT_TIMEOUT_MS = 1500;
14348
+ const PAGE_SCRIPT_TIMEOUT = /* @__PURE__ */ Symbol("page-script-timeout");
14349
+ function pageBusyError(action) {
14350
+ return `Error: Page is still busy; ${action} timed out waiting for page scripts. Retry in a moment.`;
14351
+ }
14352
+ async function loadPermittedUrl(wc, url) {
14353
+ const { assertPermittedNavigationURL: assertPermittedNavigationURL2 } = await Promise.resolve().then(() => urlSafety);
14354
+ assertPermittedNavigationURL2(url);
14355
+ await wc.loadURL(url);
14356
+ }
14357
+ async function executePageScript(wc, script, options) {
14358
+ if (wc.isDestroyed()) return null;
14359
+ const timeoutMs = Math.max(150, options?.timeoutMs ?? DEFAULT_PAGE_SCRIPT_TIMEOUT_MS);
14360
+ let timer = null;
14361
+ try {
14362
+ const result = await Promise.race([
14363
+ wc.executeJavaScript(script, options?.userGesture ?? false),
14364
+ new Promise((resolve) => {
14365
+ timer = setTimeout(() => resolve(PAGE_SCRIPT_TIMEOUT), timeoutMs);
14366
+ })
14367
+ ]);
14368
+ if (result === PAGE_SCRIPT_TIMEOUT) {
14369
+ return PAGE_SCRIPT_TIMEOUT;
14370
+ }
14371
+ return result;
14372
+ } catch (err) {
14373
+ const label = options?.label ? ` (${options.label})` : "";
14374
+ logger$j.warn(`Failed to execute page script${label}:`, err);
14375
+ return null;
14376
+ } finally {
14377
+ if (timer) {
14378
+ clearTimeout(timer);
14379
+ }
14380
+ }
14381
+ }
14382
+ async function waitForJsReady(wc, timeout = 8e3) {
14383
+ const start = Date.now();
14384
+ while (Date.now() - start < timeout) {
14385
+ const ready = await executePageScript(wc, "1", {
14386
+ timeoutMs: 250,
14387
+ userGesture: true,
14388
+ label: "js-ready probe"
14389
+ });
14390
+ if (ready === 1) return;
14391
+ await sleep(250);
14392
+ }
14393
+ }
14394
+ function waitForPotentialNavigation(wc, beforeUrl, timeout = 2500) {
14395
+ return waitForPotentialNavigation$1(wc, beforeUrl, timeout, QUIET_NAVIGATION_WINDOW_MS);
14396
+ }
14397
+ function getGlanceExtractScript() {
14398
+ return `(function() {
14399
+ var vw = window.innerWidth || document.documentElement.clientWidth || 0;
14400
+ var vh = window.innerHeight || document.documentElement.clientHeight || 0;
14401
+ var sy = window.scrollY || window.pageYOffset || 0;
14402
+
14403
+ function inViewport(el) {
14404
+ var r = el.getBoundingClientRect();
14405
+ return r.bottom > 0 && r.top < vh && r.right > 0 && r.left < vw && r.width > 0 && r.height > 0;
14406
+ }
14407
+
14408
+ function label(el) {
14409
+ return (el.getAttribute('aria-label') || el.textContent || '').trim().slice(0, 120);
14410
+ }
14411
+
14412
+ // Headings visible on screen
14413
+ var headings = [];
14414
+ document.querySelectorAll('h1, h2, h3, h4').forEach(function(h) {
14415
+ if (!inViewport(h)) return;
14416
+ var t = (h.textContent || '').trim();
14417
+ if (t && t.length < 200) headings.push(h.tagName.toLowerCase() + ': ' + t);
14418
+ });
14419
+
14420
+ // Links visible on screen (deduplicated by text)
14421
+ var links = [];
14422
+ var seenLinks = {};
14423
+ var idx = 1;
14424
+ document.querySelectorAll('a[href]').forEach(function(a) {
14425
+ if (!inViewport(a)) return;
14426
+ var t = (a.textContent || '').trim().slice(0, 100);
14427
+ if (!t || t.length < 2 || seenLinks[t]) return;
14428
+ seenLinks[t] = true;
14429
+ links.push({ text: t, href: (a.href || '').slice(0, 200), index: idx++ });
14430
+ });
14431
+
14432
+ // Buttons visible on screen
14433
+ var buttons = [];
14434
+ document.querySelectorAll('button, [role="button"], input[type="submit"], input[type="button"]').forEach(function(b) {
14435
+ if (!inViewport(b)) return;
14436
+ var t = label(b);
14437
+ if (!t || t.length < 1) return;
14438
+ buttons.push({ text: t, index: idx++ });
14439
+ });
14440
+
14441
+ // Input fields visible on screen
14442
+ var inputs = [];
14443
+ document.querySelectorAll('input:not([type="hidden"]):not([type="submit"]):not([type="button"]), select, textarea').forEach(function(inp) {
14444
+ if (!inViewport(inp)) return;
14445
+ var type = (inp.type || inp.tagName.toLowerCase() || '').toLowerCase();
14446
+ var lbl = (inp.getAttribute('aria-label') || inp.getAttribute('placeholder') || inp.name || '').trim();
14447
+ inputs.push({ type: type, label: lbl.slice(0, 80), placeholder: (inp.getAttribute('placeholder') || '').slice(0, 80), index: idx++ });
14448
+ });
14449
+
14450
+ // Content snapshot from main content area using textContent (instant, no reflow)
14451
+ var roots = ['main', 'article', '[role="main"]', '#content', '.content', '.story-body'];
14452
+ var contentRoot = null;
14453
+ for (var i = 0; i < roots.length; i++) {
14454
+ contentRoot = document.querySelector(roots[i]);
14455
+ if (contentRoot && contentRoot.textContent.trim().length > 50) break;
14456
+ contentRoot = null;
14457
+ }
14458
+ var snippet = '';
14459
+ if (contentRoot) {
14460
+ snippet = contentRoot.textContent.replace(/[ \\t]+/g, ' ').replace(/(\\n\\s*){3,}/g, '\\n\\n').trim().slice(0, 8000);
14461
+ } else {
14462
+ // Fallback: grab text from visible elements only
14463
+ var parts = [];
14464
+ document.querySelectorAll('h1, h2, h3, p, li, td, span, div').forEach(function(el) {
14465
+ if (parts.length > 100 || !inViewport(el)) return;
14466
+ var t = (el.textContent || '').trim();
14467
+ if (t.length > 10 && t.length < 500) parts.push(t);
14468
+ });
14469
+ snippet = parts.join('\\n').slice(0, 8000);
14470
+ }
14471
+
14472
+ return {
14473
+ title: document.title || '',
14474
+ url: location.href,
14475
+ headings: headings.slice(0, 20),
14476
+ links: links.slice(0, 40),
14477
+ buttons: buttons.slice(0, 20),
14478
+ inputs: inputs.slice(0, 15),
14479
+ contentSnippet: snippet,
14480
+ viewportHeight: vh,
14481
+ viewportWidth: vw,
14482
+ scrollY: Math.round(sy),
14483
+ };
14484
+ })()`;
14485
+ }
14486
+ async function glanceExtract(wc) {
14487
+ const startMs = Date.now();
14488
+ const result = await executePageScript(wc, getGlanceExtractScript(), { timeoutMs: 2500, label: "glance-extract" });
14489
+ const elapsed = Date.now() - startMs;
14490
+ if (!result || result === PAGE_SCRIPT_TIMEOUT) {
14491
+ return [
14492
+ `# ${wc.getTitle() || "(untitled)"}`,
14493
+ `URL: ${wc.getURL()}`,
14494
+ "",
14495
+ "[read_page mode=glance — page JS thread is completely blocked, no content available]",
14496
+ "[Try: click or type_text to interact directly, or wait a few seconds and retry]"
14497
+ ].join("\n");
14498
+ }
14499
+ const sections = [
14500
+ `# ${result.title}`,
14501
+ `URL: ${result.url}`,
14502
+ `Viewport: ${result.viewportWidth}×${result.viewportHeight} scrollY=${result.scrollY}`,
14503
+ `[read_page mode=glance — ${elapsed}ms, showing what's visible on screen]`
14504
+ ];
14505
+ if (result.headings.length > 0) {
14506
+ sections.push("", "## Headings", ...result.headings);
14507
+ }
14508
+ if (result.inputs.length > 0) {
14509
+ sections.push("", "## Input Fields");
14510
+ for (const inp of result.inputs) {
14511
+ const desc = inp.label || inp.placeholder || inp.type;
14512
+ sections.push(` [#${inp.index}] ${inp.type}: ${desc}`);
14513
+ }
14514
+ }
14515
+ if (result.buttons.length > 0) {
14516
+ sections.push("", "## Buttons");
14517
+ for (const btn of result.buttons) {
14518
+ sections.push(` [#${btn.index}] ${btn.text}`);
14519
+ }
14520
+ }
14521
+ if (result.links.length > 0) {
14522
+ sections.push("", "## Visible Links");
14523
+ for (const link of result.links) {
14524
+ sections.push(` [#${link.index}] ${link.text}`);
14525
+ }
14526
+ }
14527
+ if (result.contentSnippet) {
14528
+ const truncated = result.contentSnippet.length > 6e3 ? result.contentSnippet.slice(0, 6e3) + "\n[truncated]" : result.contentSnippet;
14529
+ sections.push("", "## Page Content (viewport)", "", truncated);
14530
+ }
14531
+ return sections.join("\n");
14532
+ }
14533
+ function normalizeReadPageMode(mode, pageContent) {
14534
+ if (typeof mode === "string") {
14535
+ const normalized = mode.trim().toLowerCase();
14536
+ if (normalized === "debug") return "debug";
14537
+ if (normalized === "glance") return "glance";
14538
+ if (normalized === "full" || normalized === "summary" || normalized === "interactives_only" || normalized === "forms_only" || normalized === "text_only" || normalized === "visible_only" || normalized === "results_only") {
14539
+ return normalized;
14540
+ }
14541
+ }
14542
+ return pageContent ? chooseAgentReadMode(pageContent) : "visible_only";
14543
+ }
14544
+ async function getPostNavSummary(wc) {
14545
+ const title = wc.getTitle();
14546
+ const titleLine = title ? `
14547
+ Page title: ${title}` : "";
14548
+ const overlaySignal = await executePageScript(
14549
+ wc,
14550
+ `(function() {
14551
+ var signals = [];
14552
+ var bodyStyle = window.getComputedStyle(document.body);
14553
+ var htmlStyle = window.getComputedStyle(document.documentElement);
14554
+ if (bodyStyle.overflow === 'hidden' || htmlStyle.overflow === 'hidden') {
14555
+ signals.push('body-scroll-locked');
14556
+ }
14557
+ var consentSelectors = [
14558
+ '#onetrust-consent-sdk', '#CybotCookiebotDialog', '[class*="consent-banner"]',
14559
+ '[class*="cookie-banner"]', '[class*="privacy-banner"]', '[id*="consent"]',
14560
+ '[class*="gdpr"]', '[data-testid*="consent"]', '[data-testid*="cookie"]',
14561
+ '.fc-consent-root', '#sp_message_container_', '[id*="trustarc"]',
14562
+ '[class*="cmp-"]', '[id*="cmp-"]'
14563
+ ];
14564
+ for (var i = 0; i < consentSelectors.length; i++) {
14565
+ try {
14566
+ var el = document.querySelector(consentSelectors[i]);
14567
+ if (el && el.offsetHeight > 50) {
14568
+ signals.push('consent-banner:' + consentSelectors[i]);
14569
+ break;
14570
+ }
14571
+ } catch(e) {}
14572
+ }
14573
+ var vw = window.innerWidth || 0;
14574
+ var vh = window.innerHeight || 0;
14575
+ var vpArea = Math.max(1, vw * vh);
14576
+ var els = document.querySelectorAll('dialog[open], [role="dialog"], [aria-modal="true"]');
14577
+ if (els.length > 0) signals.push('dialog-open');
14578
+ if (signals.length === 0) {
14579
+ var fixed = document.querySelectorAll('div[style*="position: fixed"], div[style*="position:fixed"]');
14580
+ for (var j = 0; j < fixed.length && j < 20; j++) {
14581
+ var r = fixed[j].getBoundingClientRect();
14582
+ if ((r.width * r.height) / vpArea > 0.3) {
14583
+ signals.push('large-fixed-overlay');
14584
+ break;
14585
+ }
14586
+ }
14587
+ }
14588
+ return signals.length > 0 ? signals.join(', ') : null;
14589
+ })()`,
14590
+ { timeoutMs: 1500, label: "overlay-probe" }
14591
+ );
14592
+ if (overlaySignal && overlaySignal !== PAGE_SCRIPT_TIMEOUT) {
14593
+ return `${titleLine}
14594
+ WARNING: Blocking overlay detected (${overlaySignal}). Call clear_overlays or accept_cookies before reading the page.`;
14595
+ }
14596
+ return titleLine;
14597
+ }
14598
+ async function getPostSearchSummary(wc) {
14599
+ await waitForLoad(wc, 2e3);
14600
+ try {
14601
+ const content = await Promise.race([
14602
+ extractContent$1(wc),
14603
+ new Promise((resolve) => setTimeout(() => resolve(null), 2500))
14604
+ ]);
14605
+ if (content && content.content.length > 0) {
14606
+ const scoped = buildScopedContext(content, "results_only");
14607
+ const truncated = scoped.length > 2600 ? `${scoped.slice(0, 2600)}
14608
+ [Search results snapshot truncated...]` : scoped;
14609
+ return `
14610
+ Search results snapshot:
14611
+ ${truncated}`;
14612
+ }
14613
+ } catch (err) {
14614
+ logger$j.warn("Failed to build post-search summary, falling back to nav summary:", err);
14615
+ }
14616
+ const fallback = await getPostNavSummary(wc);
14617
+ return fallback ? `${fallback}
14618
+ Search results snapshot unavailable. Use read_page(mode="results_only") if needed.` : `
14619
+ Search results snapshot unavailable. Use read_page(mode="results_only") if needed.`;
14620
+ }
14621
+ async function getPostClickNavSummary(wc, toolProfile) {
14622
+ try {
14623
+ const content = await Promise.race([
14624
+ extractContent$1(wc),
14625
+ new Promise((resolve) => setTimeout(() => resolve(null), 3e3))
14626
+ ]);
14627
+ if (content && content.content.length > 0) {
14628
+ const scoped = toolProfile === "compact" ? buildCompactScopedContext(content, "visible_only") : buildScopedContext(content, "visible_only");
14629
+ const maxLen = toolProfile === "compact" ? 1800 : 3e3;
14630
+ const truncated = scoped.length > maxLen ? `${scoped.slice(0, maxLen)}
14631
+ [Page snapshot truncated. Use read_page for full details.]` : scoped;
14632
+ return `
14633
+ Page snapshot after navigation:
14634
+ ${truncated}`;
14635
+ }
14636
+ } catch (err) {
14637
+ logger$j.warn("Failed to build post-click navigation summary:", err);
14638
+ }
14639
+ return "";
14640
+ }
14321
14641
  function resolveBookmarkFolderTarget$1(args) {
14322
14642
  const folderId = typeof args.folderId === "string" ? args.folderId.trim() : typeof args.folder_id === "string" ? args.folder_id.trim() : "";
14323
14643
  if (folderId) {
@@ -14706,18 +15026,6 @@ function buildDefaultEngineShortcut(rawQuery) {
14706
15026
  appliedFilters: []
14707
15027
  };
14708
15028
  }
14709
- class TabMutex {
14710
- queue = Promise.resolve();
14711
- enqueue(fn) {
14712
- const run = this.queue.then(fn, fn);
14713
- this.queue = run.then(
14714
- () => void 0,
14715
- () => void 0
14716
- );
14717
- return run;
14718
- }
14719
- }
14720
- const logger$j = createLogger("PageActions");
14721
15029
  function getBookmarkMetadataFromArgs(args) {
14722
15030
  return normalizeBookmarkMetadata({
14723
15031
  intent: args.intent ?? args.intent,
@@ -14726,311 +15034,6 @@ function getBookmarkMetadataFromArgs(args) {
14726
15034
  agentHints: args.agentHints ?? args.agent_hints
14727
15035
  });
14728
15036
  }
14729
- const DEFAULT_PAGE_SCRIPT_TIMEOUT_MS = 1500;
14730
- const PAGE_SCRIPT_TIMEOUT = /* @__PURE__ */ Symbol("page-script-timeout");
14731
- async function loadPermittedUrl(wc, url) {
14732
- assertPermittedNavigationURL(url);
14733
- await wc.loadURL(url);
14734
- }
14735
- function pageBusyError(action) {
14736
- return `Error: Page is still busy; ${action} timed out waiting for page scripts. Retry in a moment.`;
14737
- }
14738
- async function glanceExtract(wc) {
14739
- const startMs = Date.now();
14740
- const result = await executePageScript(
14741
- wc,
14742
- `(function() {
14743
- var vw = window.innerWidth || document.documentElement.clientWidth || 0;
14744
- var vh = window.innerHeight || document.documentElement.clientHeight || 0;
14745
- var sy = window.scrollY || window.pageYOffset || 0;
14746
-
14747
- function inViewport(el) {
14748
- var r = el.getBoundingClientRect();
14749
- return r.bottom > 0 && r.top < vh && r.right > 0 && r.left < vw && r.width > 0 && r.height > 0;
14750
- }
14751
-
14752
- function label(el) {
14753
- return (el.getAttribute('aria-label') || el.textContent || '').trim().slice(0, 120);
14754
- }
14755
-
14756
- // Headings visible on screen
14757
- var headings = [];
14758
- document.querySelectorAll('h1, h2, h3, h4').forEach(function(h) {
14759
- if (!inViewport(h)) return;
14760
- var t = (h.textContent || '').trim();
14761
- if (t && t.length < 200) headings.push(h.tagName.toLowerCase() + ': ' + t);
14762
- });
14763
-
14764
- // Links visible on screen (deduplicated by text)
14765
- var links = [];
14766
- var seenLinks = {};
14767
- var idx = 1;
14768
- document.querySelectorAll('a[href]').forEach(function(a) {
14769
- if (!inViewport(a)) return;
14770
- var t = (a.textContent || '').trim().slice(0, 100);
14771
- if (!t || t.length < 2 || seenLinks[t]) return;
14772
- seenLinks[t] = true;
14773
- links.push({ text: t, href: (a.href || '').slice(0, 200), index: idx++ });
14774
- });
14775
-
14776
- // Buttons visible on screen
14777
- var buttons = [];
14778
- document.querySelectorAll('button, [role="button"], input[type="submit"], input[type="button"]').forEach(function(b) {
14779
- if (!inViewport(b)) return;
14780
- var t = label(b);
14781
- if (!t || t.length < 1) return;
14782
- buttons.push({ text: t, index: idx++ });
14783
- });
14784
-
14785
- // Input fields visible on screen
14786
- var inputs = [];
14787
- document.querySelectorAll('input:not([type="hidden"]):not([type="submit"]):not([type="button"]), select, textarea').forEach(function(inp) {
14788
- if (!inViewport(inp)) return;
14789
- var type = (inp.type || inp.tagName.toLowerCase() || '').toLowerCase();
14790
- var lbl = (inp.getAttribute('aria-label') || inp.getAttribute('placeholder') || inp.name || '').trim();
14791
- inputs.push({ type: type, label: lbl.slice(0, 80), placeholder: (inp.getAttribute('placeholder') || '').slice(0, 80), index: idx++ });
14792
- });
14793
-
14794
- // Content snapshot from main content area using textContent (instant, no reflow)
14795
- var roots = ['main', 'article', '[role="main"]', '#content', '.content', '.story-body'];
14796
- var contentRoot = null;
14797
- for (var i = 0; i < roots.length; i++) {
14798
- contentRoot = document.querySelector(roots[i]);
14799
- if (contentRoot && contentRoot.textContent.trim().length > 50) break;
14800
- contentRoot = null;
14801
- }
14802
- var snippet = '';
14803
- if (contentRoot) {
14804
- snippet = contentRoot.textContent.replace(/[ \\t]+/g, ' ').replace(/(\\n\\s*){3,}/g, '\\n\\n').trim().slice(0, 8000);
14805
- } else {
14806
- // Fallback: grab text from visible elements only
14807
- var parts = [];
14808
- document.querySelectorAll('h1, h2, h3, p, li, td, span, div').forEach(function(el) {
14809
- if (parts.length > 100 || !inViewport(el)) return;
14810
- var t = (el.textContent || '').trim();
14811
- if (t.length > 10 && t.length < 500) parts.push(t);
14812
- });
14813
- snippet = parts.join('\\n').slice(0, 8000);
14814
- }
14815
-
14816
- return {
14817
- title: document.title || '',
14818
- url: location.href,
14819
- headings: headings.slice(0, 20),
14820
- links: links.slice(0, 40),
14821
- buttons: buttons.slice(0, 20),
14822
- inputs: inputs.slice(0, 15),
14823
- contentSnippet: snippet,
14824
- viewportHeight: vh,
14825
- viewportWidth: vw,
14826
- scrollY: Math.round(sy),
14827
- };
14828
- })()`,
14829
- { timeoutMs: 2500, label: "glance-extract" }
14830
- );
14831
- const elapsed = Date.now() - startMs;
14832
- if (!result || result === PAGE_SCRIPT_TIMEOUT) {
14833
- return [
14834
- `# ${wc.getTitle() || "(untitled)"}`,
14835
- `URL: ${wc.getURL()}`,
14836
- "",
14837
- "[read_page mode=glance — page JS thread is completely blocked, no content available]",
14838
- "[Try: click or type_text to interact directly, or wait a few seconds and retry]"
14839
- ].join("\n");
14840
- }
14841
- const sections = [
14842
- `# ${result.title}`,
14843
- `URL: ${result.url}`,
14844
- `Viewport: ${result.viewportWidth}×${result.viewportHeight} scrollY=${result.scrollY}`,
14845
- `[read_page mode=glance — ${elapsed}ms, showing what's visible on screen]`
14846
- ];
14847
- if (result.headings.length > 0) {
14848
- sections.push("", "## Headings", ...result.headings);
14849
- }
14850
- if (result.inputs.length > 0) {
14851
- sections.push("", "## Input Fields");
14852
- for (const inp of result.inputs) {
14853
- const desc = inp.label || inp.placeholder || inp.type;
14854
- sections.push(` [#${inp.index}] ${inp.type}: ${desc}`);
14855
- }
14856
- }
14857
- if (result.buttons.length > 0) {
14858
- sections.push("", "## Buttons");
14859
- for (const btn of result.buttons) {
14860
- sections.push(` [#${btn.index}] ${btn.text}`);
14861
- }
14862
- }
14863
- if (result.links.length > 0) {
14864
- sections.push("", "## Visible Links");
14865
- for (const link of result.links) {
14866
- sections.push(` [#${link.index}] ${link.text}`);
14867
- }
14868
- }
14869
- if (result.contentSnippet) {
14870
- const truncated = result.contentSnippet.length > 6e3 ? result.contentSnippet.slice(0, 6e3) + "\n[truncated]" : result.contentSnippet;
14871
- sections.push("", "## Page Content (viewport)", "", truncated);
14872
- }
14873
- return sections.join("\n");
14874
- }
14875
- function normalizeReadPageMode(mode, pageContent) {
14876
- if (typeof mode === "string") {
14877
- const normalized = mode.trim().toLowerCase();
14878
- if (normalized === "debug") return "debug";
14879
- if (normalized === "glance") return "glance";
14880
- if (normalized === "full" || normalized === "summary" || normalized === "interactives_only" || normalized === "forms_only" || normalized === "text_only" || normalized === "visible_only" || normalized === "results_only") {
14881
- return normalized;
14882
- }
14883
- }
14884
- return pageContent ? chooseAgentReadMode(pageContent) : "visible_only";
14885
- }
14886
- async function executePageScript(wc, script, options) {
14887
- if (wc.isDestroyed()) return null;
14888
- const timeoutMs = Math.max(
14889
- 150,
14890
- options?.timeoutMs ?? DEFAULT_PAGE_SCRIPT_TIMEOUT_MS
14891
- );
14892
- let timer = null;
14893
- try {
14894
- const result = await Promise.race([
14895
- wc.executeJavaScript(script, options?.userGesture ?? false),
14896
- new Promise((resolve) => {
14897
- timer = setTimeout(() => resolve(PAGE_SCRIPT_TIMEOUT), timeoutMs);
14898
- })
14899
- ]);
14900
- if (result === PAGE_SCRIPT_TIMEOUT) {
14901
- return PAGE_SCRIPT_TIMEOUT;
14902
- }
14903
- return result;
14904
- } catch (err) {
14905
- const label = options?.label ? ` (${options.label})` : "";
14906
- logger$j.warn(`Failed to execute page script${label}:`, err);
14907
- return null;
14908
- } finally {
14909
- if (timer) {
14910
- clearTimeout(timer);
14911
- }
14912
- }
14913
- }
14914
- async function waitForJsReady(wc, timeout = 8e3) {
14915
- const start = Date.now();
14916
- while (Date.now() - start < timeout) {
14917
- const ready = await executePageScript(wc, "1", {
14918
- timeoutMs: 250,
14919
- userGesture: true,
14920
- label: "js-ready probe"
14921
- });
14922
- if (ready === 1) return;
14923
- await sleep(250);
14924
- }
14925
- }
14926
- function waitForPotentialNavigation(wc, beforeUrl, timeout = 2500) {
14927
- return waitForPotentialNavigation$1(
14928
- wc,
14929
- beforeUrl,
14930
- timeout,
14931
- QUIET_NAVIGATION_WINDOW_MS
14932
- );
14933
- }
14934
- async function getPostNavSummary(wc) {
14935
- const title = wc.getTitle();
14936
- const titleLine = title ? `
14937
- Page title: ${title}` : "";
14938
- const overlaySignal = await executePageScript(
14939
- wc,
14940
- `(function() {
14941
- var signals = [];
14942
- // Body scroll lock is a strong overlay signal
14943
- var bodyStyle = window.getComputedStyle(document.body);
14944
- var htmlStyle = window.getComputedStyle(document.documentElement);
14945
- if (bodyStyle.overflow === 'hidden' || htmlStyle.overflow === 'hidden') {
14946
- signals.push('body-scroll-locked');
14947
- }
14948
- // Check for known consent manager containers
14949
- var consentSelectors = [
14950
- '#onetrust-consent-sdk', '#CybotCookiebotDialog', '[class*="consent-banner"]',
14951
- '[class*="cookie-banner"]', '[class*="privacy-banner"]', '[id*="consent"]',
14952
- '[class*="gdpr"]', '[data-testid*="consent"]', '[data-testid*="cookie"]',
14953
- '.fc-consent-root', '#sp_message_container_', '[id*="trustarc"]',
14954
- '[class*="cmp-"]', '[id*="cmp-"]'
14955
- ];
14956
- for (var i = 0; i < consentSelectors.length; i++) {
14957
- try {
14958
- var el = document.querySelector(consentSelectors[i]);
14959
- if (el && el.offsetHeight > 50) {
14960
- signals.push('consent-banner:' + consentSelectors[i]);
14961
- break;
14962
- }
14963
- } catch(e) {}
14964
- }
14965
- // Check for large fixed/sticky elements covering viewport
14966
- var vw = window.innerWidth || 0;
14967
- var vh = window.innerHeight || 0;
14968
- var vpArea = Math.max(1, vw * vh);
14969
- var els = document.querySelectorAll('dialog[open], [role="dialog"], [aria-modal="true"]');
14970
- if (els.length > 0) signals.push('dialog-open');
14971
- if (signals.length === 0) {
14972
- var fixed = document.querySelectorAll('div[style*="position: fixed"], div[style*="position:fixed"]');
14973
- for (var j = 0; j < fixed.length && j < 20; j++) {
14974
- var r = fixed[j].getBoundingClientRect();
14975
- if ((r.width * r.height) / vpArea > 0.3) {
14976
- signals.push('large-fixed-overlay');
14977
- break;
14978
- }
14979
- }
14980
- }
14981
- return signals.length > 0 ? signals.join(', ') : null;
14982
- })()`,
14983
- { timeoutMs: 1500, label: "overlay-probe" }
14984
- );
14985
- if (overlaySignal && overlaySignal !== PAGE_SCRIPT_TIMEOUT) {
14986
- return `${titleLine}
14987
- WARNING: Blocking overlay detected (${overlaySignal}). Call clear_overlays or accept_cookies before reading the page.`;
14988
- }
14989
- return titleLine;
14990
- }
14991
- async function getPostSearchSummary(wc) {
14992
- await waitForLoad(wc, 2e3);
14993
- try {
14994
- const content = await Promise.race([
14995
- extractContent(wc),
14996
- new Promise((resolve) => setTimeout(() => resolve(null), 2500))
14997
- ]);
14998
- if (content && content.content.length > 0) {
14999
- const scoped = buildScopedContext(content, "results_only");
15000
- const truncated = scoped.length > 2600 ? `${scoped.slice(0, 2600)}
15001
- [Search results snapshot truncated...]` : scoped;
15002
- return `
15003
- Search results snapshot:
15004
- ${truncated}`;
15005
- }
15006
- } catch (err) {
15007
- logger$j.warn("Failed to build post-search summary, falling back to nav summary:", err);
15008
- }
15009
- const fallback = await getPostNavSummary(wc);
15010
- return fallback ? `${fallback}
15011
- Search results snapshot unavailable. Use read_page(mode="results_only") if needed.` : `
15012
- Search results snapshot unavailable. Use read_page(mode="results_only") if needed.`;
15013
- }
15014
- async function getPostClickNavSummary(wc, toolProfile) {
15015
- try {
15016
- const content = await Promise.race([
15017
- extractContent(wc),
15018
- new Promise((resolve) => setTimeout(() => resolve(null), 3e3))
15019
- ]);
15020
- if (content && content.content.length > 0) {
15021
- const scoped = toolProfile === "compact" ? buildCompactScopedContext(content, "visible_only") : buildScopedContext(content, "visible_only");
15022
- const maxLen = toolProfile === "compact" ? 1800 : 3e3;
15023
- const truncated = scoped.length > maxLen ? `${scoped.slice(0, maxLen)}
15024
- [Page snapshot truncated. Use read_page for full details.]` : scoped;
15025
- return `
15026
- Page snapshot after navigation:
15027
- ${truncated}`;
15028
- }
15029
- } catch (err) {
15030
- logger$j.warn("Failed to build post-click navigation summary:", err);
15031
- }
15032
- return "";
15033
- }
15034
15037
  async function scrollPage(wc, deltaY) {
15035
15038
  const getScrollY = async () => {
15036
15039
  const scrollY = await executePageScript(
@@ -19886,7 +19889,7 @@ function parse(raw) {
19886
19889
  const items = Array.isArray(raw.items) ? raw.items : [];
19887
19890
  return { items };
19888
19891
  }
19889
- let state$2 = loadJsonFile({ filePath: filePath$1(), fallback: { items: [] }, parse });
19892
+ const state$2 = loadJsonFile({ filePath: filePath$1(), fallback: { items: [] }, parse });
19890
19893
  const persistence$2 = createDebouncedJsonPersistence({
19891
19894
  debounceMs: 250,
19892
19895
  filePath: filePath$1(),
@@ -20188,8 +20191,8 @@ function registerPrivateIpcHandlers(state2) {
20188
20191
  ipc.handle(Channels.OPEN_PRIVATE_WINDOW, () => {
20189
20192
  createPrivateWindow();
20190
20193
  });
20191
- ipc.handle(Channels.OPEN_NEW_WINDOW, () => {
20192
- const { createSecondaryWindow: createSecondaryWindow2 } = require("../secondary/window");
20194
+ ipc.handle(Channels.OPEN_NEW_WINDOW, async () => {
20195
+ const { createSecondaryWindow: createSecondaryWindow2 } = await Promise.resolve().then(() => window$1);
20193
20196
  createSecondaryWindow2();
20194
20197
  });
20195
20198
  ipc.handle(Channels.WINDOW_MINIMIZE, () => {
@@ -20254,9 +20257,7 @@ function createPrivateWindow() {
20254
20257
  const tabManager = new TabManager(
20255
20258
  win,
20256
20259
  (tabs, activeId) => {
20257
- if (!chromeView.webContents.isDestroyed()) {
20258
- chromeView.webContents.send(Channels.TAB_STATE_UPDATE, tabs, activeId);
20259
- }
20260
+ sendSafe(chromeView.webContents, Channels.TAB_STATE_UPDATE, tabs, activeId);
20260
20261
  layoutPrivateViews(state2);
20261
20262
  },
20262
20263
  { isPrivate: true, sessionPartition: privateSessionPartition }
@@ -20293,6 +20294,10 @@ function createPrivateWindow() {
20293
20294
  logger$h.info("Private browsing window opened");
20294
20295
  return state2;
20295
20296
  }
20297
+ const window$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
20298
+ __proto__: null,
20299
+ createPrivateWindow
20300
+ }, Symbol.toStringTag, { value: "Module" }));
20296
20301
  const secondaryWindows = /* @__PURE__ */ new Set();
20297
20302
  function layoutSecondaryViews(state2) {
20298
20303
  const { window: win, chromeView, tabManager } = state2;
@@ -20408,8 +20413,8 @@ function registerSecondaryIpcHandlers(state2) {
20408
20413
  (_e, groupId) => showGroupContextMenu(state2.tabManager, groupId, state2.window)
20409
20414
  );
20410
20415
  ipc.handle(Channels.OPEN_NEW_WINDOW, () => createSecondaryWindow());
20411
- ipc.handle(Channels.OPEN_PRIVATE_WINDOW, () => {
20412
- const { createPrivateWindow: createPrivateWindow2 } = require("../private/window");
20416
+ ipc.handle(Channels.OPEN_PRIVATE_WINDOW, async () => {
20417
+ const { createPrivateWindow: createPrivateWindow2 } = await Promise.resolve().then(() => window$2);
20413
20418
  createPrivateWindow2();
20414
20419
  });
20415
20420
  ipc.handle(Channels.IS_PRIVATE_MODE, () => false);
@@ -20461,9 +20466,7 @@ function createSecondaryWindow() {
20461
20466
  chromeView.setBackgroundColor("#00000000");
20462
20467
  win.contentView.addChildView(chromeView);
20463
20468
  const tabManager = new TabManager(win, (tabs, activeId) => {
20464
- if (!chromeView.webContents.isDestroyed()) {
20465
- chromeView.webContents.send(Channels.TAB_STATE_UPDATE, tabs, activeId);
20466
- }
20469
+ sendSafe(chromeView.webContents, Channels.TAB_STATE_UPDATE, tabs, activeId);
20467
20470
  layoutSecondaryViews(state2);
20468
20471
  });
20469
20472
  const state2 = { window: win, chromeView, tabManager };
@@ -20487,12 +20490,16 @@ function createSecondaryWindow() {
20487
20490
  win.show();
20488
20491
  return state2;
20489
20492
  }
20493
+ const window$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
20494
+ __proto__: null,
20495
+ createSecondaryWindow
20496
+ }, Symbol.toStringTag, { value: "Module" }));
20490
20497
  const TabIdSchema = zod.z.string().min(1);
20491
20498
  const GroupIdSchema = zod.z.string().min(1);
20492
20499
  const UrlSchema = zod.z.string().min(1);
20493
20500
  const ColorSchema = zod.z.string().min(1);
20494
20501
  const FindActionSchema = zod.z.enum(["clearSelection", "keepSelection", "activateSelection"]);
20495
- function registerTabHandlers(windowState, sendToRendererViews) {
20502
+ function registerTabHandlers(windowState, _sendToRendererViews) {
20496
20503
  const { tabManager, mainWindow } = windowState;
20497
20504
  electron.ipcMain.handle(Channels.OPEN_PRIVATE_WINDOW, (event) => {
20498
20505
  assertTrustedIpcSender(event);
@@ -21131,7 +21138,7 @@ async function handleAIQuery(query, provider, activeWebContents, onChunk, onEnd,
21131
21138
  const isSummarize = lowerQuery.startsWith("summarize") || lowerQuery.startsWith("tldr") || lowerQuery === "summary";
21132
21139
  if (provider.streamAgentQuery && tabManager && activeWebContents && runtime2) {
21133
21140
  try {
21134
- const pageContent = await extractContent(activeWebContents);
21141
+ const pageContent = await extractContent$1(activeWebContents);
21135
21142
  const pageType = detectPageType(pageContent);
21136
21143
  const defaultReadMode = chooseAgentReadMode(pageContent);
21137
21144
  if (provider.agentToolProfile === "compact") {
@@ -21232,7 +21239,7 @@ ${trackerCtx}`;
21232
21239
  let prompt;
21233
21240
  if (activeWebContents) {
21234
21241
  try {
21235
- const pageContent = await extractContent(activeWebContents);
21242
+ const pageContent = await extractContent$1(activeWebContents);
21236
21243
  if (isSummarize) {
21237
21244
  prompt = buildSummarizePrompt(pageContent);
21238
21245
  } else {
@@ -21492,7 +21499,7 @@ function registerContentHandlers(windowState) {
21492
21499
  assertTrustedIpcSender(event);
21493
21500
  const activeTab = tabManager.getActiveTab();
21494
21501
  if (!activeTab) return null;
21495
- return extractContent(activeTab.view.webContents);
21502
+ return extractContent$1(activeTab.view.webContents);
21496
21503
  });
21497
21504
  electron.ipcMain.handle(Channels.READER_MODE_TOGGLE, async (event) => {
21498
21505
  assertTrustedIpcSender(event);
@@ -21506,7 +21513,7 @@ function registerContentHandlers(windowState) {
21506
21513
  }
21507
21514
  } else {
21508
21515
  const originalUrl = activeTab.state.url;
21509
- const content = await extractContent(activeTab.view.webContents);
21516
+ const content = await extractContent$1(activeTab.view.webContents);
21510
21517
  const html = generateReaderHTML(content);
21511
21518
  activeTab.setReaderMode(true, originalUrl);
21512
21519
  void loadInternalDataURL(
@@ -22631,7 +22638,7 @@ function requiresExplicitMcpApproval(name, args) {
22631
22638
  if (name === "remove_bookmark_folder" && args.delete_contents === true) return true;
22632
22639
  return false;
22633
22640
  }
22634
- function getActiveTabSummary(tabManager) {
22641
+ function getActiveTabSummary$1(tabManager) {
22635
22642
  const activeTab = tabManager.getActiveTab();
22636
22643
  const activeTabId = tabManager.getActiveTabId();
22637
22644
  if (!activeTab || !activeTabId) return null;
@@ -22671,7 +22678,7 @@ async function getPostActionState(tabManager, name) {
22671
22678
  if (navActions.includes(name)) {
22672
22679
  let warning = "";
22673
22680
  try {
22674
- const page = await extractContent(wc);
22681
+ const page = await extractContent$1(wc);
22675
22682
  const issue = getRecoverableAccessIssue(page);
22676
22683
  if (issue) {
22677
22684
  const blockedUrl = wc.getURL();
@@ -22704,7 +22711,7 @@ async function getPostActionState(tabManager, name) {
22704
22711
  }
22705
22712
  if (tabActions.includes(name)) {
22706
22713
  const activeId = tabManager.getActiveTabId();
22707
- const active = getActiveTabSummary(tabManager);
22714
+ const active = getActiveTabSummary$1(tabManager);
22708
22715
  const count = tabManager.getAllStates().length;
22709
22716
  return `
22710
22717
  [state: activeTab=${activeId}, title=${JSON.stringify(active?.title ?? "")}, url=${active?.url ?? ""}, totalTabs=${count}]`;
@@ -24148,7 +24155,7 @@ function registerTools(server, tabManager, runtime2) {
24148
24155
  const wc = activeTab.view.webContents;
24149
24156
  pageUrl = wc.getURL();
24150
24157
  pageTitle = wc.getTitle();
24151
- const page = await extractContent(wc);
24158
+ const page = await extractContent$1(wc);
24152
24159
  pageType = detectPageType(page);
24153
24160
  } catch (err) {
24154
24161
  logger$a.warn("Failed to detect page type for tool scoring, falling back to GENERAL:", err);
@@ -24314,7 +24321,7 @@ ${buildScopedContext(pageContent, mode)}`;
24314
24321
  const tab = tabManager.getActiveTab();
24315
24322
  if (!tab) return asNoActiveTabResponse();
24316
24323
  try {
24317
- const pageContent = await extractContent(tab.view.webContents);
24324
+ const pageContent = await extractContent$1(tab.view.webContents);
24318
24325
  const effectiveMode = mode || "full";
24319
24326
  return asTextResponse(
24320
24327
  await buildExtractResponse(
@@ -24346,7 +24353,7 @@ ${buildScopedContext(pageContent, mode)}`;
24346
24353
  const tab = tabManager.getActiveTab();
24347
24354
  if (!tab) return asNoActiveTabResponse();
24348
24355
  try {
24349
- const pageContent = await extractContent(tab.view.webContents);
24356
+ const pageContent = await extractContent$1(tab.view.webContents);
24350
24357
  const effectiveMode = mode || "full";
24351
24358
  return asTextResponse(
24352
24359
  await buildExtractResponse(
@@ -24480,7 +24487,7 @@ ${buildScopedContext(pageContent, mode)}`;
24480
24487
  const tab = tabManager.getActiveTab();
24481
24488
  if (!tab) return asNoActiveTabResponse();
24482
24489
  try {
24483
- const pageContent = await extractContent(tab.view.webContents);
24490
+ const pageContent = await extractContent$1(tab.view.webContents);
24484
24491
  const requestedType = typeof type === "string" && type.trim() ? type.trim().toLowerCase() : "";
24485
24492
  const entities = (pageContent.structuredData ?? []).filter(
24486
24493
  (entity) => requestedType ? entity.types.some(
@@ -25583,7 +25590,7 @@ ${JSON.stringify(otherHighlights, null, 2)}`
25583
25590
  "memory_page_capture",
25584
25591
  { title, folder, tags },
25585
25592
  async () => {
25586
- const page = await extractContent(tab.view.webContents);
25593
+ const page = await extractContent$1(tab.view.webContents);
25587
25594
  const saved = capturePageToVault({
25588
25595
  page,
25589
25596
  title,
@@ -25704,7 +25711,7 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
25704
25711
  const wc = tab.view.webContents;
25705
25712
  let page;
25706
25713
  try {
25707
- page = await extractContent(wc);
25714
+ page = await extractContent$1(wc);
25708
25715
  } catch (err) {
25709
25716
  logger$a.warn("Failed to extract page while generating suggestions:", err);
25710
25717
  return asTextResponse(
@@ -25720,7 +25727,6 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
25720
25727
  suggestions.push(flowCtx);
25721
25728
  suggestions.push("");
25722
25729
  }
25723
- page.url.toLowerCase();
25724
25730
  const hasPasswordField = page.forms.some(
25725
25731
  (f) => f.fields.some((el) => el.inputType === "password")
25726
25732
  );
@@ -27263,7 +27269,7 @@ const BUNDLED_KIT_IDS = /* @__PURE__ */ new Set([
27263
27269
  "price-scout",
27264
27270
  "form-filler"
27265
27271
  ]);
27266
- const KIT_ID_UNSAFE_CHAR_PATTERN = /[\/\\\0]/;
27272
+ const KIT_ID_UNSAFE_CHAR_PATTERN = /[/\\\0]/;
27267
27273
  function isSafeAutomationKitId(id) {
27268
27274
  return id.length > 0 && !KIT_ID_UNSAFE_CHAR_PATTERN.test(id);
27269
27275
  }
@@ -29133,7 +29139,7 @@ function registerAutofillHandlers(windowState) {
29133
29139
  const activeTab = windowState.tabManager.getActiveTab();
29134
29140
  const wc = activeTab?.view.webContents;
29135
29141
  if (!wc) throw new Error("No active tab");
29136
- const content = await extractContent(wc);
29142
+ const content = await extractContent$1(wc);
29137
29143
  const elements = content.interactiveElements || [];
29138
29144
  const matches = matchFields(elements, profile);
29139
29145
  if (matches.length === 0) {
@@ -30785,25 +30791,12 @@ async function bootstrap() {
30785
30791
  count = 0;
30786
30792
  }
30787
30793
  }
30788
- if (!state2.chromeView.webContents.isDestroyed()) {
30789
- state2.chromeView.webContents.send(Channels.HIGHLIGHT_COUNT_UPDATE, count);
30790
- }
30791
- if (!state2.sidebarView.webContents.isDestroyed()) {
30792
- state2.sidebarView.webContents.send(Channels.HIGHLIGHT_COUNT_UPDATE, count);
30793
- }
30794
- if (!state2.devtoolsPanelView.webContents.isDestroyed()) {
30795
- state2.devtoolsPanelView.webContents.send(
30796
- Channels.HIGHLIGHT_COUNT_UPDATE,
30797
- count
30798
- );
30799
- }
30794
+ sendSafe(state2.chromeView.webContents, Channels.HIGHLIGHT_COUNT_UPDATE, count);
30795
+ sendSafe(state2.sidebarView.webContents, Channels.HIGHLIGHT_COUNT_UPDATE, count);
30796
+ sendSafe(state2.devtoolsPanelView.webContents, Channels.HIGHLIGHT_COUNT_UPDATE, count);
30800
30797
  };
30801
30798
  const windowState = createMainWindow((tabs, activeId, meta) => {
30802
- windowState.chromeView.webContents.send(
30803
- Channels.TAB_STATE_UPDATE,
30804
- tabs,
30805
- activeId
30806
- );
30799
+ sendSafe(windowState.chromeView.webContents, Channels.TAB_STATE_UPDATE, tabs, activeId);
30807
30800
  void syncActiveHighlightCount(windowState);
30808
30801
  layoutViews(windowState);
30809
30802
  if (meta.persistSession) {
@@ -30826,18 +30819,12 @@ async function bootstrap() {
30826
30819
  runtime = new AgentRuntime(tabManager);
30827
30820
  installAdBlocking(tabManager);
30828
30821
  setDevToolsPanelListener((state2) => {
30829
- if (!devtoolsPanelView.webContents.isDestroyed()) {
30830
- devtoolsPanelView.webContents.send(Channels.DEVTOOLS_PANEL_STATE, state2);
30831
- }
30822
+ sendSafe(devtoolsPanelView.webContents, Channels.DEVTOOLS_PANEL_STATE, state2);
30832
30823
  });
30833
30824
  registerIpcHandlers(windowState, runtime);
30834
30825
  tabManager.onSecurityStateChange((tabId, state2) => {
30835
- if (!chromeView.webContents.isDestroyed()) {
30836
- chromeView.webContents.send(Channels.SECURITY_STATE_UPDATE, { tabId, state: state2 });
30837
- }
30838
- if (!sidebarView.webContents.isDestroyed()) {
30839
- sidebarView.webContents.send(Channels.SECURITY_STATE_UPDATE, { tabId, state: state2 });
30840
- }
30826
+ sendSafe(chromeView.webContents, Channels.SECURITY_STATE_UPDATE, { tabId, state: state2 });
30827
+ sendSafe(sidebarView.webContents, Channels.SECURITY_STATE_UPDATE, { tabId, state: state2 });
30841
30828
  });
30842
30829
  registerHighlightShortcut(windowState.mainWindow, tabManager);
30843
30830
  setupAppMenu({
@@ -30871,21 +30858,19 @@ async function bootstrap() {
30871
30858
  }
30872
30859
  },
30873
30860
  clearBrowsingData: () => {
30874
- if (!chromeView.webContents.isDestroyed()) {
30875
- chromeView.webContents.send(Channels.CLEAR_BROWSING_DATA_OPEN);
30876
- }
30861
+ sendSafe(chromeView.webContents, Channels.CLEAR_BROWSING_DATA_OPEN);
30877
30862
  },
30878
30863
  togglePictureInPicture: () => {
30879
30864
  void togglePictureInPicture(tabManager);
30880
30865
  }
30881
30866
  });
30882
30867
  subscribe((state2) => {
30883
- chromeView.webContents.send(Channels.BOOKMARKS_UPDATE, state2);
30884
- sidebarView.webContents.send(Channels.BOOKMARKS_UPDATE, state2);
30868
+ sendSafe(chromeView.webContents, Channels.BOOKMARKS_UPDATE, state2);
30869
+ sendSafe(sidebarView.webContents, Channels.BOOKMARKS_UPDATE, state2);
30885
30870
  });
30886
30871
  subscribe$1((state2) => {
30887
- chromeView.webContents.send(Channels.HISTORY_UPDATE, state2);
30888
- sidebarView.webContents.send(Channels.HISTORY_UPDATE, state2);
30872
+ sendSafe(chromeView.webContents, Channels.HISTORY_UPDATE, state2);
30873
+ sendSafe(sidebarView.webContents, Channels.HISTORY_UPDATE, state2);
30889
30874
  });
30890
30875
  installDownloadHandler(chromeView);
30891
30876
  installPermissionHandler();