@quanta-intellect/vessel-browser 0.1.127 → 0.1.130

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
@@ -94,7 +94,7 @@ const defaults = {
94
94
  clearBookmarksOnLaunch: false,
95
95
  obsidianVaultPath: "",
96
96
  approvalMode: "confirm-dangerous",
97
- agentTranscriptMode: "summary",
97
+ agentTranscriptMode: "off",
98
98
  chatProvider: null,
99
99
  maxToolIterations: 200,
100
100
  domainPolicy: { allowedDomains: [], blockedDomains: [] },
@@ -114,12 +114,15 @@ const defaults = {
114
114
  const SAVE_DEBOUNCE_MS$6 = 150;
115
115
  const CHAT_PROVIDER_SECRET_FILENAME = "vessel-chat-provider-secret";
116
116
  const CODEX_TOKENS_FILENAME = "vessel-codex-tokens";
117
- const logger$t = createLogger("Settings");
117
+ const logger$u = createLogger("Settings");
118
118
  const SETTABLE_KEYS = new Set(Object.keys(defaults));
119
119
  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$u.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$u.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$u.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$u.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$u.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$u.warn("Could not delete Codex token file:", err);
225
+ }
209
226
  }
210
227
  }
211
228
  function mergeChatProviderSecret(provider) {
@@ -276,6 +293,11 @@ function sanitizeChatProvider(provider) {
276
293
  reasoningEffort: sanitizeReasoningEffortLevel$1(provider.reasoningEffort)
277
294
  } : null;
278
295
  }
296
+ function sanitizeAgentTranscriptMode(mode, legacyEnabled) {
297
+ if (mode === "full") return "full";
298
+ if (mode === "off" || mode === "summary") return "off";
299
+ return legacyEnabled === true ? "full" : "off";
300
+ }
279
301
  function loadSettings() {
280
302
  if (settings) return settings;
281
303
  settingsIssues = [];
@@ -298,7 +320,10 @@ function loadSettings() {
298
320
  sourceDoNotAllowList: sanitizeStringList(
299
321
  parsed.sourceDoNotAllowList ?? defaults.sourceDoNotAllowList
300
322
  ),
301
- agentTranscriptMode: parsed.agentTranscriptMode === "off" || parsed.agentTranscriptMode === "summary" || parsed.agentTranscriptMode === "full" ? parsed.agentTranscriptMode : parsed.showAgentTranscript === false ? "off" : defaults.agentTranscriptMode
323
+ agentTranscriptMode: sanitizeAgentTranscriptMode(
324
+ parsed.agentTranscriptMode,
325
+ parsed.showAgentTranscript
326
+ )
302
327
  };
303
328
  } catch (error) {
304
329
  if (fs.existsSync(getSettingsPath())) {
@@ -326,7 +351,7 @@ function persistNow() {
326
351
  JSON.stringify(buildPersistedSettings(settings), null, 2),
327
352
  { encoding: "utf-8", mode: 384 }
328
353
  )
329
- ).then(() => fs.promises.chmod(getSettingsPath(), 384).catch(() => void 0)).catch((err) => logger$t.error("Failed to save settings:", err));
354
+ ).then(() => fs.promises.chmod(getSettingsPath(), 384).catch(() => void 0)).catch((err) => logger$u.error("Failed to save settings:", err));
330
355
  }
331
356
  function saveSettings() {
332
357
  saveDirty = true;
@@ -494,9 +519,18 @@ function loadTrustedAppURL(wc, url) {
494
519
  }
495
520
  return wc.loadURL(parsed.toString());
496
521
  }
522
+ const urlSafety = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
523
+ __proto__: null,
524
+ assertPermittedNavigationURL,
525
+ assertSafeURL,
526
+ isSafeNavigationURL,
527
+ loadInternalDataURL,
528
+ loadPermittedNavigationURL,
529
+ loadTrustedAppURL
530
+ }, Symbol.toStringTag, { value: "Module" }));
497
531
  const MAX_CUSTOM_HISTORY = 50;
498
532
  const READER_MODE_DATA_URL_PREFIX = "data:text/html;charset=utf-8,";
499
- const logger$s = createLogger("Tab");
533
+ const logger$t = createLogger("Tab");
500
534
  const sessionCertExceptions = /* @__PURE__ */ new WeakMap();
501
535
  const sessionsWithVerifyProc = /* @__PURE__ */ new WeakSet();
502
536
  const CERT_VERIFY_TRUST = 0;
@@ -562,7 +596,7 @@ class Tab {
562
596
  guardedLoadURL(url, options) {
563
597
  const blockReason = this.getNavigationBlockReason(url);
564
598
  if (blockReason) {
565
- logger$s.warn(blockReason);
599
+ logger$t.warn(blockReason);
566
600
  return blockReason;
567
601
  }
568
602
  void this.view.webContents.loadURL(url, options);
@@ -646,7 +680,7 @@ class Tab {
646
680
  wc.setWindowOpenHandler(({ url, disposition }) => {
647
681
  const error = this.getNavigationBlockReason(url);
648
682
  if (error) {
649
- logger$s.warn(error);
683
+ logger$t.warn(error);
650
684
  return { action: "deny" };
651
685
  }
652
686
  this.onOpenUrl?.({
@@ -660,7 +694,7 @@ class Tab {
660
694
  const error = this.getNavigationBlockReason(url);
661
695
  if (!error) return;
662
696
  event.preventDefault();
663
- logger$s.warn(`${context}: ${error}`);
697
+ logger$t.warn(`${context}: ${error}`);
664
698
  };
665
699
  wc.on("will-navigate", (event, url) => {
666
700
  blockNavigation(event, url, "Blocked top-level navigation");
@@ -744,7 +778,7 @@ class Tab {
744
778
  ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 999px; }
745
779
  ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.22); }
746
780
  ::-webkit-scrollbar-corner { background: transparent; }
747
- `).catch((err) => logger$s.warn("Failed to inject scrollbar CSS:", err));
781
+ `).catch((err) => logger$t.warn("Failed to inject scrollbar CSS:", err));
748
782
  });
749
783
  wc.on("page-favicon-updated", (_, favicons) => {
750
784
  this._state.favicon = favicons[0] || "";
@@ -780,7 +814,7 @@ class Tab {
780
814
  ).then((highlightedText) => {
781
815
  this.buildContextMenu(wc, params, highlightedText.trim());
782
816
  }).catch((err) => {
783
- logger$s.warn("Failed to inspect highlighted text for context menu:", err);
817
+ logger$t.warn("Failed to inspect highlighted text for context menu:", err);
784
818
  this.buildContextMenu(wc, params, "");
785
819
  });
786
820
  });
@@ -981,7 +1015,7 @@ class Tab {
981
1015
  "document.documentElement.outerHTML"
982
1016
  );
983
1017
  } catch (err) {
984
- logger$s.warn("Failed to retrieve page source:", err);
1018
+ logger$t.warn("Failed to retrieve page source:", err);
985
1019
  return;
986
1020
  }
987
1021
  const escaped = html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -1109,7 +1143,7 @@ class Tab {
1109
1143
  document.addEventListener('mouseup', window.__vesselHighlightHandler);
1110
1144
  }
1111
1145
  })()
1112
- `).catch((err) => logger$s.warn("Failed to inject highlight listener:", err));
1146
+ `).catch((err) => logger$t.warn("Failed to inject highlight listener:", err));
1113
1147
  } else {
1114
1148
  void wc.executeJavaScript(`
1115
1149
  (function() {
@@ -1120,7 +1154,7 @@ class Tab {
1120
1154
  delete window.__vesselHighlightHandler;
1121
1155
  }
1122
1156
  })()
1123
- `).catch((err) => logger$s.warn("Failed to remove highlight listener:", err));
1157
+ `).catch((err) => logger$t.warn("Failed to remove highlight listener:", err));
1124
1158
  }
1125
1159
  }
1126
1160
  get webContentsId() {
@@ -1157,7 +1191,7 @@ const SEARCH_ENGINE_PRESETS = {
1157
1191
  ecosia: { label: "Ecosia", url: "https://www.ecosia.org/search?q=" },
1158
1192
  kagi: { label: "Kagi", url: "https://kagi.com/search?q=" }
1159
1193
  };
1160
- const logger$r = createLogger("JsonPersistence");
1194
+ const logger$s = createLogger("JsonPersistence");
1161
1195
  function canUseSafeStorage() {
1162
1196
  try {
1163
1197
  return electron.safeStorage.isEncryptionAvailable();
@@ -1222,7 +1256,7 @@ function createDebouncedJsonPersistence({
1222
1256
  data,
1223
1257
  typeof data === "string" ? { encoding: "utf-8", mode: 384 } : { mode: 384 }
1224
1258
  )
1225
- ).then(() => fs.promises.chmod(filePath2, 384).catch(() => void 0)).catch((err) => logger$r.error(`Failed to save ${logLabel}:`, err));
1259
+ ).then(() => fs.promises.chmod(filePath2, 384).catch(() => void 0)).catch((err) => logger$s.error(`Failed to save ${logLabel}:`, err));
1226
1260
  };
1227
1261
  const schedule = () => {
1228
1262
  saveDirty2 = true;
@@ -2914,14 +2948,9 @@ function destroySession(tabId) {
2914
2948
  sessions.delete(tabId);
2915
2949
  }
2916
2950
  }
2917
- const logger$q = createLogger("TabManager");
2918
- function sanitizePdfFilename(title) {
2951
+ const logger$r = createLogger("TabManager");
2952
+ function sanitizeFilename(title, ext) {
2919
2953
  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
2954
  const escapedExt = ext.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2926
2955
  const regex = new RegExp(`\\.${escapedExt}$`, "i");
2927
2956
  const base = (clean || "Vessel Page").replace(regex, "");
@@ -3150,7 +3179,7 @@ class TabManager {
3150
3179
  if (!tab) return null;
3151
3180
  const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
3152
3181
  title: "Save Page as PDF",
3153
- defaultPath: sanitizePdfFilename(tab.state.title || "Vessel Page"),
3182
+ defaultPath: sanitizeFilename(tab.state.title || "Vessel Page", "pdf"),
3154
3183
  filters: [{ name: "PDF", extensions: ["pdf"] }]
3155
3184
  });
3156
3185
  if (canceled || !filePath2) return null;
@@ -3166,7 +3195,7 @@ class TabManager {
3166
3195
  const ext = format === "MHTML" ? "mhtml" : "html";
3167
3196
  const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
3168
3197
  title: "Save Page As",
3169
- defaultPath: sanitizePageFilename(
3198
+ defaultPath: sanitizeFilename(
3170
3199
  tab.state.title || "Vessel Page",
3171
3200
  ext
3172
3201
  ),
@@ -3314,7 +3343,7 @@ class TabManager {
3314
3343
  }));
3315
3344
  if (entries.length > 0) {
3316
3345
  void highlightBatchOnPage(wc, entries).catch(
3317
- (err) => logger$q.warn("Failed to batch highlight:", err)
3346
+ (err) => logger$r.warn("Failed to batch highlight:", err)
3318
3347
  );
3319
3348
  }
3320
3349
  }
@@ -3336,12 +3365,12 @@ class TabManager {
3336
3365
  const result = await captureSelectionHighlight(wc);
3337
3366
  if (result.success && result.text) {
3338
3367
  await highlightOnPage(wc, null, result.text, void 0, void 0, "yellow").catch(
3339
- (err) => logger$q.warn("Failed to capture highlight:", err)
3368
+ (err) => logger$r.warn("Failed to capture highlight:", err)
3340
3369
  );
3341
3370
  }
3342
3371
  this.highlightCaptureCallback?.(result);
3343
3372
  } catch (err) {
3344
- logger$q.warn("Failed to capture highlight from page:", err);
3373
+ logger$r.warn("Failed to capture highlight from page:", err);
3345
3374
  this.highlightCaptureCallback?.({
3346
3375
  success: false,
3347
3376
  message: "Could not capture selection"
@@ -3366,7 +3395,7 @@ class TabManager {
3366
3395
  void this.removeHighlightMarksForText(wc, text);
3367
3396
  }
3368
3397
  } catch (err) {
3369
- logger$q.warn("Failed to remove highlight from matching tab:", err);
3398
+ logger$r.warn("Failed to remove highlight from matching tab:", err);
3370
3399
  }
3371
3400
  }
3372
3401
  this.highlightCaptureCallback?.({
@@ -3397,12 +3426,12 @@ class TabManager {
3397
3426
  void 0,
3398
3427
  color
3399
3428
  ).catch(
3400
- (err) => logger$q.warn("Failed to update highlight color:", err)
3429
+ (err) => logger$r.warn("Failed to update highlight color:", err)
3401
3430
  );
3402
3431
  });
3403
3432
  }
3404
3433
  } catch (err) {
3405
- logger$q.warn("Failed to iterate highlights for color change:", err);
3434
+ logger$r.warn("Failed to iterate highlights for color change:", err);
3406
3435
  }
3407
3436
  }
3408
3437
  this.highlightCaptureCallback?.({
@@ -3457,7 +3486,7 @@ class TabManager {
3457
3486
  });
3458
3487
  })()`
3459
3488
  ).catch(
3460
- (err) => logger$q.warn("Failed to remove highlight marks:", err)
3489
+ (err) => logger$r.warn("Failed to remove highlight marks:", err)
3461
3490
  );
