@quanta-intellect/vessel-browser 0.1.68 → 0.1.69

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
@@ -65,6 +65,7 @@ const defaults = {
65
65
  domainPolicy: { allowedDomains: [], blockedDomains: [] },
66
66
  downloadPath: "",
67
67
  telemetryEnabled: true,
68
+ defaultSearchEngine: "duckduckgo",
68
69
  premium: {
69
70
  status: "free",
70
71
  customerId: "",
@@ -3754,6 +3755,24 @@ const VERIFICATION_API = process.env.VESSEL_PREMIUM_API || "https://vesselpremiu
3754
3755
  const FREE_TOOL_ITERATION_LIMIT = 50;
3755
3756
  const REVALIDATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
3756
3757
  const OFFLINE_GRACE_PERIOD_MS = 7 * 24 * 60 * 60 * 1e3;
3758
+ const MAX_API_ERROR_LOG_LENGTH = 300;
3759
+ async function readApiErrorDetail(res) {
3760
+ try {
3761
+ const text = (await res.text()).trim();
3762
+ if (!text) return "";
3763
+ try {
3764
+ const data = JSON.parse(text);
3765
+ const detail = data.error || data.message;
3766
+ if (typeof detail === "string" && detail.trim()) {
3767
+ return detail.trim().slice(0, MAX_API_ERROR_LOG_LENGTH);
3768
+ }
3769
+ } catch {
3770
+ }
3771
+ return text.slice(0, MAX_API_ERROR_LOG_LENGTH);
3772
+ } catch {
3773
+ return "";
3774
+ }
3775
+ }
3757
3776
  const PREMIUM_TOOLS = /* @__PURE__ */ new Set([
3758
3777
  "screenshot",
3759
3778
  "save_session",
@@ -3843,7 +3862,12 @@ async function verifySubscription(identifier) {
3843
3862
  body: JSON.stringify({ identifier: verificationIdentifier })
3844
3863
  });
3845
3864
  if (!res.ok) {
3846
- logger$f.warn("Verification API returned a non-OK status:", res.status);
3865
+ const detail = await readApiErrorDetail(res);
3866
+ logger$f.warn(
3867
+ "Verification API returned a non-OK status:",
3868
+ res.status,
3869
+ detail
3870
+ );
3847
3871
  return current;
3848
3872
  }
3849
3873
  const data = await res.json();
@@ -6326,7 +6350,7 @@ class AnthropicProvider {
6326
6350
  });
6327
6351
  continue;
6328
6352
  }
6329
- const argSummary = [tb.input.url, tb.input.text, tb.input.direction].map((v) => typeof v === "string" ? v : "").find((v) => v.length > 0) ?? "";
6353
+ const argSummary = [tb.input.url, tb.input.query, tb.input.text, tb.input.direction].map((v) => typeof v === "string" ? v : "").find((v) => v.length > 0) ?? "";
6330
6354
  onChunk(`
6331
6355
  <<tool:${tb.name}${argSummary ? ":" + argSummary : ""}>>
6332
6356
  `);
@@ -6517,11 +6541,52 @@ const SAFE_TOOL_ALIASES = {
6517
6541
  scroll_up: "scroll",
6518
6542
  read: "read_page",
6519
6543
  read_current_page: "read_page",
6520
- scan_page: "read_page"
6544
+ scan_page: "read_page",
6545
+ save_bookmark: "save_bookmark",
6546
+ bookmark: "save_bookmark",
6547
+ bookmark_page: "save_bookmark",
6548
+ bookmark_url: "save_bookmark",
6549
+ add_bookmark: "save_bookmark",
6550
+ create_bookmark: "save_bookmark"
6521
6551
  };
6552
+ const CANONICAL_TOOL_NAMES = /* @__PURE__ */ new Set([
6553
+ "archive_bookmark",
6554
+ "click",
6555
+ "create_bookmark_folder",
6556
+ "current_tab",
6557
+ "go_back",
6558
+ "go_forward",
6559
+ "inspect_element",
6560
+ "list_bookmarks",
6561
+ "navigate",
6562
+ "open_bookmark",
6563
+ "organize_bookmark",
6564
+ "read_page",
6565
+ "save_bookmark",
6566
+ "scroll",
6567
+ "search",
6568
+ "type_text"
6569
+ ]);
6570
+ function repeatedTokenMatch(value, token) {
6571
+ if (value === token) return true;
6572
+ if (token.length === 0 || value.length <= token.length) return false;
6573
+ if (value.length % token.length !== 0) return false;
6574
+ return token.repeat(value.length / token.length) === value;
6575
+ }
6522
6576
  function normalizeToolAlias(name) {
6523
6577
  const normalized = name.trim().toLowerCase().replace(/[.\s/-]+/g, "_");
6524
- return SAFE_TOOL_ALIASES[normalized] ?? name;
6578
+ const direct = SAFE_TOOL_ALIASES[normalized] ?? normalized;
6579
+ if (CANONICAL_TOOL_NAMES.has(direct)) return direct;
6580
+ const knownTokens = [
6581
+ ...Object.keys(SAFE_TOOL_ALIASES),
6582
+ ...CANONICAL_TOOL_NAMES
6583
+ ];
6584
+ for (const token of knownTokens) {
6585
+ if (repeatedTokenMatch(normalized, token)) {
6586
+ return SAFE_TOOL_ALIASES[token] ?? token;
6587
+ }
6588
+ }
6589
+ return name;
6525
6590
  }
6526
6591
  function parseModelSizeInBillions(model) {
6527
6592
  const match = model.toLowerCase().match(/(?:^|[:/_\-\s])(\d+(?:\.\d+)?)b(?:$|[:/_\-\s])/i);
@@ -6845,8 +6910,62 @@ function scalarArgsForTool(name, scalar) {
6845
6910
  const mode = trimmed.replace(/^["']|["']$/g, "").toLowerCase();
6846
6911
  if (mode) return { mode };
6847
6912
  }
6913
+ if (name === "save_bookmark") {
6914
+ const url = toLikelyUrl(trimmed);
6915
+ if (url) return { url };
6916
+ const lastSpace = trimmed.lastIndexOf(" ");
6917
+ if (lastSpace > 0) {
6918
+ const maybeUrl = toLikelyUrl(trimmed.slice(lastSpace + 1));
6919
+ if (maybeUrl) return { url: maybeUrl, title: trimmed.slice(0, lastSpace).replace(/^["']|["']$/g, "") };
6920
+ }
6921
+ }
6922
+ return null;
6923
+ }
6924
+ function firstStringArg(args, keys) {
6925
+ for (const key of keys) {
6926
+ const value = args[key];
6927
+ if (typeof value === "string" && value.trim()) {
6928
+ return value.trim();
6929
+ }
6930
+ }
6848
6931
  return null;
6849
6932
  }
6933
+ function normalizeElementTargetArgs(args) {
6934
+ const normalized = { ...args };
6935
+ if (typeof normalized.index === "string" && /^\d+$/.test(normalized.index.trim())) {
6936
+ normalized.index = Number(normalized.index.trim());
6937
+ }
6938
+ if (typeof normalized.selector !== "string" || !normalized.selector.trim()) {
6939
+ const selector = firstStringArg(normalized, [
6940
+ "cssSelector",
6941
+ "css_selector",
6942
+ "querySelector",
6943
+ "query_selector"
6944
+ ]);
6945
+ if (selector) normalized.selector = selector;
6946
+ }
6947
+ if (typeof normalized.text !== "string" || !normalized.text.trim()) {
6948
+ const text = firstStringArg(normalized, [
6949
+ "label",
6950
+ "title",
6951
+ "name",
6952
+ "target",
6953
+ "element",
6954
+ "linkText",
6955
+ "link_text",
6956
+ "ariaLabel",
6957
+ "aria_label"
6958
+ ]);
6959
+ if (text) normalized.text = text;
6960
+ }
6961
+ return normalized;
6962
+ }
6963
+ function hasElementTarget(args) {
6964
+ return typeof args.index === "number" || typeof args.selector === "string" && args.selector.trim().length > 0 || typeof args.text === "string" && args.text.trim().length > 0;
6965
+ }
6966
+ function isTargetlessClickArgs(args) {
6967
+ return !hasElementTarget(normalizeElementTargetArgs(args));
6968
+ }
6850
6969
  function tryParseJsonWithCommonRepairs(raw) {
6851
6970
  const trimmed = raw.trim();
6852
6971
  if (!trimmed) return {};
@@ -6900,7 +7019,10 @@ function parseToolArgsWithRepair(name, argsJson) {
6900
7019
  return scalarArgs ? { args: scalarArgs, repaired: true } : null;
6901
7020
  }
6902
7021
  function coerceToolArgsForExecution(name, args) {
6903
- const coerced = { ...args };
7022
+ let coerced = { ...args };
7023
+ if (name === "click" || name === "inspect_element" || name === "scroll_to_element") {
7024
+ coerced = normalizeElementTargetArgs(coerced);
7025
+ }
6904
7026
  if (name === "search") {
6905
7027
  if (typeof coerced.query !== "string" || !coerced.query.trim()) {
6906
7028
  if (typeof coerced.text === "string" && coerced.text.trim()) {
@@ -6921,6 +7043,25 @@ function coerceToolArgsForExecution(name, args) {
6921
7043
  }
6922
7044
  }
6923
7045
  }
7046
+ if (name === "save_bookmark") {
7047
+ if (typeof coerced.url !== "string" || !coerced.url.trim()) {
7048
+ if (typeof coerced.link === "string" && coerced.link.trim()) {
7049
+ coerced.url = coerced.link.trim();
7050
+ } else if (typeof coerced.href === "string" && coerced.href.trim()) {
7051
+ coerced.url = coerced.href.trim();
7052
+ }
7053
+ }
7054
+ if (typeof coerced.folderName !== "string" || !coerced.folderName.trim()) {
7055
+ if (typeof coerced.folder === "string" && coerced.folder.trim()) {
7056
+ coerced.folderName = coerced.folder.trim();
7057
+ } else if (typeof coerced.category === "string" && coerced.category.trim()) {
7058
+ coerced.folderName = coerced.category.trim();
7059
+ }
7060
+ }
7061
+ if (coerced.folderName && typeof coerced.createFolderIfMissing === "undefined") {
7062
+ coerced.createFolderIfMissing = true;
7063
+ }
7064
+ }
6924
7065
  return coerced;
6925
7066
  }
6926
7067
  function canonicalizeArgsForTool(name, args) {
@@ -6937,6 +7078,24 @@ function canonicalizeArgsForTool(name, args) {
6937
7078
  }
6938
7079
  return canonical;
6939
7080
  }
7081
+ function unsupportedToolHint(name) {
7082
+ const normalized = name.trim().toLowerCase().replace(/[.\s/-]+/g, "_");
7083
+ const BOOKMARK_NAMES = [
7084
+ "organize_bookmark",
7085
+ "organize_bookmarks",
7086
+ "manage_bookmark",
7087
+ "manage_bookmarks",
7088
+ "add_to_bookmarks",
7089
+ "save_to_bookmarks",
7090
+ "bookmark_link",
7091
+ "save_link",
7092
+ "store_bookmark"
7093
+ ];
7094
+ if (BOOKMARK_NAMES.includes(normalized) || /bookmark|save.*link|organize/.test(normalized)) {
7095
+ return `Error: "${name}" is not a supported tool. Use save_bookmark to save a page as a bookmark, or create_bookmark_folder to create a folder. Example: save_bookmark with {"url": "...", "title": "...", "folderName": "..."}`;
7096
+ }
7097
+ return `Error: ${name} is not a supported tool. Choose one of the available browser tools instead.`;
7098
+ }
6940
7099
  function resolveToolCallName(rawName, args, availableToolNames) {
6941
7100
  const aliased = normalizeToolAlias(rawName);
6942
7101
  if (availableToolNames.has(aliased)) return aliased;
@@ -7241,6 +7400,7 @@ class OpenAICompatProvider {
7241
7400
  );
7242
7401
  if (recoveredToolCalls.length > 0) {
7243
7402
  toolCalls = recoveredToolCalls;
7403
+ if (textAccum.trim()) onChunk("<<erase_prev>>");
7244
7404
  } else {
7245
7405
  const narratedToolCalls = recoverNarratedActionToolCalls(
7246
7406
  textAccum,
@@ -7248,6 +7408,7 @@ class OpenAICompatProvider {
7248
7408
  );
7249
7409
  if (narratedToolCalls.length > 0) {
7250
7410
  toolCalls = narratedToolCalls;
7411
+ if (textAccum.trim()) onChunk("<<erase_prev>>");
7251
7412
  }
7252
7413
  }
7253
7414
  }
@@ -7314,6 +7475,25 @@ class OpenAICompatProvider {
7314
7475
  compactRecoveryCount = 0;
7315
7476
  const iterationToolResultPreviews = [];
7316
7477
  for (const tc of toolCalls) {
7478
+ if (!availableToolNames.has(tc.name)) {
7479
+ const hint = unsupportedToolHint(tc.name);
7480
+ onChunk(`
7481
+ <<tool:${tc.name}:⚠ unsupported>>
7482
+ `);
7483
+ messages.push({
7484
+ role: "tool",
7485
+ tool_call_id: tc.id,
7486
+ content: hint
7487
+ });
7488
+ compactCorrectionCount += 1;
7489
+ if (compactCorrectionCount >= 2) {
7490
+ messages.push({
7491
+ role: "user",
7492
+ content: `[System] You are calling unsupported tools. Stop inventing tool names. Use the supported tools you were given and take the next concrete step.`
7493
+ });
7494
+ }
7495
+ continue;
7496
+ }
7317
7497
  if (malformedToolCalls.has(tc.id)) {
7318
7498
  onChunk(`
7319
7499
  <<tool:${tc.name}:⚠ invalid args>>
@@ -7340,25 +7520,23 @@ class OpenAICompatProvider {
7340
7520
  }
7341
7521
  args = repairedArgs.args;
7342
7522
  args = coerceToolArgsForExecution(tc.name, args);
7343
- if (!availableToolNames.has(tc.name)) {
7523
+ const toolSignature = stableToolSignature(tc.name, args);
7524
+ if (this.agentToolProfile === "compact" && tc.name === "click" && isTargetlessClickArgs(args)) {
7344
7525
  onChunk(`
7345
- <<tool:unsupported_tool:⚠ unsupported>>
7526
+ <<tool:${tc.name}:⚠ missing target>>
7346
7527
  `);
7347
7528
  messages.push({
7348
7529
  role: "tool",
7349
7530
  tool_call_id: tc.id,
7350
- content: `Error: ${tc.name} is not a supported tool. Choose one of the available browser tools instead.`
7531
+ content: `Error: click requires an element target. Use click with {"index": N} from the latest read_page result, or {"text": "exact visible link/button text"}. If you do not have a current result index, call read_page(mode="results_only") first and then click one listed result.`
7532
+ });
7533
+ messages.push({
7534
+ role: "user",
7535
+ content: `[System] Your last click had no target. Do not call click with empty arguments. Refresh the page state with read_page(mode="results_only") if needed, then click exactly one result by index or exact visible text.`
7351
7536
  });
7352
7537
  compactCorrectionCount += 1;
7353
- if (compactCorrectionCount >= 2) {
7354
- messages.push({
7355
- role: "user",
7356
- content: `[System] You are calling unsupported tools. Stop inventing tool names. Use the supported tools you were given and take the next concrete step.`
7357
- });
7358
- }
7359
7538
  continue;
7360
7539
  }
7361
- const toolSignature = stableToolSignature(tc.name, args);
7362
7540
  const neverSuppressDuplicate = [
7363
7541
  "read_page",
7364
7542
  "current_tab",
@@ -10579,6 +10757,14 @@ function pruneToolsForContext(tools, pageType, query = "", options = {}) {
10579
10757
  return description !== tool.description ? { ...tool, description } : tool;
10580
10758
  });
10581
10759
  }
10760
+ const SEARCH_ENGINE_PRESETS = {
10761
+ duckduckgo: { label: "DuckDuckGo", url: "https://duckduckgo.com/?q=" },
10762
+ google: { label: "Google", url: "https://www.google.com/search?q=" },
10763
+ bing: { label: "Bing", url: "https://www.bing.com/search?q=" },
10764
+ brave: { label: "Brave Search", url: "https://search.brave.com/search?q=" },
10765
+ ecosia: { label: "Ecosia", url: "https://www.ecosia.org/search?q=" },
10766
+ kagi: { label: "Kagi", url: "https://kagi.com/search?q=" }
10767
+ };
10582
10768
  function trimText(value) {
10583
10769
  return typeof value === "string" ? value.trim() : "";
10584
10770
  }
@@ -14966,8 +15152,19 @@ function buildCommonSearchUrlShortcut(currentUrl, rawQuery) {
14966
15152
  appliedFilters: existingParam ? [`updated ${existingParam} query`] : []
14967
15153
  };
14968
15154
  }
14969
- function buildSearchShortcut(currentUrl, rawQuery) {
14970
- return buildHuggingFaceSearchShortcut(currentUrl, rawQuery) ?? buildCommonSearchUrlShortcut(currentUrl, rawQuery);
15155
+ function buildDefaultEngineShortcut(rawQuery) {
15156
+ const settings2 = loadSettings();
15157
+ const engineId = settings2.defaultSearchEngine ?? "duckduckgo";
15158
+ if (engineId === "none") return null;
15159
+ const preset = SEARCH_ENGINE_PRESETS[engineId];
15160
+ if (!preset) return null;
15161
+ const query = normalizeSearchQuery(rawQuery);
15162
+ if (!query) return null;
15163
+ return {
15164
+ url: preset.url + encodeURIComponent(query),
15165
+ source: "default search engine",
15166
+ appliedFilters: []
15167
+ };
14971
15168
  }
14972
15169
  async function locateSearchTarget(wc, explicitSelector) {
14973
15170
  if (explicitSelector) {
@@ -15020,7 +15217,7 @@ async function locateSearchTarget(wc, explicitSelector) {
15020
15217
  const seen = new Set();
15021
15218
  const ordered = [];
15022
15219
  const specific = document.querySelectorAll(
15023
- 'input[type="search"], input[name="q"], input[name="query"], input[name="search"], input[role="searchbox"], input[aria-label*="search" i], input[placeholder*="search" i]'
15220
+ 'input[type="search"], input[name="q"], input[name="query"], input[name="search"], input[role="searchbox"], input[aria-label*="search" i], input[placeholder*="search" i], textarea[name="q"], textarea[name="query"], textarea[name="search"], textarea[role="searchbox"], textarea[aria-label*="search" i], textarea[placeholder*="search" i]'
15024
15221
  );
15025
15222
  specific.forEach((el) => {
15026
15223
  if (!seen.has(el)) {
@@ -15028,7 +15225,7 @@ async function locateSearchTarget(wc, explicitSelector) {
15028
15225
  ordered.push(el);
15029
15226
  }
15030
15227
  });
15031
- document.querySelectorAll('input[type="text"], input:not([type])').forEach((el) => {
15228
+ document.querySelectorAll('input[type="text"], input:not([type]), textarea').forEach((el) => {
15032
15229
  if (seen.has(el)) return;
15033
15230
  const scope = nearestSearchScope(el);
15034
15231
  if (!scope) return;
@@ -15039,9 +15236,10 @@ async function locateSearchTarget(wc, explicitSelector) {
15039
15236
  }
15040
15237
 
15041
15238
  function scoreInput(el) {
15042
- if (!(el instanceof HTMLInputElement)) return -1;
15239
+ if (!(el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) return -1;
15043
15240
  if (isDisabled(el) || !isVisible(el)) return -1;
15044
- const type = normalize(el.getAttribute("type") || el.type);
15241
+ const isTextarea = el instanceof HTMLTextAreaElement;
15242
+ const type = isTextarea ? "text" : normalize(el.getAttribute("type") || el.type);
15045
15243
  if (type && !["search", "text", ""].includes(type)) return -1;
15046
15244
 
15047
15245
  let score = 0;
@@ -15188,16 +15386,19 @@ async function searchPage(wc, args) {
15188
15386
  if (looksLikeCurrentSiteNameQuery(query, wc.getURL(), wc.getTitle() || "")) {
15189
15387
  return `Error: "${query}" looks like the current site's name, not a product query. You are already on ${wc.getURL()}. Open a section like staff picks/new releases or search for actual book titles, authors, or genres instead.`;
15190
15388
  }
15389
+ const runShortcut = async (shortcut) => {
15390
+ const beforeUrl2 = wc.getURL();
15391
+ await loadPermittedUrl(wc, shortcut.url);
15392
+ await waitForPotentialNavigation(wc, beforeUrl2, 4e3);
15393
+ const afterUrl2 = wc.getURL();
15394
+ const applied = shortcut.appliedFilters.length > 0 ? ` (${shortcut.appliedFilters.join(", ")})` : "";
15395
+ const destination = shortcut.section ? ` ${shortcut.section}` : "";
15396
+ return `Searched "${query}" via ${shortcut.source}${destination} shortcut${applied} → ${afterUrl2}${await getPostSearchSummary(wc)}`;
15397
+ };
15191
15398
  if (typeof args.selector !== "string") {
15192
- const shortcut = buildSearchShortcut(wc.getURL(), query);
15399
+ const shortcut = buildHuggingFaceSearchShortcut(wc.getURL(), query) ?? buildCommonSearchUrlShortcut(wc.getURL(), query);
15193
15400
  if (shortcut) {
15194
- const beforeUrl2 = wc.getURL();
15195
- await loadPermittedUrl(wc, shortcut.url);
15196
- await waitForPotentialNavigation(wc, beforeUrl2, 4e3);
15197
- const afterUrl2 = wc.getURL();
15198
- const applied = shortcut.appliedFilters.length > 0 ? ` (${shortcut.appliedFilters.join(", ")})` : "";
15199
- const destination = shortcut.section ? ` ${shortcut.section}` : "";
15200
- return `Searched "${query}" via ${shortcut.source}${destination} shortcut${applied} → ${afterUrl2}${await getPostSearchSummary(wc)}`;
15401
+ return runShortcut(shortcut);
15201
15402
  }
15202
15403
  }
15203
15404
  const searchInfo = await locateSearchTarget(
@@ -15208,6 +15409,12 @@ async function searchPage(wc, args) {
15208
15409
  return pageBusyError("search");
15209
15410
  }
15210
15411
  if (!searchInfo?.selector) {
15412
+ if (typeof args.selector !== "string") {
15413
+ const fallback = buildDefaultEngineShortcut(query);
15414
+ if (fallback) {
15415
+ return runShortcut(fallback);
15416
+ }
15417
+ }
15211
15418
  return 'Error: Could not find a visible search input. Try read_page(mode="visible_only") or provide a selector.';
15212
15419
  }
15213
15420
  const fillResult = await setElementValue(wc, searchInfo.selector, query);
@@ -23387,6 +23594,21 @@ class AgentRuntime {
23387
23594
  }
23388
23595
  setApprovalMode(mode) {
23389
23596
  this.state.supervisor.approvalMode = mode;
23597
+ if (mode === "auto" && !this.state.supervisor.paused) {
23598
+ const approvals = this.state.supervisor.pendingApprovals;
23599
+ if (approvals.length > 0) {
23600
+ const actionIds = new Set(approvals.map((approval) => approval.actionId));
23601
+ this.state.supervisor.pendingApprovals = [];
23602
+ this.state.actions = this.state.actions.map(
23603
+ (action) => actionIds.has(action.id) ? { ...action, status: "running", error: void 0 } : action
23604
+ );
23605
+ for (const approval of approvals) {
23606
+ const resolve = this.pendingResolvers.get(approval.id);
23607
+ this.pendingResolvers.delete(approval.id);
23608
+ resolve?.(true);
23609
+ }
23610
+ }
23611
+ }
23390
23612
  this.emit();
23391
23613
  return this.getState();
23392
23614
  }
@@ -24285,6 +24507,11 @@ function closeSplash(splash, delayMs = 0) {
24285
24507
  }
24286
24508
  const logger = createLogger("Bootstrap");
24287
24509
  let runtime = null;
24510
+ function configureUserAgent() {
24511
+ const originalUA = electron.session.defaultSession.getUserAgent();
24512
+ const maskedUA = originalUA.replace(/ Electron\/[^\s]+/, "") + " Vessel/" + electron.app.getVersion();
24513
+ electron.session.defaultSession.setUserAgent(maskedUA);
24514
+ }
24288
24515
  function checkWritableUserData(userDataPath) {
24289
24516
  const issues = [];
24290
24517
  try {
@@ -24348,6 +24575,7 @@ Action: Open Settings (Ctrl+,) to choose a different port, then save to restart
24348
24575
  });
24349
24576
  }
24350
24577
  async function bootstrap() {
24578
+ configureUserAgent();
24351
24579
  const splash = createSplashWindow();
24352
24580
  const settings2 = loadSettings();
24353
24581
  const userDataPath = electron.app.getPath("userData");