3462
3491
  }
3463
3492
  broadcastState(meta = { persistSession: false }) {
@@ -4661,7 +4690,7 @@ function errorResult(error, value) {
4661
4690
  function getErrorMessage(error, fallback = "Unknown error") {
4662
4691
  return error instanceof Error && error.message ? error.message : fallback;
4663
4692
  }
4664
- const logger$p = createLogger("Premium");
4693
+ const logger$q = createLogger("Premium");
4665
4694
  const VERIFICATION_API = process.env.VESSEL_PREMIUM_API || "https://vesselpremium.quantaintellect.com";
4666
4695
  const FREE_TOOL_ITERATION_LIMIT = 50;
4667
4696
  const REVALIDATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
@@ -4837,7 +4866,7 @@ async function verifySubscription$1(identifier) {
4837
4866
  });
4838
4867
  if (!res.ok) {
4839
4868
  const detail = await readApiErrorDetail(res);
4840
- logger$p.warn(
4869
+ logger$q.warn(
4841
4870
  "Verification API returned a non-OK status:",
4842
4871
  res.status,
4843
4872
  detail
@@ -4856,7 +4885,7 @@ async function verifySubscription$1(identifier) {
4856
4885
  setSetting("premium", updated);
4857
4886
  return updated;
4858
4887
  } catch (err) {
4859
- logger$p.warn("Verification failed:", err);
4888
+ logger$q.warn("Verification failed:", err);
4860
4889
  return current;
4861
4890
  }
4862
4891
  }
@@ -5321,7 +5350,7 @@ function getOfferPrice(offers) {
5321
5350
  const offer = asStructuredObject(offers);
5322
5351
  return firstStructuredString(offer?.price);
5323
5352
  }
5324
- function extractPrimaryEntity(pageType, structuredData, metaTags) {
5353
+ function extractPrimaryEntity(pageType, structuredData, _metaTags) {
5325
5354
  if (pageType === "product") {
5326
5355
  const product = structuredData?.find(
5327
5356
  (e) => e.types.some((t) => /^product$/i.test(t))
@@ -5511,7 +5540,7 @@ const EXTRACT_TIMEOUT_MAX_MS = 2e4;
5511
5540
  const MUTATION_CAPTURE_INTERVAL_MS = 5e3;
5512
5541
  const MUTATION_SETTLE_AFTER_MS = 1500;
5513
5542
  const AGENT_STREAM_IDLE_TIMEOUT_MS = 3e4;
5514
- const logger$o = createLogger("Extractor");
5543
+ const logger$p = createLogger("Extractor");
5515
5544
  const EXTRACTION_CACHE_TTL_MS = 1500;
5516
5545
  const MAX_EXTRACTION_CACHE_ENTRIES = 50;
5517
5546
  const extractionCache = new BoundedCache(
@@ -6276,9 +6305,9 @@ async function executeScript(webContents, script, options = {}) {
6276
6305
  const message = err instanceof Error ? err.message : String(err);
6277
6306
  const detail = `Failed to execute page script${label} on ${url}: ${message}`;
6278
6307
  if (options.warnOnFailure) {
6279
- logger$o.warn(detail);
6308
+ logger$p.warn(detail);
6280
6309
  } else {
6281
- logger$o.debug(detail);
6310
+ logger$p.debug(detail);
6282
6311
  }
6283
6312
  return null;
6284
6313
  } finally {
@@ -6387,7 +6416,7 @@ async function estimateExtractionTimeout(webContents) {
6387
6416
  return EXTRACT_TIMEOUT_BASE_MS + extra;
6388
6417
  }
6389
6418
  } catch (err) {
6390
- logger$o.warn("Failed to estimate extraction timeout, using base timeout:", err);
6419
+ logger$p.warn("Failed to estimate extraction timeout, using base timeout:", err);
6391
6420
  }
6392
6421
  return EXTRACT_TIMEOUT_BASE_MS;
6393
6422
  }
@@ -6406,7 +6435,7 @@ async function extractContentInner(webContents) {
6406
6435
  webContents
6407
6436
  );
6408
6437
  }
6409
- async function extractContent(webContents) {
6438
+ async function extractContent$1(webContents) {
6410
6439
  const cacheKey = `${webContents.id}:${webContents.getURL() || ""}`;
6411
6440
  const cached = extractionCache.get(cacheKey);
6412
6441
  if (cached) {
@@ -6624,7 +6653,7 @@ async function capturePageSnapshot(url, wc, sendToRendererViews) {
6624
6653
  if (!shouldTrackSnapshotUrl(url)) return;
6625
6654
  const key2 = normalizeUrl(url);
6626
6655
  const oldSnap = getSnapshot(key2);
6627
- const content = await extractContent(wc);
6656
+ const content = await extractContent$1(wc);
6628
6657
  const textContent = content.content || "";
6629
6658
  const title = content.title || "";
6630
6659
  const headings = content.headings || [];
@@ -6699,6 +6728,60 @@ function schedulePageSnapshotCapture(wc, sendToRendererViews, delayMs = 0, optio
6699
6728
  }
6700
6729
  scheduleTimerAt(wc, sendToRendererViews, nextDueAt, options);
6701
6730
  }
6731
+ const trustedIpcSenderIds = /* @__PURE__ */ new Set();
6732
+ function registerTrustedIpcSender(wc) {
6733
+ trustedIpcSenderIds.add(wc.id);
6734
+ wc.once("destroyed", () => trustedIpcSenderIds.delete(wc.id));
6735
+ }
6736
+ function assertTrustedIpcSender(event) {
6737
+ if (!trustedIpcSenderIds.has(event.sender.id)) {
6738
+ throw new Error("Blocked IPC from untrusted renderer");
6739
+ }
6740
+ }
6741
+ function isManagedTabIpcSender(event, tabManager) {
6742
+ return Boolean(tabManager.findTabByWebContentsId(event.sender.id));
6743
+ }
6744
+ function parseIpc(schema, value, label) {
6745
+ const result = schema.safeParse(value);
6746
+ if (!result.success) {
6747
+ const message = result.error.issues.map((i) => i.message).join("; ");
6748
+ throw new Error(
6749
+ label ? `Invalid ${label}: ${message}` : `Invalid input: ${message}`
6750
+ );
6751
+ }
6752
+ return result.data;
6753
+ }
6754
+ function assertString(value, name) {
6755
+ if (typeof value !== "string") throw new Error(`${name} must be a string`);
6756
+ }
6757
+ function assertOptionalString(value, name) {
6758
+ if (value !== void 0 && typeof value !== "string") {
6759
+ throw new Error(`${name} must be a string`);
6760
+ }
6761
+ }
6762
+ function assertNumber(value, name) {
6763
+ if (typeof value !== "number" || Number.isNaN(value)) {
6764
+ throw new Error(`${name} must be a number`);
6765
+ }
6766
+ }
6767
+ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
6768
+ function isValidEmail$1(value) {
6769
+ return EMAIL_RE.test(value.trim());
6770
+ }
6771
+ function getActiveTabInfo(tabManager) {
6772
+ const tab = tabManager.getActiveTab();
6773
+ if (!tab) return null;
6774
+ const wc = tab.view.webContents;
6775
+ if (wc.isDestroyed()) return null;
6776
+ return { tab, wc };
6777
+ }
6778
+ function sendSafe(wc, channel, ...args) {
6779
+ if (!wc || wc.isDestroyed()) return;
6780
+ try {
6781
+ wc.send(channel, ...args);
6782
+ } catch {
6783
+ }
6784
+ }
6702
6785
  function setSidebarPanelMode(state2, mode, reason = "user") {
6703
6786
  state2.uiState.sidebarPanelMode = mode;
6704
6787
  if (reason === "user") {
@@ -6741,15 +6824,8 @@ function getSidebarPanelState(state2) {
6741
6824
  }
6742
6825
  function emitSidebarPanelState(state2) {
6743
6826
  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
- }
6827
+ sendSafe(state2.chromeView.webContents, Channels.SIDEBAR_STATE_UPDATE, panelState);
6828
+ sendSafe(state2.sidebarView.webContents, Channels.SIDEBAR_STATE_UPDATE, panelState);
6753
6829
  return panelState;
6754
6830
  }
6755
6831
  function isSidebarAttached(state2) {
@@ -7059,8 +7135,8 @@ function createMainWindow(onTabStateChange) {
7059
7135
  };
7060
7136
  const tabManager = new TabManager(mainWindow, onTabStateChange);
7061
7137
  const sendToRendererViews = (channel, ...args) => {
7062
- chromeView.webContents.send(channel, ...args);
7063
- sidebarView.webContents.send(channel, ...args);
7138
+ sendSafe(chromeView.webContents, channel, ...args);
7139
+ sendSafe(sidebarView.webContents, channel, ...args);
7064
7140
  };
7065
7141
  tabManager.onPageLoad((url, wc) => {
7066
7142
  const activeWc = tabManager.getActiveTab()?.view.webContents;
@@ -8171,7 +8247,7 @@ function recoverNarratedActionToolCalls(text, availableToolNames) {
8171
8247
  }
8172
8248
  return recovered;
8173
8249
  }
8174
- const logger$n = createLogger("OpenAIProvider");
8250
+ const logger$o = createLogger("OpenAIProvider");
8175
8251
  function shouldDebugAgentLoop() {
8176
8252
  const value = process.env.VESSEL_DEBUG_AGENT_LOOP;
8177
8253
  return value === "1" || value === "true";
@@ -8439,9 +8515,9 @@ function shouldRetryCompactToolLoop(profile, text, hasToolHistory, userMessage)
8439
8515
  function logAgentLoopDebug(payload) {
8440
8516
  if (!shouldDebugAgentLoop()) return;
8441
8517
  try {
8442
- logger$n.info(`[agent-debug] ${JSON.stringify(payload)}`);
8518
+ logger$o.info(`[agent-debug] ${JSON.stringify(payload)}`);
8443
8519
  } catch (err) {
8444
- logger$n.warn("Failed to serialize debug payload:", err);
8520
+ logger$o.warn("Failed to serialize debug payload:", err);
8445
8521
  }
8446
8522
  }
8447
8523
  function formatOpenAICompatErrorMessage(providerId, message) {
@@ -9045,7 +9121,7 @@ function createLocalPkceOAuthFlow(config) {
9045
9121
  isInProgress: () => activeFlow !== null
9046
9122
  };
9047
9123
  }
9048
- const logger$m = createLogger("CodexOAuth");
9124
+ const logger$n = createLogger("CodexOAuth");
9049
9125
  const ISSUER = "https://auth.openai.com";
9050
9126
  const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
9051
9127
  const SCOPE = "openid profile email offline_access api.connectors.read api.connectors.invoke";
@@ -9176,7 +9252,7 @@ function escapeHtml$1(text) {
9176
9252
  }
9177
9253
  const codexOAuth = createLocalPkceOAuthFlow({
9178
9254
  name: "Codex",
9179
- logger: logger$m,
9255
+ logger: logger$n,
9180
9256
  preferredPorts: [PREFERRED_PORT$1, FALLBACK_PORT$1],
9181
9257
  timeoutMs: AUTH_TIMEOUT_MS$1,
9182
9258
  callbackPath: () => "/auth/callback",
@@ -9200,7 +9276,7 @@ async function startCodexOAuth(onStatus) {
9200
9276
  function cancelCodexOAuth() {
9201
9277
  codexOAuth.cancel();
9202
9278
  }
9203
- const logger$l = createLogger("CodexProvider");
9279
+ const logger$m = createLogger("CodexProvider");
9204
9280
  const REFRESH_WINDOW_MS = 5 * 60 * 1e3;
9205
9281
  const CODEX_BACKEND_BASE_URL = "https://chatgpt.com/backend-api/codex";
9206
9282
  const CODEX_CLIENT_VERSION = "0.129.0";
@@ -9265,7 +9341,7 @@ class CodexProvider {
9265
9341
  async ensureFreshTokens() {
9266
9342
  if (Date.now() < this.tokens.expiresAt - REFRESH_WINDOW_MS) return;
9267
9343
  try {
9268
- logger$l.info("Refreshing Codex access token");
9344
+ logger$m.info("Refreshing Codex access token");
9269
9345
  const fresh = await refreshAccessToken(this.tokens);
9270
9346
  this.tokens = fresh;
9271
9347
  writeStoredCodexTokens(fresh);
@@ -9413,7 +9489,7 @@ class CodexProvider {
9413
9489
  } catch (err) {
9414
9490
  if (err.name !== "AbortError") {
9415
9491
  const msg = err instanceof Error ? err.message : String(err);
9416
- logger$l.error("Codex streamQuery error:", err);
9492
+ logger$m.error("Codex streamQuery error:", err);
9417
9493
  onChunk(`
9418
9494
 
9419
9495
  [Error: ${msg}]`);
@@ -9481,7 +9557,7 @@ class CodexProvider {
9481
9557
  } catch (err) {
9482
9558
  if (err.name !== "AbortError") {
9483
9559
  const msg = err instanceof Error ? err.message : String(err);
9484
- logger$l.error("Codex streamAgentQuery error:", err);
9560
+ logger$m.error("Codex streamAgentQuery error:", err);
9485
9561
  onChunk(`
9486
9562
 
9487
9563
  [Error: ${msg}]`);
@@ -9681,60 +9757,6 @@ function createProvider(config) {
9681
9757
  }
9682
9758
  return new OpenAICompatProvider(normalized);
9683
9759
  }
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
9760
  function buildSubAgentSystemPrompt(thread) {
9739
9761
  const domainBlock = thread.blockedDomains.length > 0 ? `
9740
9762
  BLOCKED DOMAINS (never visit): ${thread.blockedDomains.join(", ")}` : "";
@@ -10769,6 +10791,7 @@ function normalizeBookmarkMetadataUpdate(input) {
10769
10791
  }
10770
10792
  return normalized;
10771
10793
  }
10794
+ const logger$l = createLogger("BookmarkManager");
10772
10795
  const UNSORTED_ID = "unsorted";
10773
10796
  const ARCHIVE_FOLDER_NAME = "Archive";
10774
10797
  const NETSCAPE_BOOKMARKS_DOCTYPE = "<!DOCTYPE NETSCAPE-Bookmark-file-1>";
@@ -11340,7 +11363,8 @@ function importBookmarksFromJson(content) {
11340
11363
  save$1();
11341
11364
  emit$2();
11342
11365
  }
11343
- } catch {
11366
+ } catch (err) {
11367
+ logger$l.warn("Failed to import bookmarks from JSON:", err);
11344
11368
  errors++;
11345
11369
  }
11346
11370
  return { imported, skipped, errors };
@@ -11740,7 +11764,7 @@ async function resolveSelector(wc, index, selector) {
11740
11764
  if (typeof fallbackSelector === "string" && fallbackSelector) {
11741
11765
  return fallbackSelector;
11742
11766
  }
11743
- const page = await extractContent(wc);
11767
+ const page = await extractContent$1(wc);
11744
11768
  const extractedSelector = findSelectorByIndex(page, index);
11745
11769
  if (extractedSelector) return extractedSelector;
11746
11770
  return null;
@@ -14318,65 +14342,371 @@ function cartOrigin(url) {
14318
14342
  return null;
14319
14343
  }
14320
14344
  }
14321
- function resolveBookmarkFolderTarget$1(args) {
14322
- const folderId = typeof args.folderId === "string" ? args.folderId.trim() : typeof args.folder_id === "string" ? args.folder_id.trim() : "";
14323
- if (folderId) {
14324
- if (folderId === UNSORTED_ID) {
14325
- return {
14326
- folderId: UNSORTED_ID,
14327
- folderName: "Unsorted"
14328
- };
14345
+ class TabMutex {
14346
+ queue = Promise.resolve();
14347
+ enqueue(fn) {
14348
+ const run = this.queue.then(fn, fn);
14349
+ this.queue = run.then(
14350
+ () => void 0,
14351
+ () => void 0
14352
+ );
14353
+ return run;
14354
+ }
14355
+ }
14356
+ const logger$j = createLogger("PageActions");
14357
+ const DEFAULT_PAGE_SCRIPT_TIMEOUT_MS = 1500;
14358
+ const PAGE_SCRIPT_TIMEOUT = /* @__PURE__ */ Symbol("page-script-timeout");
14359
+ function pageBusyError(action) {
14360
+ return `Error: Page is still busy; ${action} timed out waiting for page scripts. Retry in a moment.`;
14361
+ }
14362
+ async function loadPermittedUrl(wc, url) {
14363
+ const { assertPermittedNavigationURL: assertPermittedNavigationURL2 } = await Promise.resolve().then(() => urlSafety);
14364
+ assertPermittedNavigationURL2(url);
14365
+ await wc.loadURL(url);
14366
+ }
14367
+ async function executePageScript(wc, script, options) {
14368
+ if (wc.isDestroyed()) return null;
14369
+ const timeoutMs = Math.max(150, options?.timeoutMs ?? DEFAULT_PAGE_SCRIPT_TIMEOUT_MS);
14370
+ let timer = null;
14371
+ try {
14372
+ const result = await Promise.race([
14373
+ wc.executeJavaScript(script, options?.userGesture ?? false),
14374
+ new Promise((resolve) => {
14375
+ timer = setTimeout(() => resolve(PAGE_SCRIPT_TIMEOUT), timeoutMs);
14376
+ })
14377
+ ]);
14378
+ if (result === PAGE_SCRIPT_TIMEOUT) {
14379
+ return PAGE_SCRIPT_TIMEOUT;
14329
14380
  }
14330
- const folder2 = getFolder(folderId);
14331
- if (!folder2) {
14332
- return { folderName: "Unsorted", error: `Folder ${folderId} not found` };
14381
+ return result;
14382
+ } catch (err) {
14383
+ const label = options?.label ? ` (${options.label})` : "";
14384
+ logger$j.warn(`Failed to execute page script${label}:`, err);
14385
+ return null;
14386
+ } finally {
14387
+ if (timer) {
14388
+ clearTimeout(timer);
14333
14389
  }
14334
- return { folderId: folder2.id, folderName: folder2.name };
14335
- }
14336
- const folderName = typeof args.folderName === "string" && args.folderName.trim() ? args.folderName.trim() : typeof args.folder_name === "string" && args.folder_name.trim() ? args.folder_name.trim() : args.archive ? ARCHIVE_FOLDER_NAME : "";
14337
- if (!folderName || folderName.toLowerCase() === "unsorted") {
14338
- return {
14339
- folderId: UNSORTED_ID,
14340
- folderName: "Unsorted"
14341
- };
14342
- }
14343
- const existing = findFolderByName(folderName);
14344
- if (existing) {
14345
- return { folderId: existing.id, folderName: existing.name };
14346
- }
14347
- const createIfMissing = args.createFolderIfMissing ?? args.create_folder_if_missing;
14348
- if (createIfMissing === false) {
14349
- return { folderName, error: `Folder "${folderName}" not found` };
14350
14390
  }
14351
- const folderSummary = typeof args.folderSummary === "string" && args.folderSummary.trim() ? args.folderSummary.trim() : typeof args.folder_summary === "string" && args.folder_summary.trim() ? args.folder_summary.trim() : void 0;
14352
- const { folder } = ensureFolder(folderName, folderSummary);
14353
- return {
14354
- folderId: folder.id,
14355
- folderName: folder.name,
14356
- createdFolder: folder.name
14357
- };
14358
- }
14359
- function formatFolderStatus(limit = 6) {
14360
- const folders = listFolderOverviews();
14361
- const summary = folders.slice(0, limit).map((folder) => `${folder.name} (${folder.count})`).join(", ");
14362
- return `Folder status: ${summary}${folders.length > limit ? ", ..." : ""}`;
14363
14391
  }
14364
- function describeFolder$1(folderId) {
14365
- if (!folderId || folderId === UNSORTED_ID) {
14366
- return "Unsorted";
14392
+ async function waitForJsReady(wc, timeout = 8e3) {
14393
+ const start = Date.now();
14394
+ while (Date.now() - start < timeout) {
14395
+ const ready = await executePageScript(wc, "1", {
14396
+ timeoutMs: 250,
14397
+ userGesture: true,
14398
+ label: "js-ready probe"
14399
+ });
14400
+ if (ready === 1) return;
14401
+ await sleep(250);
14367
14402
  }
14368
- return getFolder(folderId)?.name ?? folderId;
14369
14403
  }
14370
- function composeDuplicateBookmarkResponse$1(args) {
14371
- return `Bookmark already exists for ${args.url} in "${args.folderName}" (id=${args.bookmarkId}). Retry with onDuplicate="update" to refresh the existing bookmark or onDuplicate="duplicate" to keep both entries.`;
14404
+ function waitForPotentialNavigation(wc, beforeUrl, timeout = 2500) {
14405
+ return waitForPotentialNavigation$1(wc, beforeUrl, timeout, QUIET_NAVIGATION_WINDOW_MS);
14372
14406
  }
14373
- function composeFolderAwareResponse$1(message, createdFolder) {
14374
- const prefix = createdFolder ? `Created folder "${createdFolder}".
14375
- ` : "";
14376
- return `${prefix}${message}
14377
- ${formatFolderStatus()}`;
14407
+ function getGlanceExtractScript() {
14408
+ return `(function() {
14409
+ var vw = window.innerWidth || document.documentElement.clientWidth || 0;
14410
+ var vh = window.innerHeight || document.documentElement.clientHeight || 0;
14411
+ var sy = window.scrollY || window.pageYOffset || 0;
14412
+
14413
+ function inViewport(el) {
14414
+ var r = el.getBoundingClientRect();
14415
+ return r.bottom > 0 && r.top < vh && r.right > 0 && r.left < vw && r.width > 0 && r.height > 0;
14416
+ }
14417
+
14418
+ function label(el) {
14419
+ return (el.getAttribute('aria-label') || el.textContent || '').trim().slice(0, 120);
14420
+ }
14421
+
14422
+ // Headings visible on screen
14423
+ var headings = [];
14424
+ document.querySelectorAll('h1, h2, h3, h4').forEach(function(h) {
14425
+ if (!inViewport(h)) return;
14426
+ var t = (h.textContent || '').trim();
14427
+ if (t && t.length < 200) headings.push(h.tagName.toLowerCase() + ': ' + t);
14428
+ });
14429
+
14430
+ // Links visible on screen (deduplicated by text)
14431
+ var links = [];
14432
+ var seenLinks = {};
14433
+ var idx = 1;
14434
+ document.querySelectorAll('a[href]').forEach(function(a) {
14435
+ if (!inViewport(a)) return;
14436
+ var t = (a.textContent || '').trim().slice(0, 100);
14437
+ if (!t || t.length < 2 || seenLinks[t]) return;
14438
+ seenLinks[t] = true;
14439
+ links.push({ text: t, href: (a.href || '').slice(0, 200), index: idx++ });
14440
+ });
14441
+
14442
+ // Buttons visible on screen
14443
+ var buttons = [];
14444
+ document.querySelectorAll('button, [role="button"], input[type="submit"], input[type="button"]').forEach(function(b) {
14445
+ if (!inViewport(b)) return;
14446
+ var t = label(b);
14447
+ if (!t || t.length < 1) return;
14448
+ buttons.push({ text: t, index: idx++ });
14449
+ });
14450
+
14451
+ // Input fields visible on screen
14452
+ var inputs = [];
14453
+ document.querySelectorAll('input:not([type="hidden"]):not([type="submit"]):not([type="button"]), select, textarea').forEach(function(inp) {
14454
+ if (!inViewport(inp)) return;
14455
+ var type = (inp.type || inp.tagName.toLowerCase() || '').toLowerCase();
14456
+ var lbl = (inp.getAttribute('aria-label') || inp.getAttribute('placeholder') || inp.name || '').trim();
14457
+ inputs.push({ type: type, label: lbl.slice(0, 80), placeholder: (inp.getAttribute('placeholder') || '').slice(0, 80), index: idx++ });
14458
+ });
14459
+
14460
+ // Content snapshot from main content area using textContent (instant, no reflow)
14461
+ var roots = ['main', 'article', '[role="main"]', '#content', '.content', '.story-body'];
14462
+ var contentRoot = null;
14463
+ for (var i = 0; i < roots.length; i++) {
14464
+ contentRoot = document.querySelector(roots[i]);
14465
+ if (contentRoot && contentRoot.textContent.trim().length > 50) break;
14466
+ contentRoot = null;
14467
+ }
14468
+ var snippet = '';
14469
+ if (contentRoot) {
14470
+ snippet = contentRoot.textContent.replace(/[ \\t]+/g, ' ').replace(/(\\n\\s*){3,}/g, '\\n\\n').trim().slice(0, 8000);
14471
+ } else {
14472
+ // Fallback: grab text from visible elements only
14473
+ var parts = [];
14474
+ document.querySelectorAll('h1, h2, h3, p, li, td, span, div').forEach(function(el) {
14475
+ if (parts.length > 100 || !inViewport(el)) return;
14476
+ var t = (el.textContent || '').trim();
14477
+ if (t.length > 10 && t.length < 500) parts.push(t);
14478
+ });
14479
+ snippet = parts.join('\\n').slice(0, 8000);
14480
+ }
14481
+
14482
+ return {
14483
+ title: document.title || '',
14484
+ url: location.href,
14485
+ headings: headings.slice(0, 20),
14486
+ links: links.slice(0, 40),
14487
+ buttons: buttons.slice(0, 20),
14488
+ inputs: inputs.slice(0, 15),
14489
+ contentSnippet: snippet,
14490
+ viewportHeight: vh,
14491
+ viewportWidth: vw,
14492
+ scrollY: Math.round(sy),
14493
+ };
14494
+ })()`;
14378
14495
  }
14379
- const HUGGING_FACE_HUB_HOSTS = /* @__PURE__ */ new Set(["huggingface.co", "www.huggingface.co"]);
14496
+ async function glanceExtract(wc) {
14497
+ const startMs = Date.now();
14498
+ const result = await executePageScript(wc, getGlanceExtractScript(), { timeoutMs: 2500, label: "glance-extract" });
14499
+ const elapsed = Date.now() - startMs;
14500
+ if (!result || result === PAGE_SCRIPT_TIMEOUT) {
14501
+ return [
14502
+ `# ${wc.getTitle() || "(untitled)"}`,
14503
+ `URL: ${wc.getURL()}`,
14504
+ "",
14505
+ "[read_page mode=glance — page JS thread is completely blocked, no content available]",
14506
+ "[Try: click or type_text to interact directly, or wait a few seconds and retry]"
14507
+ ].join("\n");
14508
+ }
14509
+ const sections = [
14510
+ `# ${result.title}`,
14511
+ `URL: ${result.url}`,
14512
+ `Viewport: ${result.viewportWidth}×${result.viewportHeight} scrollY=${result.scrollY}`,
14513
+ `[read_page mode=glance — ${elapsed}ms, showing what's visible on screen]`
14514
+ ];
14515
+ if (result.headings.length > 0) {
14516
+ sections.push("", "## Headings", ...result.headings);
14517
+ }
14518
+ if (result.inputs.length > 0) {
14519
+ sections.push("", "## Input Fields");
14520
+ for (const inp of result.inputs) {
14521
+ const desc = inp.label || inp.placeholder || inp.type;
14522
+ sections.push(` [#${inp.index}] ${inp.type}: ${desc}`);
14523
+ }
14524
+ }
14525
+ if (result.buttons.length > 0) {
14526
+ sections.push("", "## Buttons");
14527
+ for (const btn of result.buttons) {
14528
+ sections.push(` [#${btn.index}] ${btn.text}`);
14529
+ }
14530
+ }
14531
+ if (result.links.length > 0) {
14532
+ sections.push("", "## Visible Links");
14533
+ for (const link of result.links) {
14534
+ sections.push(` [#${link.index}] ${link.text}`);
14535
+ }
14536
+ }
14537
+ if (result.contentSnippet) {
14538
+ const truncated = result.contentSnippet.length > 6e3 ? result.contentSnippet.slice(0, 6e3) + "\n[truncated]" : result.contentSnippet;
14539
+ sections.push("", "## Page Content (viewport)", "", truncated);
14540
+ }
14541
+ return sections.join("\n");
14542
+ }
14543
+ function normalizeReadPageMode(mode, pageContent) {
14544
+ if (typeof mode === "string") {
14545
+ const normalized = mode.trim().toLowerCase();
14546
+ if (normalized === "debug") return "debug";
14547
+ if (normalized === "glance") return "glance";
14548
+ if (normalized === "full" || normalized === "summary" || normalized === "interactives_only" || normalized === "forms_only" || normalized === "text_only" || normalized === "visible_only" || normalized === "results_only") {
14549
+ return normalized;
14550
+ }
14551
+ }
14552
+ return pageContent ? chooseAgentReadMode(pageContent) : "visible_only";
14553
+ }
14554
+ async function getPostNavSummary(wc) {
14555
+ const title = wc.getTitle();
14556
+ const titleLine = title ? `
14557
+ Page title: ${title}` : "";
14558
+ const overlaySignal = await executePageScript(
14559
+ wc,
14560
+ `(function() {
14561
+ var signals = [];
14562
+ var bodyStyle = window.getComputedStyle(document.body);
14563
+ var htmlStyle = window.getComputedStyle(document.documentElement);
14564
+ if (bodyStyle.overflow === 'hidden' || htmlStyle.overflow === 'hidden') {
14565
+ signals.push('body-scroll-locked');
14566
+ }
14567
+ var consentSelectors = [
14568
+ '#onetrust-consent-sdk', '#CybotCookiebotDialog', '[class*="consent-banner"]',
14569
+ '[class*="cookie-banner"]', '[class*="privacy-banner"]', '[id*="consent"]',
14570
+ '[class*="gdpr"]', '[data-testid*="consent"]', '[data-testid*="cookie"]',
14571
+ '.fc-consent-root', '#sp_message_container_', '[id*="trustarc"]',
14572
+ '[class*="cmp-"]', '[id*="cmp-"]'
14573
+ ];
14574
+ for (var i = 0; i < consentSelectors.length; i++) {
14575
+ try {
14576
+ var el = document.querySelector(consentSelectors[i]);
14577
+ if (el && el.offsetHeight > 50) {
14578
+ signals.push('consent-banner:' + consentSelectors[i]);
14579
+ break;
14580
+ }
14581
+ } catch(e) {}
14582
+ }
14583
+ var vw = window.innerWidth || 0;
14584
+ var vh = window.innerHeight || 0;
14585
+ var vpArea = Math.max(1, vw * vh);
14586
+ var els = document.querySelectorAll('dialog[open], [role="dialog"], [aria-modal="true"]');
14587
+ if (els.length > 0) signals.push('dialog-open');
14588
+ if (signals.length === 0) {
14589
+ var fixed = document.querySelectorAll('div[style*="position: fixed"], div[style*="position:fixed"]');
14590
+ for (var j = 0; j < fixed.length && j < 20; j++) {
14591
+ var r = fixed[j].getBoundingClientRect();
14592
+ if ((r.width * r.height) / vpArea > 0.3) {
14593
+ signals.push('large-fixed-overlay');
14594
+ break;
14595
+ }
14596
+ }
14597
+ }
14598
+ return signals.length > 0 ? signals.join(', ') : null;
14599
+ })()`,
14600
+ { timeoutMs: 1500, label: "overlay-probe" }
14601
+ );
14602
+ if (overlaySignal && overlaySignal !== PAGE_SCRIPT_TIMEOUT) {
14603
+ return `${titleLine}
14604
+ WARNING: Blocking overlay detected (${overlaySignal}). Call clear_overlays or accept_cookies before reading the page.`;
14605
+ }
14606
+ return titleLine;
14607
+ }
14608
+ async function getPostSearchSummary(wc) {
14609
+ await waitForLoad(wc, 2e3);
14610
+ try {
14611
+ const content = await Promise.race([
14612
+ extractContent$1(wc),
14613
+ new Promise((resolve) => setTimeout(() => resolve(null), 2500))
14614
+ ]);
14615
+ if (content && content.content.length > 0) {
14616
+ const scoped = buildScopedContext(content, "results_only");
14617
+ const truncated = scoped.length > 2600 ? `${scoped.slice(0, 2600)}
14618
+ [Search results snapshot truncated...]` : scoped;
14619
+ return `
14620
+ Search results snapshot:
14621
+ ${truncated}`;
14622
+ }
14623
+ } catch (err) {
14624
+ logger$j.warn("Failed to build post-search summary, falling back to nav summary:", err);
14625
+ }
14626
+ const fallback = await getPostNavSummary(wc);
14627
+ return fallback ? `${fallback}
14628
+ Search results snapshot unavailable. Use read_page(mode="results_only") if needed.` : `
14629
+ Search results snapshot unavailable. Use read_page(mode="results_only") if needed.`;
14630
+ }
14631
+ async function getPostClickNavSummary(wc, toolProfile) {
14632
+ try {
14633
+ const content = await Promise.race([
14634
+ extractContent$1(wc),
14635
+ new Promise((resolve) => setTimeout(() => resolve(null), 3e3))
14636
+ ]);
14637
+ if (content && content.content.length > 0) {
14638
+ const scoped = toolProfile === "compact" ? buildCompactScopedContext(content, "visible_only") : buildScopedContext(content, "visible_only");
14639
+ const maxLen = toolProfile === "compact" ? 1800 : 3e3;
14640
+ const truncated = scoped.length > maxLen ? `${scoped.slice(0, maxLen)}
14641
+ [Page snapshot truncated. Use read_page for full details.]` : scoped;
14642
+ return `
14643
+ Page snapshot after navigation:
14644
+ ${truncated}`;
14645
+ }
14646
+ } catch (err) {
14647
+ logger$j.warn("Failed to build post-click navigation summary:", err);
14648
+ }
14649
+ return "";
14650
+ }
14651
+ function resolveBookmarkFolderTarget$1(args) {
14652
+ const folderId = typeof args.folderId === "string" ? args.folderId.trim() : typeof args.folder_id === "string" ? args.folder_id.trim() : "";
14653
+ if (folderId) {
14654
+ if (folderId === UNSORTED_ID) {
14655
+ return {
14656
+ folderId: UNSORTED_ID,
14657
+ folderName: "Unsorted"
14658
+ };
14659
+ }
14660
+ const folder2 = getFolder(folderId);
14661
+ if (!folder2) {
14662
+ return { folderName: "Unsorted", error: `Folder ${folderId} not found` };
14663
+ }
14664
+ return { folderId: folder2.id, folderName: folder2.name };
14665
+ }
14666
+ const folderName = typeof args.folderName === "string" && args.folderName.trim() ? args.folderName.trim() : typeof args.folder_name === "string" && args.folder_name.trim() ? args.folder_name.trim() : args.archive ? ARCHIVE_FOLDER_NAME : "";
14667
+ if (!folderName || folderName.toLowerCase() === "unsorted") {
14668
+ return {
14669
+ folderId: UNSORTED_ID,
14670
+ folderName: "Unsorted"
14671
+ };
14672
+ }
14673
+ const existing = findFolderByName(folderName);
14674
+ if (existing) {
14675
+ return { folderId: existing.id, folderName: existing.name };
14676
+ }
14677
+ const createIfMissing = args.createFolderIfMissing ?? args.create_folder_if_missing;
14678
+ if (createIfMissing === false) {
14679
+ return { folderName, error: `Folder "${folderName}" not found` };
14680
+ }
14681
+ const folderSummary = typeof args.folderSummary === "string" && args.folderSummary.trim() ? args.folderSummary.trim() : typeof args.folder_summary === "string" && args.folder_summary.trim() ? args.folder_summary.trim() : void 0;
14682
+ const { folder } = ensureFolder(folderName, folderSummary);
14683
+ return {
14684
+ folderId: folder.id,
14685
+ folderName: folder.name,
14686
+ createdFolder: folder.name
14687
+ };
14688
+ }
14689
+ function formatFolderStatus(limit = 6) {
14690
+ const folders = listFolderOverviews();
14691
+ const summary = folders.slice(0, limit).map((folder) => `${folder.name} (${folder.count})`).join(", ");
14692
+ return `Folder status: ${summary}${folders.length > limit ? ", ..." : ""}`;
14693
+ }
14694
+ function describeFolder$1(folderId) {
14695
+ if (!folderId || folderId === UNSORTED_ID) {
14696
+ return "Unsorted";
14697
+ }
14698
+ return getFolder(folderId)?.name ?? folderId;
14699
+ }
14700
+ function composeDuplicateBookmarkResponse$1(args) {
14701
+ return `Bookmark already exists for ${args.url} in "${args.folderName}" (id=${args.bookmarkId}). Retry with onDuplicate="update" to refresh the existing bookmark or onDuplicate="duplicate" to keep both entries.`;
14702
+ }
14703
+ function composeFolderAwareResponse$1(message, createdFolder) {
14704
+ const prefix = createdFolder ? `Created folder "${createdFolder}".
14705
+ ` : "";
14706
+ return `${prefix}${message}
14707
+ ${formatFolderStatus()}`;
14708
+ }
14709
+ const HUGGING_FACE_HUB_HOSTS = /* @__PURE__ */ new Set(["huggingface.co", "www.huggingface.co"]);
14380
14710
  const HUGGING_FACE_MODEL_TASKS = [
14381
14711
  {
14382
14712
  value: "automatic-speech-recognition",
@@ -14613,423 +14943,106 @@ function buildHuggingFaceSearchShortcut(currentUrl, rawQuery) {
14613
14943
  const parameterResult = mapHuggingFaceParameterBucket(remainingQuery);
14614
14944
  remainingQuery = parameterResult.remainingQuery;
14615
14945
  if (parameterResult.value && parameterResult.label) {
14616
- target.searchParams.append("num_parameters", parameterResult.value);
14617
- appliedFilters.push(parameterResult.label);
14618
- }
14619
- }
14620
- remainingQuery = collapseSearchTerms(
14621
- remainingQuery.replace(/\bmodels?\b/gi, " ").replace(/\bdatasets?\b/gi, " ").replace(/\bspaces?\b/gi, " ").replace(/\bapps?\b/gi, " ")
14622
- );
14623
- if (remainingQuery) {
14624
- target.searchParams.set("search", remainingQuery);
14625
- }
14626
- if (!remainingQuery && appliedFilters.length === 0) {
14627
- return null;
14628
- }
14629
- if (section === "spaces" && remainingQuery) {
14630
- target.searchParams.set("includeNonRunning", "true");
14631
- }
14632
- return {
14633
- url: target.toString(),
14634
- source: "Hugging Face",
14635
- section,
14636
- appliedFilters
14637
- };
14638
- }
14639
- function normalizeSearchQuery(query) {
14640
- return query.replace(/\s+/g, " ").trim();
14641
- }
14642
- const COMMON_SEARCH_QUERY_PARAMS = [
14643
- "search",
14644
- "q",
14645
- "query",
14646
- "keyword",
14647
- "keywords",
14648
- "term",
14649
- "text"
14650
- ];
14651
- const COMMON_PAGINATION_PARAMS = [
14652
- "p",
14653
- "page",
14654
- "offset",
14655
- "start",
14656
- "cursor",
14657
- "skip"
14658
- ];
14659
- function looksLikeSearchResultsPath(pathname) {
14660
- return /\/(search|results|browse|discover|find)(\/|$)/i.test(pathname);
14661
- }
14662
- function buildCommonSearchUrlShortcut(currentUrl, rawQuery) {
14663
- let url;
14664
- try {
14665
- url = new URL(currentUrl);
14666
- } catch {
14667
- return null;
14668
- }
14669
- if (!/^https?:$/i.test(url.protocol)) {
14670
- return null;
14671
- }
14672
- const query = normalizeSearchQuery(rawQuery);
14673
- if (!query) return null;
14674
- const existingParam = COMMON_SEARCH_QUERY_PARAMS.find(
14675
- (param) => url.searchParams.has(param)
14676
- );
14677
- if (!existingParam && !looksLikeSearchResultsPath(url.pathname)) {
14678
- return null;
14679
- }
14680
- const target = new URL(url.toString());
14681
- const searchParam = existingParam ?? "q";
14682
- target.searchParams.set(searchParam, query);
14683
- for (const param of COMMON_PAGINATION_PARAMS) {
14684
- target.searchParams.delete(param);
14685
- }
14686
- if (target.toString() === url.toString()) {
14687
- return null;
14688
- }
14689
- return {
14690
- url: target.toString(),
14691
- source: "page URL",
14692
- appliedFilters: existingParam ? [`updated ${existingParam} query`] : []
14693
- };
14694
- }
14695
- function buildDefaultEngineShortcut(rawQuery) {
14696
- const settings2 = loadSettings();
14697
- const engineId = settings2.defaultSearchEngine ?? "duckduckgo";
14698
- if (engineId === "none") return null;
14699
- const preset = SEARCH_ENGINE_PRESETS[engineId];
14700
- if (!preset) return null;
14701
- const query = normalizeSearchQuery(rawQuery);
14702
- if (!query) return null;
14703
- return {
14704
- url: preset.url + encodeURIComponent(query),
14705
- source: "default search engine",
14706
- appliedFilters: []
14707
- };
14708
- }
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
- function getBookmarkMetadataFromArgs(args) {
14722
- return normalizeBookmarkMetadata({
14723
- intent: args.intent ?? args.intent,
14724
- expectedContent: args.expectedContent ?? args.expected_content,
14725
- keyFields: args.keyFields ?? args.key_fields,
14726
- agentHints: args.agentHints ?? args.agent_hints
14727
- });
14728
- }
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;
14946
+ target.searchParams.append("num_parameters", parameterResult.value);
14947
+ appliedFilters.push(parameterResult.label);
14882
14948
  }
14883
14949
  }
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
14950
+ remainingQuery = collapseSearchTerms(
14951
+ remainingQuery.replace(/\bmodels?\b/gi, " ").replace(/\bdatasets?\b/gi, " ").replace(/\bspaces?\b/gi, " ").replace(/\bapps?\b/gi, " ")
14891
14952
  );
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);
14953
+ if (remainingQuery) {
14954
+ target.searchParams.set("search", remainingQuery);
14955
+ }
14956
+ if (!remainingQuery && appliedFilters.length === 0) {
14907
14957
  return null;
14908
- } finally {
14909
- if (timer) {
14910
- clearTimeout(timer);
14911
- }
14912
14958
  }
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);
14959
+ if (section === "spaces" && remainingQuery) {
14960
+ target.searchParams.set("includeNonRunning", "true");
14924
14961
  }
14962
+ return {
14963
+ url: target.toString(),
14964
+ source: "Hugging Face",
14965
+ section,
14966
+ appliedFilters
14967
+ };
14925
14968
  }
14926
- function waitForPotentialNavigation(wc, beforeUrl, timeout = 2500) {
14927
- return waitForPotentialNavigation$1(
14928
- wc,
14929
- beforeUrl,
14930
- timeout,
14931
- QUIET_NAVIGATION_WINDOW_MS
14932
- );
14969
+ function normalizeSearchQuery(query) {
14970
+ return query.replace(/\s+/g, " ").trim();
14933
14971
  }
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;
14972
+ const COMMON_SEARCH_QUERY_PARAMS = [
14973
+ "search",
14974
+ "q",
14975
+ "query",
14976
+ "keyword",
14977
+ "keywords",
14978
+ "term",
14979
+ "text"
14980
+ ];
14981
+ const COMMON_PAGINATION_PARAMS = [
14982
+ "p",
14983
+ "page",
14984
+ "offset",
14985
+ "start",
14986
+ "cursor",
14987
+ "skip"
14988
+ ];
14989
+ function looksLikeSearchResultsPath(pathname) {
14990
+ return /\/(search|results|browse|discover|find)(\/|$)/i.test(pathname);
14990
14991
  }
14991
- async function getPostSearchSummary(wc) {
14992
- await waitForLoad(wc, 2e3);
14992
+ function buildCommonSearchUrlShortcut(currentUrl, rawQuery) {
14993
+ let url;
14993
14994
  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);
14995
+ url = new URL(currentUrl);
14996
+ } catch {
14997
+ return null;
15008
14998
  }
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);
14999
+ if (!/^https?:$/i.test(url.protocol)) {
15000
+ return null;
15031
15001
  }
15032
- return "";
15002
+ const query = normalizeSearchQuery(rawQuery);
15003
+ if (!query) return null;
15004
+ const existingParam = COMMON_SEARCH_QUERY_PARAMS.find(
15005
+ (param) => url.searchParams.has(param)
15006
+ );
15007
+ if (!existingParam && !looksLikeSearchResultsPath(url.pathname)) {
15008
+ return null;
15009
+ }
15010
+ const target = new URL(url.toString());
15011
+ const searchParam = existingParam ?? "q";
15012
+ target.searchParams.set(searchParam, query);
15013
+ for (const param of COMMON_PAGINATION_PARAMS) {
15014
+ target.searchParams.delete(param);
15015
+ }
15016
+ if (target.toString() === url.toString()) {
15017
+ return null;
15018
+ }
15019
+ return {
15020
+ url: target.toString(),
15021
+ source: "page URL",
15022
+ appliedFilters: existingParam ? [`updated ${existingParam} query`] : []
15023
+ };
15024
+ }
15025
+ function buildDefaultEngineShortcut(rawQuery) {
15026
+ const settings2 = loadSettings();
15027
+ const engineId = settings2.defaultSearchEngine ?? "duckduckgo";
15028
+ if (engineId === "none") return null;
15029
+ const preset = SEARCH_ENGINE_PRESETS[engineId];
15030
+ if (!preset) return null;
15031
+ const query = normalizeSearchQuery(rawQuery);
15032
+ if (!query) return null;
15033
+ return {
15034
+ url: preset.url + encodeURIComponent(query),
15035
+ source: "default search engine",
15036
+ appliedFilters: []
15037
+ };
15038
+ }
15039
+ function getBookmarkMetadataFromArgs(args) {
15040
+ return normalizeBookmarkMetadata({
15041
+ intent: args.intent ?? args.intent,
15042
+ expectedContent: args.expectedContent ?? args.expected_content,
15043
+ keyFields: args.keyFields ?? args.key_fields,
15044
+ agentHints: args.agentHints ?? args.agent_hints
15045
+ });
15033
15046
  }
15034
15047
  async function scrollPage(wc, deltaY) {
15035
15048
  const getScrollY = async () => {
@@ -15594,6 +15607,19 @@ Click one of these dialog actions. Do NOT click any other element.` : "";
15594
15607
  return `
15595
15608
  ${overlayHint}${actionsSuffix}${cartSummary}`;
15596
15609
  }
15610
+ async function followHrefFromClickResult(wc, beforeUrl, result, logMessage) {
15611
+ const hrefMatch = typeof result === "string" ? result.match(/\nhref: (https?:\/\/\S+)/) : null;
15612
+ if (!hrefMatch) return null;
15613
+ try {
15614
+ await loadPermittedUrl(wc, hrefMatch[1]);
15615
+ await waitForLoad(wc, 8e3);
15616
+ const hrefUrl = wc.getURL();
15617
+ if (hrefUrl !== beforeUrl) return `${result.split("\n")[0]} -> ${hrefUrl}`;
15618
+ } catch (err) {
15619
+ logger$j.warn(logMessage, err);
15620
+ }
15621
+ return null;
15622
+ }
15597
15623
  async function clickResolvedSelector(wc, selector) {
15598
15624
  if (selector.startsWith("__vessel_idx:")) {
15599
15625
  const idx = Number(selector.slice("__vessel_idx:".length));
@@ -15636,16 +15662,13 @@ Go back and select a different product.`;
15636
15662
  return `${result}${await buildCartSuccessSuffix(wc, beforeUrl2, idxOverlay)}`;
15637
15663
  }
15638
15664
  if (!idxOverlay) {
15639
- const hrefMatch = typeof result === "string" ? result.match(/\nhref: (https?:\/\/\S+)/) : null;
15640
- if (hrefMatch) {
15641
- try {
15642
- await loadPermittedUrl(wc, hrefMatch[1]);
15643
- await waitForLoad(wc, 8e3);
15644
- const hrefUrl = wc.getURL();
15645
- if (hrefUrl !== beforeUrl2) return `${result.split("\n")[0]} -> ${hrefUrl}`;
15646
- } catch {
15647
- }
15648
- }
15665
+ const hrefFallback = await followHrefFromClickResult(
15666
+ wc,
15667
+ beforeUrl2,
15668
+ result,
15669
+ "Failed to follow href fallback after click:"
15670
+ );
15671
+ if (hrefFallback) return hrefFallback;
15649
15672
  }
15650
15673
  return idxOverlay ? `${result}
15651
15674
  ${idxOverlay}` : `${result}
@@ -15703,16 +15726,13 @@ Go back and select a different product.`;
15703
15726
  return `${result}${await buildCartSuccessSuffix(wc, beforeUrl2, shadowOverlay)}`;
15704
15727
  }
15705
15728
  if (!shadowOverlay) {
15706
- const hrefMatch = typeof result === "string" ? result.match(/\nhref: (https?:\/\/\S+)/) : null;
15707
- if (hrefMatch) {
15708
- try {
15709
- await loadPermittedUrl(wc, hrefMatch[1]);
15710
- await waitForLoad(wc, 8e3);
15711
- const hrefUrl = wc.getURL();
15712
- if (hrefUrl !== beforeUrl2) return `${result.split("\n")[0]} -> ${hrefUrl}`;
15713
- } catch {
15714
- }
15715
- }
15729
+ const hrefFallback = await followHrefFromClickResult(
15730
+ wc,
15731
+ beforeUrl2,
15732
+ result,
15733
+ "Failed to follow href fallback after shadow click:"
15734
+ );
15735
+ if (hrefFallback) return hrefFallback;
15716
15736
  }
15717
15737
  return shadowOverlay ? `${result}
15718
15738
  ${shadowOverlay}` : `${result}
@@ -19886,7 +19906,7 @@ function parse(raw) {
19886
19906
  const items = Array.isArray(raw.items) ? raw.items : [];
19887
19907
  return { items };
19888
19908
  }
19889
- let state$2 = loadJsonFile({ filePath: filePath$1(), fallback: { items: [] }, parse });
19909
+ const state$2 = loadJsonFile({ filePath: filePath$1(), fallback: { items: [] }, parse });
19890
19910
  const persistence$2 = createDebouncedJsonPersistence({
19891
19911
  debounceMs: 250,
19892
19912
  filePath: filePath$1(),
@@ -20188,8 +20208,8 @@ function registerPrivateIpcHandlers(state2) {
20188
20208
  ipc.handle(Channels.OPEN_PRIVATE_WINDOW, () => {
20189
20209
  createPrivateWindow();
20190
20210
  });
20191
- ipc.handle(Channels.OPEN_NEW_WINDOW, () => {
20192
- const { createSecondaryWindow: createSecondaryWindow2 } = require("../secondary/window");
20211
+ ipc.handle(Channels.OPEN_NEW_WINDOW, async () => {
20212
+ const { createSecondaryWindow: createSecondaryWindow2 } = await Promise.resolve().then(() => window$1);
20193
20213
  createSecondaryWindow2();
20194
20214
  });
20195
20215
  ipc.handle(Channels.WINDOW_MINIMIZE, () => {
@@ -20254,9 +20274,7 @@ function createPrivateWindow() {
20254
20274
  const tabManager = new TabManager(
20255
20275
  win,
20256
20276
  (tabs, activeId) => {
20257
- if (!chromeView.webContents.isDestroyed()) {
20258
- chromeView.webContents.send(Channels.TAB_STATE_UPDATE, tabs, activeId);
20259
- }
20277
+ sendSafe(chromeView.webContents, Channels.TAB_STATE_UPDATE, tabs, activeId);
20260
20278
  layoutPrivateViews(state2);
20261
20279
  },
20262
20280
  { isPrivate: true, sessionPartition: privateSessionPartition }
@@ -20293,6 +20311,10 @@ function createPrivateWindow() {
20293
20311
  logger$h.info("Private browsing window opened");
20294
20312
  return state2;
20295
20313
  }
20314
+ const window$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
20315
+ __proto__: null,
20316
+ createPrivateWindow
20317
+ }, Symbol.toStringTag, { value: "Module" }));
20296
20318
  const secondaryWindows = /* @__PURE__ */ new Set();
20297
20319
  function layoutSecondaryViews(state2) {
20298
20320
  const { window: win, chromeView, tabManager } = state2;
@@ -20408,8 +20430,8 @@ function registerSecondaryIpcHandlers(state2) {
20408
20430
  (_e, groupId) => showGroupContextMenu(state2.tabManager, groupId, state2.window)
20409
20431
  );
20410
20432
  ipc.handle(Channels.OPEN_NEW_WINDOW, () => createSecondaryWindow());
20411
- ipc.handle(Channels.OPEN_PRIVATE_WINDOW, () => {
20412
- const { createPrivateWindow: createPrivateWindow2 } = require("../private/window");
20433
+ ipc.handle(Channels.OPEN_PRIVATE_WINDOW, async () => {
20434
+ const { createPrivateWindow: createPrivateWindow2 } = await Promise.resolve().then(() => window$2);
20413
20435
  createPrivateWindow2();
20414
20436
  });
20415
20437
  ipc.handle(Channels.IS_PRIVATE_MODE, () => false);
@@ -20461,9 +20483,7 @@ function createSecondaryWindow() {
20461
20483
  chromeView.setBackgroundColor("#00000000");
20462
20484
  win.contentView.addChildView(chromeView);
20463
20485
  const tabManager = new TabManager(win, (tabs, activeId) => {
20464
- if (!chromeView.webContents.isDestroyed()) {
20465
- chromeView.webContents.send(Channels.TAB_STATE_UPDATE, tabs, activeId);
20466
- }
20486
+ sendSafe(chromeView.webContents, Channels.TAB_STATE_UPDATE, tabs, activeId);
20467
20487
  layoutSecondaryViews(state2);
20468
20488
  });
20469
20489
  const state2 = { window: win, chromeView, tabManager };
@@ -20487,12 +20507,16 @@ function createSecondaryWindow() {
20487
20507
  win.show();
20488
20508
  return state2;
20489
20509
  }
20510
+ const window$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
20511
+ __proto__: null,
20512
+ createSecondaryWindow
20513
+ }, Symbol.toStringTag, { value: "Module" }));
20490
20514
  const TabIdSchema = zod.z.string().min(1);
20491
20515
  const GroupIdSchema = zod.z.string().min(1);
20492
20516
  const UrlSchema = zod.z.string().min(1);
20493
20517
  const ColorSchema = zod.z.string().min(1);
20494
20518
  const FindActionSchema = zod.z.enum(["clearSelection", "keepSelection", "activateSelection"]);
20495
- function registerTabHandlers(windowState, sendToRendererViews) {
20519
+ function registerTabHandlers(windowState, _sendToRendererViews) {
20496
20520
  const { tabManager, mainWindow } = windowState;
20497
20521
  electron.ipcMain.handle(Channels.OPEN_PRIVATE_WINDOW, (event) => {
20498
20522
  assertTrustedIpcSender(event);
@@ -21131,7 +21155,7 @@ async function handleAIQuery(query, provider, activeWebContents, onChunk, onEnd,
21131
21155
  const isSummarize = lowerQuery.startsWith("summarize") || lowerQuery.startsWith("tldr") || lowerQuery === "summary";
21132
21156
  if (provider.streamAgentQuery && tabManager && activeWebContents && runtime2) {
21133
21157
  try {
21134
- const pageContent = await extractContent(activeWebContents);
21158
+ const pageContent = await extractContent$1(activeWebContents);
21135
21159
  const pageType = detectPageType(pageContent);
21136
21160
  const defaultReadMode = chooseAgentReadMode(pageContent);
21137
21161
  if (provider.agentToolProfile === "compact") {
@@ -21232,7 +21256,7 @@ ${trackerCtx}`;
21232
21256
  let prompt;
21233
21257
  if (activeWebContents) {
21234
21258
  try {
21235
- const pageContent = await extractContent(activeWebContents);
21259
+ const pageContent = await extractContent$1(activeWebContents);
21236
21260
  if (isSummarize) {
21237
21261
  prompt = buildSummarizePrompt(pageContent);
21238
21262
  } else {
@@ -21492,7 +21516,7 @@ function registerContentHandlers(windowState) {
21492
21516
  assertTrustedIpcSender(event);
21493
21517
  const activeTab = tabManager.getActiveTab();
21494
21518
  if (!activeTab) return null;
21495
- return extractContent(activeTab.view.webContents);
21519
+ return extractContent$1(activeTab.view.webContents);
21496
21520
  });
21497
21521
  electron.ipcMain.handle(Channels.READER_MODE_TOGGLE, async (event) => {
21498
21522
  assertTrustedIpcSender(event);
@@ -21506,7 +21530,7 @@ function registerContentHandlers(windowState) {
21506
21530
  }
21507
21531
  } else {
21508
21532
  const originalUrl = activeTab.state.url;
21509
- const content = await extractContent(activeTab.view.webContents);
21533
+ const content = await extractContent$1(activeTab.view.webContents);
21510
21534
  const html = generateReaderHTML(content);
21511
21535
  activeTab.setReaderMode(true, originalUrl);
21512
21536
  void loadInternalDataURL(
@@ -22631,7 +22655,7 @@ function requiresExplicitMcpApproval(name, args) {
22631
22655
  if (name === "remove_bookmark_folder" && args.delete_contents === true) return true;
22632
22656
  return false;
22633
22657
  }
22634
- function getActiveTabSummary(tabManager) {
22658
+ function getActiveTabSummary$1(tabManager) {
22635
22659
  const activeTab = tabManager.getActiveTab();
22636
22660
  const activeTabId = tabManager.getActiveTabId();
22637
22661
  if (!activeTab || !activeTabId) return null;
@@ -22671,7 +22695,7 @@ async function getPostActionState(tabManager, name) {
22671
22695
  if (navActions.includes(name)) {
22672
22696
  let warning = "";
22673
22697
  try {
22674
- const page = await extractContent(wc);
22698
+ const page = await extractContent$1(wc);
22675
22699
  const issue = getRecoverableAccessIssue(page);
22676
22700
  if (issue) {
22677
22701
  const blockedUrl = wc.getURL();
@@ -22704,7 +22728,7 @@ async function getPostActionState(tabManager, name) {
22704
22728
  }
22705
22729
  if (tabActions.includes(name)) {
22706
22730
  const activeId = tabManager.getActiveTabId();
22707
- const active = getActiveTabSummary(tabManager);
22731
+ const active = getActiveTabSummary$1(tabManager);
22708
22732
  const count = tabManager.getAllStates().length;
22709
22733
  return `
22710
22734
  [state: activeTab=${activeId}, title=${JSON.stringify(active?.title ?? "")}, url=${active?.url ?? ""}, totalTabs=${count}]`;
@@ -24148,7 +24172,7 @@ function registerTools(server, tabManager, runtime2) {
24148
24172
  const wc = activeTab.view.webContents;
24149
24173
  pageUrl = wc.getURL();
24150
24174
  pageTitle = wc.getTitle();
24151
- const page = await extractContent(wc);
24175
+ const page = await extractContent$1(wc);
24152
24176
  pageType = detectPageType(page);
24153
24177
  } catch (err) {
24154
24178
  logger$a.warn("Failed to detect page type for tool scoring, falling back to GENERAL:", err);
@@ -24314,7 +24338,7 @@ ${buildScopedContext(pageContent, mode)}`;
24314
24338
  const tab = tabManager.getActiveTab();
24315
24339
  if (!tab) return asNoActiveTabResponse();
24316
24340
  try {
24317
- const pageContent = await extractContent(tab.view.webContents);
24341
+ const pageContent = await extractContent$1(tab.view.webContents);
24318
24342
  const effectiveMode = mode || "full";
24319
24343
  return asTextResponse(
24320
24344
  await buildExtractResponse(
@@ -24346,7 +24370,7 @@ ${buildScopedContext(pageContent, mode)}`;
24346
24370
  const tab = tabManager.getActiveTab();
24347
24371
  if (!tab) return asNoActiveTabResponse();
24348
24372
  try {
24349
- const pageContent = await extractContent(tab.view.webContents);
24373
+ const pageContent = await extractContent$1(tab.view.webContents);
24350
24374
  const effectiveMode = mode || "full";
24351
24375
  return asTextResponse(
24352
24376
  await buildExtractResponse(
@@ -24480,7 +24504,7 @@ ${buildScopedContext(pageContent, mode)}`;
24480
24504
  const tab = tabManager.getActiveTab();
24481
24505
  if (!tab) return asNoActiveTabResponse();
24482
24506
  try {
24483
- const pageContent = await extractContent(tab.view.webContents);
24507
+ const pageContent = await extractContent$1(tab.view.webContents);
24484
24508
  const requestedType = typeof type === "string" && type.trim() ? type.trim().toLowerCase() : "";
24485
24509
  const entities = (pageContent.structuredData ?? []).filter(
24486
24510
  (entity) => requestedType ? entity.types.some(
@@ -25583,7 +25607,7 @@ ${JSON.stringify(otherHighlights, null, 2)}`
25583
25607
  "memory_page_capture",
25584
25608
  { title, folder, tags },
25585
25609
  async () => {
25586
- const page = await extractContent(tab.view.webContents);
25610
+ const page = await extractContent$1(tab.view.webContents);
25587
25611
  const saved = capturePageToVault({
25588
25612
  page,
25589
25613
  title,
@@ -25704,7 +25728,7 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
25704
25728
  const wc = tab.view.webContents;
25705
25729
  let page;
25706
25730
  try {
25707
- page = await extractContent(wc);
25731
+ page = await extractContent$1(wc);
25708
25732
  } catch (err) {
25709
25733
  logger$a.warn("Failed to extract page while generating suggestions:", err);
25710
25734
  return asTextResponse(
@@ -25720,7 +25744,6 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
25720
25744
  suggestions.push(flowCtx);
25721
25745
  suggestions.push("");
25722
25746
  }
25723
- page.url.toLowerCase();
25724
25747
  const hasPasswordField = page.forms.some(
25725
25748
  (f) => f.fields.some((el) => el.inputType === "password")
25726
25749
  );
@@ -27263,7 +27286,7 @@ const BUNDLED_KIT_IDS = /* @__PURE__ */ new Set([
27263
27286
  "price-scout",
27264
27287
  "form-filler"
27265
27288
  ]);
27266
- const KIT_ID_UNSAFE_CHAR_PATTERN = /[\/\\\0]/;
27289
+ const KIT_ID_UNSAFE_CHAR_PATTERN = /[/\\\0]/;
27267
27290
  function isSafeAutomationKitId(id) {
27268
27291
  return id.length > 0 && !KIT_ID_UNSAFE_CHAR_PATTERN.test(id);
27269
27292
  }
@@ -27294,7 +27317,8 @@ function getInstalledKits() {
27294
27317
  let files;
27295
27318
  try {
27296
27319
  files = fs$1.readdirSync(dir).filter((f) => f.endsWith(".kit.json"));
27297
- } catch {
27320
+ } catch (err) {
27321
+ logger$9.warn("Failed to read kit directory:", err);
27298
27322
  return [];
27299
27323
  }
27300
27324
  const kits = [];
@@ -27325,13 +27349,15 @@ async function installKitFromFile() {
27325
27349
  let raw;
27326
27350
  try {
27327
27351
  raw = fs$1.readFileSync(filePaths[0], "utf-8");
27328
- } catch {
27352
+ } catch (err) {
27353
+ logger$9.warn("Failed to read selected kit file:", err);
27329
27354
  return errorResult("Could not read the selected file.");
27330
27355
  }
27331
27356
  let parsed;
27332
27357
  try {
27333
27358
  parsed = JSON.parse(raw);
27334
- } catch {
27359
+ } catch (err) {
27360
+ logger$9.warn("Selected kit file is not valid JSON:", err);
27335
27361
  return errorResult("File is not valid JSON.");
27336
27362
  }
27337
27363
  if (!isValidKit(parsed)) {
@@ -27351,7 +27377,8 @@ async function installKitFromFile() {
27351
27377
  }
27352
27378
  try {
27353
27379
  fs$1.writeFileSync(dest, JSON.stringify(parsed, null, 2), "utf-8");
27354
- } catch {
27380
+ } catch (err) {
27381
+ logger$9.warn("Failed to save kit file:", err);
27355
27382
  return errorResult("Failed to save the kit file.");
27356
27383
  }
27357
27384
  return okResult({ kit: parsed });
@@ -27376,7 +27403,8 @@ function uninstallKit(id, scheduledKitIds) {
27376
27403
  try {
27377
27404
  fs$1.unlinkSync(target);
27378
27405
  return okResult();
27379
- } catch {
27406
+ } catch (err) {
27407
+ logger$9.warn("Failed to remove kit file:", err);
27380
27408
  return errorResult("Failed to remove the kit file.");
27381
27409
  }
27382
27410
  }
@@ -29133,7 +29161,7 @@ function registerAutofillHandlers(windowState) {
29133
29161
  const activeTab = windowState.tabManager.getActiveTab();
29134
29162
  const wc = activeTab?.view.webContents;
29135
29163
  if (!wc) throw new Error("No active tab");
29136
- const content = await extractContent(wc);
29164
+ const content = await extractContent$1(wc);
29137
29165
  const elements = content.interactiveElements || [];
29138
29166
  const matches = matchFields(elements, profile);
29139
29167
  if (matches.length === 0) {
@@ -30785,25 +30813,12 @@ async function bootstrap() {
30785
30813
  count = 0;
30786
30814
  }
30787
30815
  }
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
- }
30816
+ sendSafe(state2.chromeView.webContents, Channels.HIGHLIGHT_COUNT_UPDATE, count);
30817
+ sendSafe(state2.sidebarView.webContents, Channels.HIGHLIGHT_COUNT_UPDATE, count);
30818
+ sendSafe(state2.devtoolsPanelView.webContents, Channels.HIGHLIGHT_COUNT_UPDATE, count);
30800
30819
  };
30801
30820
  const windowState = createMainWindow((tabs, activeId, meta) => {
30802
- windowState.chromeView.webContents.send(
30803
- Channels.TAB_STATE_UPDATE,
30804
- tabs,
30805
- activeId
30806
- );
30821
+ sendSafe(windowState.chromeView.webContents, Channels.TAB_STATE_UPDATE, tabs, activeId);
30807
30822
  void syncActiveHighlightCount(windowState);
30808
30823
  layoutViews(windowState);
30809
30824
  if (meta.persistSession) {
@@ -30826,18 +30841,12 @@ async function bootstrap() {
30826
30841
  runtime = new AgentRuntime(tabManager);
30827
30842
  installAdBlocking(tabManager);
30828
30843
  setDevToolsPanelListener((state2) => {
30829
- if (!devtoolsPanelView.webContents.isDestroyed()) {
30830
- devtoolsPanelView.webContents.send(Channels.DEVTOOLS_PANEL_STATE, state2);
30831
- }
30844
+ sendSafe(devtoolsPanelView.webContents, Channels.DEVTOOLS_PANEL_STATE, state2);
30832
30845
  });
30833
30846
  registerIpcHandlers(windowState, runtime);
30834
30847
  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
- }
30848
+ sendSafe(chromeView.webContents, Channels.SECURITY_STATE_UPDATE, { tabId, state: state2 });
30849
+ sendSafe(sidebarView.webContents, Channels.SECURITY_STATE_UPDATE, { tabId, state: state2 });
30841
30850
  });
30842
30851
  registerHighlightShortcut(windowState.mainWindow, tabManager);
30843
30852
  setupAppMenu({
@@ -30871,21 +30880,19 @@ async function bootstrap() {
30871
30880
  }
30872
30881
  },
30873
30882
  clearBrowsingData: () => {
30874
- if (!chromeView.webContents.isDestroyed()) {
30875
- chromeView.webContents.send(Channels.CLEAR_BROWSING_DATA_OPEN);
30876
- }
30883
+ sendSafe(chromeView.webContents, Channels.CLEAR_BROWSING_DATA_OPEN);
30877
30884
  },
30878
30885
  togglePictureInPicture: () => {
30879
30886
  void togglePictureInPicture(tabManager);
30880
30887
  }
30881
30888
  });
30882
30889
  subscribe((state2) => {
30883
- chromeView.webContents.send(Channels.BOOKMARKS_UPDATE, state2);
30884
- sidebarView.webContents.send(Channels.BOOKMARKS_UPDATE, state2);
30890
+ sendSafe(chromeView.webContents, Channels.BOOKMARKS_UPDATE, state2);
30891
+ sendSafe(sidebarView.webContents, Channels.BOOKMARKS_UPDATE, state2);
30885
30892
  });
30886
30893
  subscribe$1((state2) => {
30887
- chromeView.webContents.send(Channels.HISTORY_UPDATE, state2);
30888
- sidebarView.webContents.send(Channels.HISTORY_UPDATE, state2);
30894
+ sendSafe(chromeView.webContents, Channels.HISTORY_UPDATE, state2);
30895
+ sendSafe(sidebarView.webContents, Channels.HISTORY_UPDATE, state2);
30889
30896
  });
30890
30897
  installDownloadHandler(chromeView);
30891
30898
  installPermissionHandler();