@quanta-intellect/vessel-browser 0.1.130 → 0.1.132

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
@@ -6,10 +6,10 @@ const fs = require("fs");
6
6
  const crypto$1 = require("crypto");
7
7
  const Anthropic = require("@anthropic-ai/sdk");
8
8
  const OpenAI = require("openai");
9
+ const crypto$2 = require("node:crypto");
9
10
  const http = require("http");
10
11
  const zod = require("zod");
11
12
  const path$1 = require("node:path");
12
- const crypto$2 = require("node:crypto");
13
13
  const node_module = require("node:module");
14
14
  const http$1 = require("node:http");
15
15
  const os = require("node:os");
@@ -114,7 +114,7 @@ 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$u = createLogger("Settings");
117
+ const logger$v = createLogger("Settings");
118
118
  const SETTABLE_KEYS = new Set(Object.keys(defaults));
119
119
  let settings = null;
120
120
  let settingsIssues = [];
@@ -139,7 +139,7 @@ function canUseSafeStorage$1() {
139
139
  try {
140
140
  return electron.safeStorage.isEncryptionAvailable();
141
141
  } catch (err) {
142
- logger$u.warn("safeStorage.isEncryptionAvailable() failed, assuming unavailable:", err);
142
+ logger$v.warn("safeStorage.isEncryptionAvailable() failed, assuming unavailable:", err);
143
143
  return false;
144
144
  }
145
145
  }
@@ -148,7 +148,7 @@ function writePrivateFile(filePath2, data) {
148
148
  try {
149
149
  fs.chmodSync(filePath2, 384);
150
150
  } catch (err) {
151
- logger$u.debug("Could not chmod private file (non-POSIX filesystem):", err);
151
+ logger$v.debug("Could not chmod private file (non-POSIX filesystem):", err);
152
152
  }
153
153
  }
154
154
  function assertSafeStorageAvailable() {
@@ -167,7 +167,7 @@ function readStoredProviderSecret() {
167
167
  }
168
168
  } catch (err) {
169
169
  if (!isMissingFileError(err)) {
170
- logger$u.warn("Could not read stored provider secret:", err);
170
+ logger$v.warn("Could not read stored provider secret:", err);
171
171
  }
172
172
  }
173
173
  return null;
@@ -185,7 +185,7 @@ function clearStoredProviderSecret() {
185
185
  fs.unlinkSync(getChatProviderSecretPath());
186
186
  } catch (err) {
187
187
  if (!isMissingFileError(err)) {
188
- logger$u.warn("Could not delete provider secret file:", err);
188
+ logger$v.warn("Could not delete provider secret file:", err);
189
189
  }
190
190
  }
191
191
  }
@@ -203,7 +203,7 @@ function readStoredCodexTokens() {
203
203
  }
204
204
  } catch (err) {
205
205
  if (!isMissingFileError(err)) {
206
- logger$u.warn("Could not read stored Codex tokens:", err);
206
+ logger$v.warn("Could not read stored Codex tokens:", err);
207
207
  }
208
208
  }
209
209
  return null;
@@ -221,7 +221,7 @@ function clearStoredCodexTokens() {
221
221
  fs.unlinkSync(getCodexTokensPath());
222
222
  } catch (err) {
223
223
  if (!isMissingFileError(err)) {
224
- logger$u.warn("Could not delete Codex token file:", err);
224
+ logger$v.warn("Could not delete Codex token file:", err);
225
225
  }
226
226
  }
227
227
  }
@@ -351,7 +351,7 @@ function persistNow() {
351
351
  JSON.stringify(buildPersistedSettings(settings), null, 2),
352
352
  { encoding: "utf-8", mode: 384 }
353
353
  )
354
- ).then(() => fs.promises.chmod(getSettingsPath(), 384).catch(() => void 0)).catch((err) => logger$u.error("Failed to save settings:", err));
354
+ ).then(() => fs.promises.chmod(getSettingsPath(), 384).catch(() => void 0)).catch((err) => logger$v.error("Failed to save settings:", err));
355
355
  }
356
356
  function saveSettings() {
357
357
  saveDirty = true;
@@ -530,7 +530,7 @@ const urlSafety = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
530
530
  }, Symbol.toStringTag, { value: "Module" }));
531
531
  const MAX_CUSTOM_HISTORY = 50;
532
532
  const READER_MODE_DATA_URL_PREFIX = "data:text/html;charset=utf-8,";
533
- const logger$t = createLogger("Tab");
533
+ const logger$u = createLogger("Tab");
534
534
  const sessionCertExceptions = /* @__PURE__ */ new WeakMap();
535
535
  const sessionsWithVerifyProc = /* @__PURE__ */ new WeakSet();
536
536
  const CERT_VERIFY_TRUST = 0;
@@ -596,7 +596,7 @@ class Tab {
596
596
  guardedLoadURL(url, options) {
597
597
  const blockReason = this.getNavigationBlockReason(url);
598
598
  if (blockReason) {
599
- logger$t.warn(blockReason);
599
+ logger$u.warn(blockReason);
600
600
  return blockReason;
601
601
  }
602
602
  void this.view.webContents.loadURL(url, options);
@@ -680,7 +680,7 @@ class Tab {
680
680
  wc.setWindowOpenHandler(({ url, disposition }) => {
681
681
  const error = this.getNavigationBlockReason(url);
682
682
  if (error) {
683
- logger$t.warn(error);
683
+ logger$u.warn(error);
684
684
  return { action: "deny" };
685
685
  }
686
686
  this.onOpenUrl?.({
@@ -694,7 +694,7 @@ class Tab {
694
694
  const error = this.getNavigationBlockReason(url);
695
695
  if (!error) return;
696
696
  event.preventDefault();
697
- logger$t.warn(`${context}: ${error}`);
697
+ logger$u.warn(`${context}: ${error}`);
698
698
  };
699
699
  wc.on("will-navigate", (event, url) => {
700
700
  blockNavigation(event, url, "Blocked top-level navigation");
@@ -778,7 +778,7 @@ class Tab {
778
778
  ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 999px; }
779
779
  ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.22); }
780
780
  ::-webkit-scrollbar-corner { background: transparent; }
781
- `).catch((err) => logger$t.warn("Failed to inject scrollbar CSS:", err));
781
+ `).catch((err) => logger$u.warn("Failed to inject scrollbar CSS:", err));
782
782
  });
783
783
  wc.on("page-favicon-updated", (_, favicons) => {
784
784
  this._state.favicon = favicons[0] || "";
@@ -814,7 +814,7 @@ class Tab {
814
814
  ).then((highlightedText) => {
815
815
  this.buildContextMenu(wc, params, highlightedText.trim());
816
816
  }).catch((err) => {
817
- logger$t.warn("Failed to inspect highlighted text for context menu:", err);
817
+ logger$u.warn("Failed to inspect highlighted text for context menu:", err);
818
818
  this.buildContextMenu(wc, params, "");
819
819
  });
820
820
  });
@@ -1015,7 +1015,7 @@ class Tab {
1015
1015
  "document.documentElement.outerHTML"
1016
1016
  );
1017
1017
  } catch (err) {
1018
- logger$t.warn("Failed to retrieve page source:", err);
1018
+ logger$u.warn("Failed to retrieve page source:", err);
1019
1019
  return;
1020
1020
  }
1021
1021
  const escaped = html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -1143,7 +1143,7 @@ class Tab {
1143
1143
  document.addEventListener('mouseup', window.__vesselHighlightHandler);
1144
1144
  }
1145
1145
  })()
1146
- `).catch((err) => logger$t.warn("Failed to inject highlight listener:", err));
1146
+ `).catch((err) => logger$u.warn("Failed to inject highlight listener:", err));
1147
1147
  } else {
1148
1148
  void wc.executeJavaScript(`
1149
1149
  (function() {
@@ -1154,13 +1154,13 @@ class Tab {
1154
1154
  delete window.__vesselHighlightHandler;
1155
1155
  }
1156
1156
  })()
1157
- `).catch((err) => logger$t.warn("Failed to remove highlight listener:", err));
1157
+ `).catch((err) => logger$u.warn("Failed to remove highlight listener:", err));
1158
1158
  }
1159
1159
  }
1160
1160
  get webContentsId() {
1161
1161
  return this.view.webContents.id;
1162
1162
  }
1163
- destroy() {
1163
+ dispose() {
1164
1164
  this.setHighlightMode(false);
1165
1165
  this.view.webContents.close();
1166
1166
  }
@@ -1191,7 +1191,7 @@ const SEARCH_ENGINE_PRESETS = {
1191
1191
  ecosia: { label: "Ecosia", url: "https://www.ecosia.org/search?q=" },
1192
1192
  kagi: { label: "Kagi", url: "https://kagi.com/search?q=" }
1193
1193
  };
1194
- const logger$s = createLogger("JsonPersistence");
1194
+ const logger$t = createLogger("JsonPersistence");
1195
1195
  function canUseSafeStorage() {
1196
1196
  try {
1197
1197
  return electron.safeStorage.isEncryptionAvailable();
@@ -1256,7 +1256,7 @@ function createDebouncedJsonPersistence({
1256
1256
  data,
1257
1257
  typeof data === "string" ? { encoding: "utf-8", mode: 384 } : { mode: 384 }
1258
1258
  )
1259
- ).then(() => fs.promises.chmod(filePath2, 384).catch(() => void 0)).catch((err) => logger$s.error(`Failed to save ${logLabel}:`, err));
1259
+ ).then(() => fs.promises.chmod(filePath2, 384).catch(() => void 0)).catch((err) => logger$t.error(`Failed to save ${logLabel}:`, err));
1260
1260
  };
1261
1261
  const schedule = () => {
1262
1262
  saveDirty2 = true;
@@ -2948,7 +2948,7 @@ function destroySession(tabId) {
2948
2948
  sessions.delete(tabId);
2949
2949
  }
2950
2950
  }
2951
- const logger$r = createLogger("TabManager");
2951
+ const logger$s = createLogger("TabManager");
2952
2952
  function sanitizeFilename(title, ext) {
2953
2953
  const clean = title.replace(/[<>:"/\\|?*\x00-\x1f]/g, " ").replace(/\s+/g, " ").trim();
2954
2954
  const escapedExt = ext.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -3048,7 +3048,7 @@ class TabManager {
3048
3048
  }
3049
3049
  destroySession(id);
3050
3050
  this.window.contentView.removeChildView(tab.view);
3051
- tab.destroy();
3051
+ tab.dispose();
3052
3052
  this.tabs.delete(id);
3053
3053
  this.order = this.order.filter((tid) => tid !== id);
3054
3054
  this.removeGroupIfEmpty(groupId);
@@ -3318,7 +3318,7 @@ class TabManager {
3318
3318
  if (!tab) continue;
3319
3319
  destroySession(id);
3320
3320
  this.window.contentView.removeChildView(tab.view);
3321
- tab.destroy();
3321
+ tab.dispose();
3322
3322
  }
3323
3323
  this.tabs.clear();
3324
3324
  this.order = [];
@@ -3327,6 +3327,30 @@ class TabManager {
3327
3327
  this.broadcastState({ persistSession: true });
3328
3328
  }
3329
3329
  lastReapply = /* @__PURE__ */ new Map();
3330
+ /**
3331
+ * Clean up all tabs, event listeners, and internal state.
3332
+ * Call when the window is closing or when the TabManager is being replaced
3333
+ * (e.g. secondary / private windows).
3334
+ */
3335
+ dispose() {
3336
+ for (const id of [...this.order]) {
3337
+ const tab = this.tabs.get(id);
3338
+ if (!tab) continue;
3339
+ destroySession(id);
3340
+ this.window.contentView.removeChildView(tab.view);
3341
+ tab.dispose();
3342
+ }
3343
+ this.tabs.clear();
3344
+ this.order = [];
3345
+ this.tabGroups.clear();
3346
+ this.activeTabId = null;
3347
+ this.closedTabs = [];
3348
+ this.lastReapply.clear();
3349
+ this.highlightCaptureCallback = null;
3350
+ this.pageLoadCallback = null;
3351
+ this.securityStateCallback = null;
3352
+ this.lastSessionSignature = "";
3353
+ }
3330
3354
  reapplyHighlights(url, wc) {
3331
3355
  const wcId = wc.id;
3332
3356
  const now = Date.now();
@@ -3343,7 +3367,7 @@ class TabManager {
3343
3367
  }));
3344
3368
  if (entries.length > 0) {
3345
3369
  void highlightBatchOnPage(wc, entries).catch(
3346
- (err) => logger$r.warn("Failed to batch highlight:", err)
3370
+ (err) => logger$s.warn("Failed to batch highlight:", err)
3347
3371
  );
3348
3372
  }
3349
3373
  }
@@ -3365,12 +3389,12 @@ class TabManager {
3365
3389
  const result = await captureSelectionHighlight(wc);
3366
3390
  if (result.success && result.text) {
3367
3391
  await highlightOnPage(wc, null, result.text, void 0, void 0, "yellow").catch(
3368
- (err) => logger$r.warn("Failed to capture highlight:", err)
3392
+ (err) => logger$s.warn("Failed to capture highlight:", err)
3369
3393
  );
3370
3394
  }
3371
3395
  this.highlightCaptureCallback?.(result);
3372
3396
  } catch (err) {
3373
- logger$r.warn("Failed to capture highlight from page:", err);
3397
+ logger$s.warn("Failed to capture highlight from page:", err);
3374
3398
  this.highlightCaptureCallback?.({
3375
3399
  success: false,
3376
3400
  message: "Could not capture selection"
@@ -3395,7 +3419,7 @@ class TabManager {
3395
3419
  void this.removeHighlightMarksForText(wc, text);
3396
3420
  }
3397
3421
  } catch (err) {
3398
- logger$r.warn("Failed to remove highlight from matching tab:", err);
3422
+ logger$s.warn("Failed to remove highlight from matching tab:", err);
3399
3423
  }
3400
3424
  }
3401
3425
  this.highlightCaptureCallback?.({
@@ -3426,12 +3450,12 @@ class TabManager {
3426
3450
  void 0,
3427
3451
  color
3428
3452
  ).catch(
3429
- (err) => logger$r.warn("Failed to update highlight color:", err)
3453
+ (err) => logger$s.warn("Failed to update highlight color:", err)
3430
3454
  );
3431
3455
  });
3432
3456
  }
3433
3457
  } catch (err) {
3434
- logger$r.warn("Failed to iterate highlights for color change:", err);
3458
+ logger$s.warn("Failed to iterate highlights for color change:", err);
3435
3459
  }
3436
3460
  }
3437
3461
  this.highlightCaptureCallback?.({
@@ -3486,7 +3510,7 @@ class TabManager {
3486
3510
  });
3487
3511
  })()`
3488
3512
  ).catch(
3489
- (err) => logger$r.warn("Failed to remove highlight marks:", err)
3513
+ (err) => logger$s.warn("Failed to remove highlight marks:", err)
3490
3514
  );
3491
3515
  }
3492
3516
  broadcastState(meta = { persistSession: false }) {
@@ -3501,19 +3525,7 @@ class TabManager {
3501
3525
  }
3502
3526
  }
3503
3527
  }
3504
- const Channels = {
3505
- // Tab management
3506
- TAB_CREATE: "tab:create",
3507
- TAB_CLOSE: "tab:close",
3508
- TAB_SWITCH: "tab:switch",
3509
- TAB_NAVIGATE: "tab:navigate",
3510
- TAB_BACK: "tab:back",
3511
- TAB_FORWARD: "tab:forward",
3512
- TAB_RELOAD: "tab:reload",
3513
- TAB_STATE_GET: "tab:state-get",
3514
- TAB_STATE_UPDATE: "tab:state-update",
3515
- RENDERER_VIEW_READY: "renderer:view-ready",
3516
- // AI
3528
+ const AIChannels = {
3517
3529
  AI_QUERY: "ai:query",
3518
3530
  AI_STREAM_START: "ai:stream-start",
3519
3531
  AI_STREAM_CHUNK: "ai:stream-chunk",
@@ -3536,32 +3548,26 @@ const Channels = {
3536
3548
  AGENT_CHECKPOINT_UPDATE_NOTE: "agent:checkpoint-update-note",
3537
3549
  AGENT_UNDO_LAST_ACTION: "agent:undo-last-action",
3538
3550
  AGENT_SESSION_CAPTURE: "agent:session-capture",
3539
- AGENT_SESSION_RESTORE: "agent:session-restore",
3540
- // Content
3541
- CONTENT_EXTRACT: "content:extract",
3542
- READER_MODE_TOGGLE: "reader:toggle",
3543
- // UI state
3544
- SIDEBAR_TOGGLE: "ui:sidebar-toggle",
3545
- SIDEBAR_NAVIGATE: "ui:sidebar-navigate",
3546
- SIDEBAR_RESIZE: "ui:sidebar-resize",
3547
- SIDEBAR_RESIZE_START: "ui:sidebar-resize-start",
3548
- SIDEBAR_RESIZE_COMMIT: "ui:sidebar-resize-commit",
3549
- SIDEBAR_POPOUT: "ui:sidebar-popout",
3550
- SIDEBAR_DOCK: "ui:sidebar-dock",
3551
- SIDEBAR_STATE_UPDATE: "ui:sidebar-state-update",
3552
- SIDEBAR_CONTEXT_MENU: "ui:sidebar-context-menu",
3553
- FOCUS_MODE_TOGGLE: "ui:focus-mode-toggle",
3554
- SETTINGS_VISIBILITY: "ui:settings-visibility",
3555
- // Settings
3556
- SETTINGS_GET: "settings:get",
3557
- SETTINGS_SET: "settings:set",
3558
- SETTINGS_UPDATE: "settings:update",
3559
- SETTINGS_HEALTH_GET: "settings:health:get",
3560
- SETTINGS_HEALTH_UPDATE: "settings:health:update",
3561
- MCP_REGENERATE_TOKEN: "mcp:regenerate-token",
3562
- // Support
3563
- SUPPORT_SUBMIT_FEEDBACK: "support:submit-feedback",
3564
- // Bookmarks
3551
+ AGENT_SESSION_RESTORE: "agent:session-restore"
3552
+ };
3553
+ const AutofillChannels = {
3554
+ AUTOFILL_LIST: "autofill:list",
3555
+ AUTOFILL_ADD: "autofill:add",
3556
+ AUTOFILL_UPDATE: "autofill:update",
3557
+ AUTOFILL_DELETE: "autofill:delete",
3558
+ AUTOFILL_FILL: "autofill:fill"
3559
+ };
3560
+ const AutomationChannels = {
3561
+ AUTOMATION_GET_INSTALLED: "automation:get-installed",
3562
+ AUTOMATION_INSTALL_FROM_FILE: "automation:install-from-file",
3563
+ AUTOMATION_UNINSTALL: "automation:uninstall",
3564
+ SCHEDULE_GET_ALL: "schedule:get-all",
3565
+ SCHEDULE_CREATE: "schedule:create",
3566
+ SCHEDULE_UPDATE: "schedule:update",
3567
+ SCHEDULE_DELETE: "schedule:delete",
3568
+ SCHEDULE_JOBS_UPDATE: "schedule:jobs-update"
3569
+ };
3570
+ const BookmarkChannels = {
3565
3571
  BOOKMARKS_GET: "bookmarks:get",
3566
3572
  BOOKMARKS_UPDATE: "bookmarks:update",
3567
3573
  BOOKMARK_SAVE: "bookmarks:save",
@@ -3575,8 +3581,43 @@ const Channels = {
3575
3581
  FOLDER_CREATE: "bookmarks:folder-create",
3576
3582
  FOLDER_REMOVE: "bookmarks:folder-remove",
3577
3583
  FOLDER_RENAME: "bookmarks:folder-rename",
3578
- FOLDER_EXPORT_HTML: "bookmarks:folder-export-html",
3579
- // Highlights
3584
+ FOLDER_EXPORT_HTML: "bookmarks:folder-export-html"
3585
+ };
3586
+ const BrowsingDataChannels = {
3587
+ CLEAR_BROWSING_DATA: "browsing-data:clear",
3588
+ CLEAR_BROWSING_DATA_OPEN: "browsing-data:open"
3589
+ };
3590
+ const CodexChannels = {
3591
+ CODEX_START_AUTH: "codex:start-auth",
3592
+ CODEX_CANCEL_AUTH: "codex:cancel-auth",
3593
+ CODEX_AUTH_STATUS: "codex:auth-status",
3594
+ CODEX_DISCONNECT: "codex:disconnect"
3595
+ };
3596
+ const ContentChannels = {
3597
+ CONTENT_EXTRACT: "content:extract",
3598
+ READER_MODE_TOGGLE: "reader:toggle",
3599
+ PAGE_DIFF_ACTIVITY: "page:diff-activity",
3600
+ PAGE_CHANGED: "page:changed",
3601
+ PAGE_DIFF_GET: "page:diff-get",
3602
+ PAGE_DIFF_HISTORY: "page:diff-history",
3603
+ PAGE_DIFF_DIRTY: "page:diff-dirty"
3604
+ };
3605
+ const DevToolsChannels = {
3606
+ DEVTOOLS_PANEL_TOGGLE: "devtools-panel:toggle",
3607
+ DEVTOOLS_PANEL_STATE: "devtools-panel:state",
3608
+ DEVTOOLS_PANEL_RESIZE: "devtools-panel:resize"
3609
+ };
3610
+ const DownloadChannels = {
3611
+ DOWNLOAD_STARTED: "download:started",
3612
+ DOWNLOAD_PROGRESS: "download:progress",
3613
+ DOWNLOAD_DONE: "download:done",
3614
+ DOWNLOADS_GET: "downloads:get",
3615
+ DOWNLOADS_CLEAR: "downloads:clear",
3616
+ DOWNLOADS_OPEN: "downloads:open",
3617
+ DOWNLOADS_SHOW_IN_FOLDER: "downloads:show-in-folder",
3618
+ DOWNLOADS_UPDATE: "downloads:update"
3619
+ };
3620
+ const HighlightChannels = {
3580
3621
  HIGHLIGHT_CAPTURE: "highlights:capture",
3581
3622
  HIGHLIGHT_CAPTURE_RESULT: "highlights:capture-result",
3582
3623
  HIGHLIGHT_SELECTION: "vessel:highlight-selection",
@@ -3585,154 +3626,171 @@ const Channels = {
3585
3626
  HIGHLIGHT_NAV_SCROLL: "highlights:nav-scroll",
3586
3627
  HIGHLIGHT_NAV_REMOVE: "highlights:nav-remove",
3587
3628
  HIGHLIGHT_NAV_CLEAR: "highlights:nav-clear",
3588
- SIDEBAR_HIGHLIGHT_ACTION: "highlights:sidebar-action",
3589
- // DevTools panel
3590
- DEVTOOLS_PANEL_TOGGLE: "devtools-panel:toggle",
3591
- DEVTOOLS_PANEL_STATE: "devtools-panel:state",
3592
- DEVTOOLS_PANEL_RESIZE: "devtools-panel:resize",
3593
- // Ad blocking
3629
+ SIDEBAR_HIGHLIGHT_ACTION: "highlights:sidebar-action"
3630
+ };
3631
+ const HistoryChannels = {
3632
+ HISTORY_GET: "history:get",
3633
+ HISTORY_LIST: "history:list",
3634
+ HISTORY_SEARCH: "history:search",
3635
+ HISTORY_CLEAR: "history:clear",
3636
+ HISTORY_UPDATE: "history:update",
3637
+ HISTORY_EXPORT_HTML: "history:export-html",
3638
+ HISTORY_EXPORT_JSON: "history:export-json",
3639
+ HISTORY_IMPORT: "history:import"
3640
+ };
3641
+ const HumanVaultChannels = {
3642
+ HUMAN_VAULT_LIST: "human-vault:list",
3643
+ HUMAN_VAULT_GET: "human-vault:get",
3644
+ HUMAN_VAULT_SAVE: "human-vault:save",
3645
+ HUMAN_VAULT_UPDATE: "human-vault:update",
3646
+ HUMAN_VAULT_REMOVE: "human-vault:remove",
3647
+ HUMAN_VAULT_AUDIT_LOG: "human-vault:audit-log"
3648
+ };
3649
+ const OpenRouterChannels = {
3650
+ OPENROUTER_START_AUTH: "openrouter:start-auth",
3651
+ OPENROUTER_CANCEL_AUTH: "openrouter:cancel-auth",
3652
+ OPENROUTER_AUTH_STATUS: "openrouter:auth-status"
3653
+ };
3654
+ const PermissionChannels = {
3655
+ PERMISSIONS_GET: "permissions:get",
3656
+ PERMISSIONS_CLEAR: "permissions:clear",
3657
+ PERMISSIONS_CLEAR_ORIGIN: "permissions:clear-origin"
3658
+ };
3659
+ const PremiumChannels = {
3660
+ PREMIUM_GET_STATE: "premium:get-state",
3661
+ PREMIUM_ACTIVATION_START: "premium:activation-start",
3662
+ PREMIUM_ACTIVATION_VERIFY: "premium:activation-verify",
3663
+ PREMIUM_CHECKOUT: "premium:checkout",
3664
+ PREMIUM_PORTAL: "premium:portal",
3665
+ PREMIUM_RESET: "premium:reset",
3666
+ PREMIUM_TRACK_CONTEXT: "premium:track-context",
3667
+ PREMIUM_UPDATE: "premium:update"
3668
+ };
3669
+ const ResearchChannels = {
3670
+ RESEARCH_STATE_GET: "research:state-get",
3671
+ RESEARCH_STATE_UPDATE: "research:state-update",
3672
+ RESEARCH_START_BRIEF: "research:start-brief",
3673
+ RESEARCH_CONFIRM_BRIEF: "research:confirm-brief",
3674
+ RESEARCH_APPROVE_OBJECTIVES: "research:approve-objectives",
3675
+ RESEARCH_SET_MODE: "research:set-mode",
3676
+ RESEARCH_SET_TRACES: "research:set-traces",
3677
+ RESEARCH_CANCEL: "research:cancel",
3678
+ RESEARCH_STOP_AND_SYNTHESIZE: "research:stop-and-synthesize",
3679
+ RESEARCH_EXPORT_REPORT: "research:export-report"
3680
+ };
3681
+ const SecurityChannels = {
3682
+ SECURITY_STATE_UPDATE: "security:state-update",
3683
+ SECURITY_SHOW_DETAILS: "security:show-details",
3684
+ SECURITY_PROCEED_ANYWAY: "security:proceed-anyway",
3685
+ SECURITY_GO_BACK_TO_SAFETY: "security:go-back-to-safety"
3686
+ };
3687
+ const SessionChannels = {
3688
+ SESSION_LIST: "session:list",
3689
+ SESSION_SAVE: "session:save",
3690
+ SESSION_LOAD: "session:load",
3691
+ SESSION_DELETE: "session:delete"
3692
+ };
3693
+ const SettingsChannels = {
3694
+ SETTINGS_GET: "settings:get",
3695
+ SETTINGS_SET: "settings:set",
3696
+ SETTINGS_UPDATE: "settings:update",
3697
+ SETTINGS_HEALTH_GET: "settings:health:get",
3698
+ SETTINGS_HEALTH_UPDATE: "settings:health:update",
3699
+ MCP_REGENERATE_TOKEN: "mcp:regenerate-token",
3700
+ SUPPORT_SUBMIT_FEEDBACK: "support:submit-feedback"
3701
+ };
3702
+ const TabChannels = {
3703
+ TAB_CREATE: "tab:create",
3704
+ TAB_CLOSE: "tab:close",
3705
+ TAB_SWITCH: "tab:switch",
3706
+ TAB_NAVIGATE: "tab:navigate",
3707
+ TAB_BACK: "tab:back",
3708
+ TAB_FORWARD: "tab:forward",
3709
+ TAB_RELOAD: "tab:reload",
3710
+ TAB_STATE_GET: "tab:state-get",
3711
+ TAB_STATE_UPDATE: "tab:state-update",
3712
+ RENDERER_VIEW_READY: "renderer:view-ready",
3594
3713
  TAB_TOGGLE_AD_BLOCK: "tab:toggle-ad-block",
3595
- // Zoom
3596
3714
  TAB_ZOOM_IN: "tab:zoom-in",
3597
3715
  TAB_ZOOM_OUT: "tab:zoom-out",
3598
3716
  TAB_ZOOM_RESET: "tab:zoom-reset",
3599
- // Security indicator
3600
- SECURITY_STATE_UPDATE: "security:state-update",
3601
- SECURITY_SHOW_DETAILS: "security:show-details",
3602
- SECURITY_PROCEED_ANYWAY: "security:proceed-anyway",
3603
- SECURITY_GO_BACK_TO_SAFETY: "security:go-back-to-safety",
3604
- // Closed tabs / duplication
3605
3717
  TAB_REOPEN_CLOSED: "tab:reopen-closed",
3606
3718
  TAB_DUPLICATE: "tab:duplicate",
3607
3719
  TAB_CONTEXT_MENU: "tab:context-menu",
3608
- // Pin tabs
3609
3720
  TAB_PIN: "tab:pin",
3610
3721
  TAB_UNPIN: "tab:unpin",
3611
- // Tab groups
3612
3722
  TAB_GROUP_CREATE: "tab-group:create",
3613
3723
  TAB_GROUP_ADD_TAB: "tab-group:add-tab",
3614
3724
  TAB_GROUP_REMOVE_TAB: "tab-group:remove-tab",
3615
3725
  TAB_GROUP_TOGGLE_COLLAPSED: "tab-group:toggle-collapsed",
3616
3726
  TAB_GROUP_SET_COLOR: "tab-group:set-color",
3617
3727
  TAB_GROUP_CONTEXT_MENU: "tab-group:context-menu",
3618
- // Audio / mute
3619
3728
  TAB_TOGGLE_MUTE: "tab:toggle-mute",
3620
- // Print
3621
3729
  TAB_PRINT: "tab:print",
3622
3730
  TAB_PRINT_TO_PDF: "tab:print-to-pdf",
3623
- // Windows
3731
+ TAB_TOGGLE_PIP: "tab:toggle-pip",
3624
3732
  OPEN_NEW_WINDOW: "window:open-new",
3625
- // Private browsing
3626
3733
  OPEN_PRIVATE_WINDOW: "private:open-window",
3627
3734
  IS_PRIVATE_MODE: "private:is-private",
3628
- // Find in page
3629
3735
  FIND_IN_PAGE_START: "find:start",
3630
3736
  FIND_IN_PAGE_NEXT: "find:next",
3631
3737
  FIND_IN_PAGE_STOP: "find:stop",
3632
- FIND_IN_PAGE_RESULT: "find:result",
3633
- // Browsing history
3634
- HISTORY_GET: "history:get",
3635
- HISTORY_LIST: "history:list",
3636
- HISTORY_SEARCH: "history:search",
3637
- HISTORY_CLEAR: "history:clear",
3638
- HISTORY_UPDATE: "history:update",
3639
- HISTORY_EXPORT_HTML: "history:export-html",
3640
- HISTORY_EXPORT_JSON: "history:export-json",
3641
- HISTORY_IMPORT: "history:import",
3642
- // Downloads
3643
- DOWNLOAD_STARTED: "download:started",
3644
- DOWNLOAD_PROGRESS: "download:progress",
3645
- DOWNLOAD_DONE: "download:done",
3646
- DOWNLOADS_GET: "downloads:get",
3647
- DOWNLOADS_CLEAR: "downloads:clear",
3648
- DOWNLOADS_OPEN: "downloads:open",
3649
- DOWNLOADS_SHOW_IN_FOLDER: "downloads:show-in-folder",
3650
- DOWNLOADS_UPDATE: "downloads:update",
3651
- // Premium
3652
- PREMIUM_GET_STATE: "premium:get-state",
3653
- PREMIUM_ACTIVATION_START: "premium:activation-start",
3654
- PREMIUM_ACTIVATION_VERIFY: "premium:activation-verify",
3655
- PREMIUM_CHECKOUT: "premium:checkout",
3656
- PREMIUM_PORTAL: "premium:portal",
3657
- PREMIUM_RESET: "premium:reset",
3658
- PREMIUM_TRACK_CONTEXT: "premium:track-context",
3659
- PREMIUM_UPDATE: "premium:update",
3660
- // Named sessions
3661
- SESSION_LIST: "session:list",
3662
- SESSION_SAVE: "session:save",
3663
- SESSION_LOAD: "session:load",
3664
- SESSION_DELETE: "session:delete",
3665
- // Agent Credential Vault
3738
+ FIND_IN_PAGE_RESULT: "find:result"
3739
+ };
3740
+ const UIChannels = {
3741
+ SIDEBAR_TOGGLE: "ui:sidebar-toggle",
3742
+ SIDEBAR_NAVIGATE: "ui:sidebar-navigate",
3743
+ SIDEBAR_RESIZE: "ui:sidebar-resize",
3744
+ SIDEBAR_RESIZE_START: "ui:sidebar-resize-start",
3745
+ SIDEBAR_RESIZE_COMMIT: "ui:sidebar-resize-commit",
3746
+ SIDEBAR_POPOUT: "ui:sidebar-popout",
3747
+ SIDEBAR_DOCK: "ui:sidebar-dock",
3748
+ SIDEBAR_STATE_UPDATE: "ui:sidebar-state-update",
3749
+ SIDEBAR_CONTEXT_MENU: "ui:sidebar-context-menu",
3750
+ FOCUS_MODE_TOGGLE: "ui:focus-mode-toggle",
3751
+ SETTINGS_VISIBILITY: "ui:settings-visibility"
3752
+ };
3753
+ const UpdateChannels = {
3754
+ UPDATES_CHECK: "updates:check",
3755
+ UPDATES_OPEN_DOWNLOAD: "updates:open-download"
3756
+ };
3757
+ const VaultChannels = {
3666
3758
  VAULT_LIST: "vault:list",
3667
3759
  VAULT_ADD: "vault:add",
3668
3760
  VAULT_UPDATE: "vault:update",
3669
3761
  VAULT_REMOVE: "vault:remove",
3670
- VAULT_AUDIT_LOG: "vault:audit-log",
3671
- // Human Password Manager
3672
- HUMAN_VAULT_LIST: "human-vault:list",
3673
- HUMAN_VAULT_GET: "human-vault:get",
3674
- HUMAN_VAULT_SAVE: "human-vault:save",
3675
- HUMAN_VAULT_UPDATE: "human-vault:update",
3676
- HUMAN_VAULT_REMOVE: "human-vault:remove",
3677
- HUMAN_VAULT_AUDIT_LOG: "human-vault:audit-log",
3678
- // Automation kits
3679
- AUTOMATION_GET_INSTALLED: "automation:get-installed",
3680
- AUTOMATION_INSTALL_FROM_FILE: "automation:install-from-file",
3681
- AUTOMATION_UNINSTALL: "automation:uninstall",
3682
- // Scheduled jobs
3683
- SCHEDULE_GET_ALL: "schedule:get-all",
3684
- SCHEDULE_CREATE: "schedule:create",
3685
- SCHEDULE_UPDATE: "schedule:update",
3686
- SCHEDULE_DELETE: "schedule:delete",
3687
- SCHEDULE_JOBS_UPDATE: "schedule:jobs-update",
3688
- // Window controls
3762
+ VAULT_AUDIT_LOG: "vault:audit-log"
3763
+ };
3764
+ const WindowControlChannels = {
3689
3765
  WINDOW_MINIMIZE: "window:minimize",
3690
3766
  WINDOW_MAXIMIZE: "window:maximize",
3691
- WINDOW_CLOSE: "window:close",
3692
- // Autofill
3693
- AUTOFILL_LIST: "autofill:list",
3694
- AUTOFILL_ADD: "autofill:add",
3695
- AUTOFILL_UPDATE: "autofill:update",
3696
- AUTOFILL_DELETE: "autofill:delete",
3697
- AUTOFILL_FILL: "autofill:fill",
3698
- // Page snapshots / What Changed
3699
- PAGE_DIFF_ACTIVITY: "page:diff-activity",
3700
- PAGE_CHANGED: "page:changed",
3701
- PAGE_DIFF_GET: "page:diff-get",
3702
- PAGE_DIFF_HISTORY: "page:diff-history",
3703
- PAGE_DIFF_DIRTY: "page:diff-dirty",
3704
- // Clear browsing data
3705
- CLEAR_BROWSING_DATA: "browsing-data:clear",
3706
- CLEAR_BROWSING_DATA_OPEN: "browsing-data:open",
3707
- // Picture-in-Picture
3708
- TAB_TOGGLE_PIP: "tab:toggle-pip",
3709
- // Research Desk
3710
- RESEARCH_STATE_GET: "research:state-get",
3711
- RESEARCH_STATE_UPDATE: "research:state-update",
3712
- RESEARCH_START_BRIEF: "research:start-brief",
3713
- RESEARCH_CONFIRM_BRIEF: "research:confirm-brief",
3714
- RESEARCH_APPROVE_OBJECTIVES: "research:approve-objectives",
3715
- RESEARCH_SET_MODE: "research:set-mode",
3716
- RESEARCH_SET_TRACES: "research:set-traces",
3717
- RESEARCH_CANCEL: "research:cancel",
3718
- RESEARCH_STOP_AND_SYNTHESIZE: "research:stop-and-synthesize",
3719
- RESEARCH_EXPORT_REPORT: "research:export-report",
3720
- // Codex OAuth
3721
- CODEX_START_AUTH: "codex:start-auth",
3722
- CODEX_CANCEL_AUTH: "codex:cancel-auth",
3723
- CODEX_AUTH_STATUS: "codex:auth-status",
3724
- CODEX_DISCONNECT: "codex:disconnect",
3725
- // OpenRouter OAuth
3726
- OPENROUTER_START_AUTH: "openrouter:start-auth",
3727
- OPENROUTER_CANCEL_AUTH: "openrouter:cancel-auth",
3728
- OPENROUTER_AUTH_STATUS: "openrouter:auth-status",
3729
- // Updates
3730
- UPDATES_CHECK: "updates:check",
3731
- UPDATES_OPEN_DOWNLOAD: "updates:open-download",
3732
- // Permissions
3733
- PERMISSIONS_GET: "permissions:get",
3734
- PERMISSIONS_CLEAR: "permissions:clear",
3735
- PERMISSIONS_CLEAR_ORIGIN: "permissions:clear-origin"
3767
+ WINDOW_CLOSE: "window:close"
3768
+ };
3769
+ const Channels = {
3770
+ ...AIChannels,
3771
+ ...AutofillChannels,
3772
+ ...AutomationChannels,
3773
+ ...BookmarkChannels,
3774
+ ...BrowsingDataChannels,
3775
+ ...CodexChannels,
3776
+ ...ContentChannels,
3777
+ ...DevToolsChannels,
3778
+ ...DownloadChannels,
3779
+ ...HighlightChannels,
3780
+ ...HistoryChannels,
3781
+ ...HumanVaultChannels,
3782
+ ...OpenRouterChannels,
3783
+ ...PermissionChannels,
3784
+ ...PremiumChannels,
3785
+ ...ResearchChannels,
3786
+ ...SecurityChannels,
3787
+ ...SessionChannels,
3788
+ ...SettingsChannels,
3789
+ ...TabChannels,
3790
+ ...UIChannels,
3791
+ ...UpdateChannels,
3792
+ ...VaultChannels,
3793
+ ...WindowControlChannels
3736
3794
  };
3737
3795
  const MAX_DETAIL_ITEMS = 3;
3738
3796
  const MIN_BLOCK_SIMILARITY = 0.82;
@@ -4690,7 +4748,7 @@ function errorResult(error, value) {
4690
4748
  function getErrorMessage(error, fallback = "Unknown error") {
4691
4749
  return error instanceof Error && error.message ? error.message : fallback;
4692
4750
  }
4693
- const logger$q = createLogger("Premium");
4751
+ const logger$r = createLogger("Premium");
4694
4752
  const VERIFICATION_API = process.env.VESSEL_PREMIUM_API || "https://vesselpremium.quantaintellect.com";
4695
4753
  const FREE_TOOL_ITERATION_LIMIT = 50;
4696
4754
  const REVALIDATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
@@ -4866,7 +4924,7 @@ async function verifySubscription$1(identifier) {
4866
4924
  });
4867
4925
  if (!res.ok) {
4868
4926
  const detail = await readApiErrorDetail(res);
4869
- logger$q.warn(
4927
+ logger$r.warn(
4870
4928
  "Verification API returned a non-OK status:",
4871
4929
  res.status,
4872
4930
  detail
@@ -4885,7 +4943,7 @@ async function verifySubscription$1(identifier) {
4885
4943
  setSetting("premium", updated);
4886
4944
  return updated;
4887
4945
  } catch (err) {
4888
- logger$q.warn("Verification failed:", err);
4946
+ logger$r.warn("Verification failed:", err);
4889
4947
  return current;
4890
4948
  }
4891
4949
  }
@@ -5540,7 +5598,7 @@ const EXTRACT_TIMEOUT_MAX_MS = 2e4;
5540
5598
  const MUTATION_CAPTURE_INTERVAL_MS = 5e3;
5541
5599
  const MUTATION_SETTLE_AFTER_MS = 1500;
5542
5600
  const AGENT_STREAM_IDLE_TIMEOUT_MS = 3e4;
5543
- const logger$p = createLogger("Extractor");
5601
+ const logger$q = createLogger("Extractor");
5544
5602
  const EXTRACTION_CACHE_TTL_MS = 1500;
5545
5603
  const MAX_EXTRACTION_CACHE_ENTRIES = 50;
5546
5604
  const extractionCache = new BoundedCache(
@@ -6305,9 +6363,9 @@ async function executeScript(webContents, script, options = {}) {
6305
6363
  const message = err instanceof Error ? err.message : String(err);
6306
6364
  const detail = `Failed to execute page script${label} on ${url}: ${message}`;
6307
6365
  if (options.warnOnFailure) {
6308
- logger$p.warn(detail);
6366
+ logger$q.warn(detail);
6309
6367
  } else {
6310
- logger$p.debug(detail);
6368
+ logger$q.debug(detail);
6311
6369
  }
6312
6370
  return null;
6313
6371
  } finally {
@@ -6416,7 +6474,7 @@ async function estimateExtractionTimeout(webContents) {
6416
6474
  return EXTRACT_TIMEOUT_BASE_MS + extra;
6417
6475
  }
6418
6476
  } catch (err) {
6419
- logger$p.warn("Failed to estimate extraction timeout, using base timeout:", err);
6477
+ logger$q.warn("Failed to estimate extraction timeout, using base timeout:", err);
6420
6478
  }
6421
6479
  return EXTRACT_TIMEOUT_BASE_MS;
6422
6480
  }
@@ -6775,11 +6833,21 @@ function getActiveTabInfo(tabManager) {
6775
6833
  if (wc.isDestroyed()) return null;
6776
6834
  return { tab, wc };
6777
6835
  }
6836
+ function createWindowStateMessenger(chromeView, sidebarView, devtoolsPanelView) {
6837
+ return (channel, ...args) => {
6838
+ sendSafe(chromeView.webContents, channel, ...args);
6839
+ sendSafe(sidebarView.webContents, channel, ...args);
6840
+ sendSafe(devtoolsPanelView.webContents, channel, ...args);
6841
+ };
6842
+ }
6778
6843
  function sendSafe(wc, channel, ...args) {
6779
6844
  if (!wc || wc.isDestroyed()) return;
6780
6845
  try {
6781
6846
  wc.send(channel, ...args);
6782
- } catch {
6847
+ } catch (err) {
6848
+ if (process.env.VESSEL_DEBUG === "1" || process.env.VESSEL_DEBUG === "true") {
6849
+ console.debug("sendSafe failed for channel", channel, err);
6850
+ }
6783
6851
  }
6784
6852
  }
6785
6853
  function setSidebarPanelMode(state2, mode, reason = "user") {
@@ -7374,6 +7442,82 @@ function isClickReadLoop(names) {
7374
7442
  return clickReadPairs >= 2;
7375
7443
  }
7376
7444
  const TERMINAL_TOOL_RESULT = "__VESSEL_TERMINAL_TOOL_RESULT__";
7445
+ const logger$p = createLogger("PromptCache");
7446
+ function shortHash(value) {
7447
+ return crypto$2.createHash("sha256").update(value).digest("hex").slice(0, 12);
7448
+ }
7449
+ function buildPromptCacheKey(input) {
7450
+ return [
7451
+ "vessel",
7452
+ input.providerId,
7453
+ input.mode,
7454
+ input.profile ?? "default",
7455
+ shortHash(input.model.trim().toLowerCase())
7456
+ ].join(":");
7457
+ }
7458
+ function openAIPromptCacheOptions(input) {
7459
+ if (input.providerId !== "openai") return {};
7460
+ return {
7461
+ prompt_cache_key: buildPromptCacheKey(input),
7462
+ prompt_cache_retention: "in_memory",
7463
+ stream_options: { include_usage: true }
7464
+ };
7465
+ }
7466
+ function withAnthropicCacheControl(value) {
7467
+ return {
7468
+ ...value,
7469
+ cache_control: { type: "ephemeral" }
7470
+ };
7471
+ }
7472
+ function anthropicCachedSystem(systemPrompt) {
7473
+ return [
7474
+ withAnthropicCacheControl({
7475
+ type: "text",
7476
+ text: systemPrompt
7477
+ })
7478
+ ];
7479
+ }
7480
+ function anthropicCachedTools(tools) {
7481
+ if (tools.length === 0) return tools;
7482
+ return tools.map(
7483
+ (tool, index) => index === tools.length - 1 ? withAnthropicCacheControl(tool) : tool
7484
+ );
7485
+ }
7486
+ function numericField(record, key2) {
7487
+ const value = record[key2];
7488
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
7489
+ }
7490
+ function logOpenAIPromptCacheUsage(usage, context) {
7491
+ if (!usage || typeof usage !== "object") return;
7492
+ const record = usage;
7493
+ const promptTokens = numericField(record, "prompt_tokens");
7494
+ const details = record.prompt_tokens_details;
7495
+ const cachedTokens = details && typeof details === "object" ? numericField(details, "cached_tokens") : null;
7496
+ if (promptTokens === null && cachedTokens === null) return;
7497
+ logger$p.debug("OpenAI prompt cache usage", {
7498
+ model: context.model,
7499
+ mode: context.mode,
7500
+ promptTokens,
7501
+ cachedTokens
7502
+ });
7503
+ }
7504
+ function logAnthropicPromptCacheUsage(usage, context) {
7505
+ if (!usage || typeof usage !== "object") return;
7506
+ const record = usage;
7507
+ const inputTokens = numericField(record, "input_tokens");
7508
+ const cacheCreationTokens = numericField(record, "cache_creation_input_tokens");
7509
+ const cacheReadTokens = numericField(record, "cache_read_input_tokens");
7510
+ if (inputTokens === null && cacheCreationTokens === null && cacheReadTokens === null) {
7511
+ return;
7512
+ }
7513
+ logger$p.debug("Anthropic prompt cache usage", {
7514
+ model: context.model,
7515
+ mode: context.mode,
7516
+ inputTokens,
7517
+ cacheCreationTokens,
7518
+ cacheReadTokens
7519
+ });
7520
+ }
7377
7521
  const ANTHROPIC_MAX_TOKENS = 4096;
7378
7522
  function isRecord$1(value) {
7379
7523
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -7419,13 +7563,19 @@ class AnthropicProvider {
7419
7563
  {
7420
7564
  model: this.model,
7421
7565
  max_tokens: ANTHROPIC_MAX_TOKENS,
7422
- system: systemPrompt,
7566
+ system: anthropicCachedSystem(systemPrompt),
7423
7567
  messages,
7424
7568
  ...thinking ? { thinking } : {}
7425
7569
  },
7426
7570
  { signal: this.abortController.signal }
7427
7571
  );
7428
7572
  for await (const event of stream) {
7573
+ if (event.type === "message_start") {
7574
+ logAnthropicPromptCacheUsage(event.message.usage, {
7575
+ model: this.model,
7576
+ mode: "chat"
7577
+ });
7578
+ }
7429
7579
  if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
7430
7580
  onChunk(event.delta.text);
7431
7581
  }
@@ -7459,9 +7609,9 @@ class AnthropicProvider {
7459
7609
  {
7460
7610
  model: this.model,
7461
7611
  max_tokens: ANTHROPIC_MAX_TOKENS,
7462
- system: systemPrompt,
7612
+ system: anthropicCachedSystem(systemPrompt),
7463
7613
  messages,
7464
- tools,
7614
+ tools: anthropicCachedTools(tools),
7465
7615
  ...thinking ? { thinking } : {}
7466
7616
  },
7467
7617
  { signal: this.abortController.signal }
@@ -7481,6 +7631,12 @@ class AnthropicProvider {
7481
7631
  try {
7482
7632
  for await (const event of stream) {
7483
7633
  resetIdleTimer();
7634
+ if (event.type === "message_start") {
7635
+ logAnthropicPromptCacheUsage(event.message.usage, {
7636
+ model: this.model,
7637
+ mode: "agent"
7638
+ });
7639
+ }
7484
7640
  if (event.type === "content_block_start") {
7485
7641
  if (event.content_block.type === "tool_use") {
7486
7642
  currentToolUse = {
@@ -8573,11 +8729,21 @@ class OpenAICompatProvider {
8573
8729
  max_tokens: 4096,
8574
8730
  stream: true,
8575
8731
  messages,
8732
+ ...openAIPromptCacheOptions({
8733
+ providerId: this.providerId,
8734
+ model: this.model,
8735
+ mode: "chat",
8736
+ profile: this.agentToolProfile
8737
+ }),
8576
8738
  ...reasoningEffort ? { reasoning_effort: reasoningEffort } : {}
8577
8739
  },
8578
8740
  { signal: this.abortController.signal }
8579
8741
  );
8580
8742
  for await (const chunk of stream) {
8743
+ logOpenAIPromptCacheUsage(chunk.usage, {
8744
+ model: this.model,
8745
+ mode: "chat"
8746
+ });
8581
8747
  const choice = chunk.choices[0];
8582
8748
  if (!choice) continue;
8583
8749
  const delta = choice.delta;
@@ -8644,11 +8810,21 @@ class OpenAICompatProvider {
8644
8810
  tools: openAITools,
8645
8811
  tool_choice: "auto",
8646
8812
  temperature: agentTemperatureForProfile(this.agentToolProfile),
8813
+ ...openAIPromptCacheOptions({
8814
+ providerId: this.providerId,
8815
+ model: this.model,
8816
+ mode: "agent",
8817
+ profile: this.agentToolProfile
8818
+ }),
8647
8819
  ...reasoningEffort ? { reasoning_effort: reasoningEffort } : {}
8648
8820
  },
8649
8821
  { signal: this.abortController.signal }
8650
8822
  );
8651
8823
  for await (const chunk of stream) {
8824
+ logOpenAIPromptCacheUsage(chunk.usage, {
8825
+ model: this.model,
8826
+ mode: "agent"
8827
+ });
8652
8828
  const choice = chunk.choices[0];
8653
8829
  if (!choice) continue;
8654
8830
  const delta = choice.delta;
@@ -19845,15 +20021,14 @@ function installAdBlocking(tabManager) {
19845
20021
  callback(getRequestFilterDecision(details, false) ?? {});
19846
20022
  return;
19847
20023
  }
19848
- const manager = [...defaultSessionTabManagers].find(
19849
- (candidate) => candidate.findTabByWebContentsId(webContentsId)
19850
- );
19851
- callback(
19852
- getRequestFilterDecision(
19853
- details,
19854
- manager?.isAdBlockingEnabledForWebContents(webContentsId) ?? false
19855
- ) ?? {}
19856
- );
20024
+ let enabled = false;
20025
+ for (const candidate of defaultSessionTabManagers) {
20026
+ if (candidate.findTabByWebContentsId(webContentsId)) {
20027
+ enabled = candidate.isAdBlockingEnabledForWebContents(webContentsId);
20028
+ break;
20029
+ }
20030
+ }
20031
+ callback(getRequestFilterDecision(details, enabled) ?? {});
19857
20032
  });
19858
20033
  }
19859
20034
  function unregisterAdBlockingTabManager(tabManager) {
@@ -20516,8 +20691,8 @@ const GroupIdSchema = zod.z.string().min(1);
20516
20691
  const UrlSchema = zod.z.string().min(1);
20517
20692
  const ColorSchema = zod.z.string().min(1);
20518
20693
  const FindActionSchema = zod.z.enum(["clearSelection", "keepSelection", "activateSelection"]);
20519
- function registerTabHandlers(windowState, _sendToRendererViews) {
20520
- const { tabManager, mainWindow } = windowState;
20694
+ function registerTabHandlers(windowState2, _sendToRendererViews) {
20695
+ const { tabManager, mainWindow } = windowState2;
20521
20696
  electron.ipcMain.handle(Channels.OPEN_PRIVATE_WINDOW, (event) => {
20522
20697
  assertTrustedIpcSender(event);
20523
20698
  createPrivateWindow();
@@ -20533,20 +20708,20 @@ function registerTabHandlers(windowState, _sendToRendererViews) {
20533
20708
  electron.ipcMain.handle(Channels.TAB_CREATE, (event, url) => {
20534
20709
  assertTrustedIpcSender(event);
20535
20710
  const id = tabManager.createTab(url || loadSettings().defaultUrl);
20536
- layoutViews(windowState);
20711
+ layoutViews(windowState2);
20537
20712
  return id;
20538
20713
  });
20539
20714
  electron.ipcMain.handle(Channels.TAB_CLOSE, (event, id) => {
20540
20715
  assertTrustedIpcSender(event);
20541
20716
  const validated = parseIpc(TabIdSchema, id, "tabId");
20542
20717
  tabManager.closeTab(validated);
20543
- layoutViews(windowState);
20718
+ layoutViews(windowState2);
20544
20719
  });
20545
20720
  electron.ipcMain.handle(Channels.TAB_SWITCH, (event, id) => {
20546
20721
  assertTrustedIpcSender(event);
20547
20722
  const validated = parseIpc(TabIdSchema, id, "tabId");
20548
20723
  tabManager.switchTab(validated);
20549
- layoutViews(windowState);
20724
+ layoutViews(windowState2);
20550
20725
  });
20551
20726
  electron.ipcMain.handle(
20552
20727
  Channels.TAB_NAVIGATE,
@@ -20593,14 +20768,14 @@ function registerTabHandlers(windowState, _sendToRendererViews) {
20593
20768
  electron.ipcMain.handle(Channels.TAB_REOPEN_CLOSED, (event) => {
20594
20769
  assertTrustedIpcSender(event);
20595
20770
  const id = tabManager.reopenClosedTab();
20596
- if (id) layoutViews(windowState);
20771
+ if (id) layoutViews(windowState2);
20597
20772
  return id;
20598
20773
  });
20599
20774
  electron.ipcMain.handle(Channels.TAB_DUPLICATE, (event, id) => {
20600
20775
  assertTrustedIpcSender(event);
20601
20776
  const validated = parseIpc(TabIdSchema, id, "id");
20602
20777
  const newId = tabManager.duplicateTab(validated);
20603
- if (newId) layoutViews(windowState);
20778
+ if (newId) layoutViews(windowState2);
20604
20779
  return newId;
20605
20780
  });
20606
20781
  electron.ipcMain.handle(Channels.TAB_PIN, (event, id) => {
@@ -20654,7 +20829,7 @@ function registerTabHandlers(windowState, _sendToRendererViews) {
20654
20829
  });
20655
20830
  electron.ipcMain.on(Channels.TAB_CONTEXT_MENU, (event, id) => {
20656
20831
  assertTrustedIpcSender(event);
20657
- showTabContextMenu(tabManager, parseIpc(TabIdSchema, id, "id"), mainWindow, () => layoutViews(windowState));
20832
+ showTabContextMenu(tabManager, parseIpc(TabIdSchema, id, "id"), mainWindow, () => layoutViews(windowState2));
20658
20833
  });
20659
20834
  electron.ipcMain.on(Channels.TAB_GROUP_CONTEXT_MENU, (event, groupId) => {
20660
20835
  assertTrustedIpcSender(event);
@@ -20667,7 +20842,7 @@ function registerTabHandlers(windowState, _sendToRendererViews) {
20667
20842
  activeId: tabManager.getActiveTabId() || ""
20668
20843
  };
20669
20844
  });
20670
- const findBridge = createFindInPageBridge(tabManager, windowState.chromeView);
20845
+ const findBridge = createFindInPageBridge(tabManager, windowState2.chromeView);
20671
20846
  electron.ipcMain.handle(Channels.FIND_IN_PAGE_START, (event, text, options) => {
20672
20847
  assertTrustedIpcSender(event);
20673
20848
  return findBridge.start(text, options);
@@ -21510,8 +21685,8 @@ function renderReaderContent(page) {
21510
21685
  function escapeHtml(str) {
21511
21686
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
21512
21687
  }
21513
- function registerContentHandlers(windowState) {
21514
- const { tabManager } = windowState;
21688
+ function registerContentHandlers(windowState2) {
21689
+ const { tabManager } = windowState2;
21515
21690
  electron.ipcMain.handle(Channels.CONTENT_EXTRACT, async (event) => {
21516
21691
  assertTrustedIpcSender(event);
21517
21692
  const activeTab = tabManager.getActiveTab();
@@ -21541,14 +21716,14 @@ function registerContentHandlers(windowState) {
21541
21716
  });
21542
21717
  electron.ipcMain.handle(Channels.FOCUS_MODE_TOGGLE, (event) => {
21543
21718
  assertTrustedIpcSender(event);
21544
- windowState.uiState.focusMode = !windowState.uiState.focusMode;
21545
- layoutViews(windowState);
21546
- return windowState.uiState.focusMode;
21719
+ windowState2.uiState.focusMode = !windowState2.uiState.focusMode;
21720
+ layoutViews(windowState2);
21721
+ return windowState2.uiState.focusMode;
21547
21722
  });
21548
21723
  }
21549
21724
  const logger$f = createLogger("HighlightIPC");
21550
- function registerHighlightHandlers(windowState, sendToRendererViews) {
21551
- const { tabManager, chromeView } = windowState;
21725
+ function registerHighlightHandlers(windowState2, sendToRendererViews) {
21726
+ const { tabManager, chromeView } = windowState2;
21552
21727
  const getActiveHighlightCountSafe = async () => {
21553
21728
  const info = getActiveTabInfo(tabManager);
21554
21729
  if (!info) return 0;
@@ -27010,7 +27185,8 @@ function registerSettingsHandlers(tabManager, runtime2, sendToRendererViews, get
27010
27185
  if (key2 === "chatProvider" && researchOrchestrator) {
27011
27186
  try {
27012
27187
  researchOrchestrator.setProvider(createProvider(value));
27013
- } catch {
27188
+ } catch (err) {
27189
+ console.warn("Research provider config invalid, retaining current provider:", err);
27014
27190
  }
27015
27191
  }
27016
27192
  const rendererSettings = getRendererSettings();
@@ -27271,7 +27447,8 @@ async function togglePictureInPicture(tabManager) {
27271
27447
  }
27272
27448
  })()
27273
27449
  `);
27274
- } catch {
27450
+ } catch (err) {
27451
+ console.warn("Picture-in-picture toggle failed:", err);
27275
27452
  return false;
27276
27453
  }
27277
27454
  }
@@ -27529,8 +27706,8 @@ function isValidJobData(v) {
27529
27706
  const j = v;
27530
27707
  return typeof j.kitId === "string" && j.kitId.length > 0 && typeof j.kitName === "string" && j.kitName.length > 0 && typeof j.kitIcon === "string" && typeof j.renderedPrompt === "string" && j.renderedPrompt.length > 0 && (j.fieldValues === void 0 || isStringRecord(j.fieldValues)) && isValidScheduleConfig(j.schedule) && typeof j.enabled === "boolean";
27531
27708
  }
27532
- async function fireJob(job, windowState, runtime2) {
27533
- const { chromeView, sidebarView, devtoolsPanelView, tabManager } = windowState;
27709
+ async function fireJob(job, windowState2, runtime2) {
27710
+ const { chromeView, sidebarView, devtoolsPanelView, tabManager } = windowState2;
27534
27711
  const send = (channel, ...args) => {
27535
27712
  if (!chromeView.webContents.isDestroyed())
27536
27713
  chromeView.webContents.send(channel, ...args);
@@ -27594,7 +27771,7 @@ async function fireJob(job, windowState, runtime2) {
27594
27771
  finishActivity("failed");
27595
27772
  }
27596
27773
  }
27597
- function tick(windowState, runtime2) {
27774
+ function tick(windowState2, runtime2) {
27598
27775
  if (isAIStreamActive()) return;
27599
27776
  const dueIds = jobs.filter((job) => job.enabled && /* @__PURE__ */ new Date() >= new Date(job.nextRunAt)).map((job) => job.id);
27600
27777
  if (dueIds.length === 0) return;
@@ -27603,7 +27780,7 @@ function tick(windowState, runtime2) {
27603
27780
  const fireNext = () => {
27604
27781
  if (idx >= dueIds.length) {
27605
27782
  endAIStream("scheduled");
27606
- queueMicrotask(() => tick(windowState, runtime2));
27783
+ queueMicrotask(() => tick(windowState2, runtime2));
27607
27784
  return;
27608
27785
  }
27609
27786
  const jobId = dueIds[idx++];
@@ -27625,26 +27802,26 @@ function tick(windowState, runtime2) {
27625
27802
  }
27626
27803
  saveJobs();
27627
27804
  broadcastFn?.(Channels.SCHEDULE_JOBS_UPDATE, jobs);
27628
- void fireJob(job, windowState, runtime2).catch((err) => {
27805
+ void fireJob(job, windowState2, runtime2).catch((err) => {
27629
27806
  logger$8.warn("Unexpected error firing job:", err);
27630
27807
  }).finally(fireNext);
27631
27808
  };
27632
27809
  fireNext();
27633
27810
  }
27634
- function registerScheduleHandlers(windowState, runtime2, sendToAll) {
27811
+ function registerScheduleHandlers(windowState2, runtime2, sendToAll) {
27635
27812
  stopScheduler();
27636
27813
  broadcastFn = sendToAll;
27637
27814
  loadJobs();
27638
27815
  if (normalizeJobs()) {
27639
27816
  saveJobs();
27640
27817
  }
27641
- removeIdleListener = onAIStreamIdle(() => tick(windowState, runtime2));
27818
+ removeIdleListener = onAIStreamIdle(() => tick(windowState2, runtime2));
27642
27819
  const now = /* @__PURE__ */ new Date();
27643
27820
  const msToNextMinute = (60 - now.getSeconds()) * 1e3 - now.getMilliseconds();
27644
27821
  alignStartTimeout = setTimeout(() => {
27645
27822
  alignStartTimeout = null;
27646
- tick(windowState, runtime2);
27647
- pollInterval = setInterval(() => tick(windowState, runtime2), 6e4);
27823
+ tick(windowState2, runtime2);
27824
+ pollInterval = setInterval(() => tick(windowState2, runtime2), 6e4);
27648
27825
  }, msToNextMinute);
27649
27826
  electron.ipcMain.handle(Channels.SCHEDULE_GET_ALL, (event) => {
27650
27827
  assertTrustedIpcSender(event);
@@ -27722,19 +27899,19 @@ function stopScheduler() {
27722
27899
  }
27723
27900
  const KitIdSchema = zod.z.string().min(1);
27724
27901
  const OriginSchema = zod.z.string().min(1);
27725
- function registerSystemHandlers(windowState, sendToRendererViews) {
27726
- const { tabManager } = windowState;
27902
+ function registerSystemHandlers(windowState2, sendToRendererViews) {
27903
+ const { tabManager } = windowState2;
27727
27904
  electron.ipcMain.handle(Channels.DEVTOOLS_PANEL_TOGGLE, (event) => {
27728
27905
  assertTrustedIpcSender(event);
27729
- windowState.uiState.devtoolsPanelOpen = !windowState.uiState.devtoolsPanelOpen;
27730
- layoutViews(windowState);
27731
- return { open: windowState.uiState.devtoolsPanelOpen };
27906
+ windowState2.uiState.devtoolsPanelOpen = !windowState2.uiState.devtoolsPanelOpen;
27907
+ layoutViews(windowState2);
27908
+ return { open: windowState2.uiState.devtoolsPanelOpen };
27732
27909
  });
27733
27910
  electron.ipcMain.handle(Channels.DEVTOOLS_PANEL_RESIZE, (event, height) => {
27734
27911
  assertTrustedIpcSender(event);
27735
27912
  const clamped = Math.max(MIN_DEVTOOLS_PANEL, Math.min(MAX_DEVTOOLS_PANEL, Math.round(height)));
27736
- windowState.uiState.devtoolsPanelHeight = clamped;
27737
- layoutViews(windowState);
27913
+ windowState2.uiState.devtoolsPanelHeight = clamped;
27914
+ layoutViews(windowState2);
27738
27915
  return clamped;
27739
27916
  });
27740
27917
  electron.ipcMain.handle(Channels.AUTOMATION_GET_INSTALLED, (event) => {
@@ -28484,10 +28661,10 @@ function registerOpenRouterHandlers(applySettingChange) {
28484
28661
  return { ok: true };
28485
28662
  });
28486
28663
  }
28487
- function registerSidebarHandlers(windowState, requireTrusted) {
28664
+ function registerSidebarHandlers(windowState2, requireTrusted) {
28488
28665
  let sidebarResizeRecoveryTimer = null;
28489
28666
  let sidebarResizeActive = false;
28490
- const relayout = () => layoutViews(windowState);
28667
+ const relayout = () => layoutViews(windowState2);
28491
28668
  const clearSidebarResizeRecoveryTimer = () => {
28492
28669
  if (!sidebarResizeRecoveryTimer) return;
28493
28670
  clearTimeout(sidebarResizeRecoveryTimer);
@@ -28509,32 +28686,32 @@ function registerSidebarHandlers(windowState, requireTrusted) {
28509
28686
  restoreSidebarLayoutAfterResize();
28510
28687
  }, 1200);
28511
28688
  };
28512
- windowState.mainWindow.once("closed", stopSidebarResize);
28689
+ windowState2.mainWindow.once("closed", stopSidebarResize);
28513
28690
  electron.ipcMain.handle(Channels.SIDEBAR_TOGGLE, (event) => {
28514
28691
  requireTrusted(event);
28515
- return toggleDockedSidebar(windowState, relayout);
28692
+ return toggleDockedSidebar(windowState2, relayout);
28516
28693
  });
28517
28694
  electron.ipcMain.handle(Channels.SIDEBAR_NAVIGATE, (event, tab) => {
28518
28695
  requireTrusted(event);
28519
28696
  assertString(tab, "tab");
28520
- if (windowState.uiState.sidebarPanelMode === "closed") {
28521
- openDockedSidebar(windowState, relayout);
28697
+ if (windowState2.uiState.sidebarPanelMode === "closed") {
28698
+ openDockedSidebar(windowState2, relayout);
28522
28699
  }
28523
- if (!windowState.sidebarView.webContents.isDestroyed()) {
28524
- windowState.sidebarView.webContents.send(Channels.SIDEBAR_NAVIGATE, tab);
28700
+ if (!windowState2.sidebarView.webContents.isDestroyed()) {
28701
+ windowState2.sidebarView.webContents.send(Channels.SIDEBAR_NAVIGATE, tab);
28525
28702
  }
28526
- windowState.sidebarWindow?.focus();
28527
- return emitSidebarPanelState(windowState);
28703
+ windowState2.sidebarWindow?.focus();
28704
+ return emitSidebarPanelState(windowState2);
28528
28705
  });
28529
28706
  electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_START, (event) => {
28530
28707
  requireTrusted(event);
28531
- if (isSidebarDetached(windowState)) return;
28708
+ if (isSidebarDetached(windowState2)) return;
28532
28709
  sidebarResizeActive = true;
28533
28710
  clearSidebarResizeRecoveryTimer();
28534
- const [width, height] = windowState.mainWindow.getContentSize();
28535
- const chromeHeight = windowState.uiState.focusMode ? 0 : CHROME_HEIGHT;
28536
- const sidebarWidth = windowState.uiState.sidebarWidth;
28537
- windowState.sidebarView.setBounds({
28711
+ const [width, height] = windowState2.mainWindow.getContentSize();
28712
+ const chromeHeight = windowState2.uiState.focusMode ? 0 : CHROME_HEIGHT;
28713
+ const sidebarWidth = windowState2.uiState.sidebarWidth;
28714
+ windowState2.sidebarView.setBounds({
28538
28715
  x: width - sidebarWidth - SIDEBAR_RESIZE_HANDLE_OVERLAP,
28539
28716
  y: chromeHeight,
28540
28717
  width: sidebarWidth + SIDEBAR_RESIZE_HANDLE_OVERLAP,
@@ -28545,26 +28722,26 @@ function registerSidebarHandlers(windowState, requireTrusted) {
28545
28722
  electron.ipcMain.handle(Channels.SIDEBAR_RESIZE, (event, width) => {
28546
28723
  requireTrusted(event);
28547
28724
  assertNumber(width, "width");
28548
- if (isSidebarDetached(windowState)) {
28549
- return windowState.uiState.sidebarWidth;
28725
+ if (isSidebarDetached(windowState2)) {
28726
+ return windowState2.uiState.sidebarWidth;
28550
28727
  }
28551
28728
  const clamped = clampSidebarWidth(width);
28552
- windowState.uiState.sidebarWidth = clamped;
28553
- resizeSidebarViews(windowState);
28554
- emitSidebarPanelState(windowState);
28729
+ windowState2.uiState.sidebarWidth = clamped;
28730
+ resizeSidebarViews(windowState2);
28731
+ emitSidebarPanelState(windowState2);
28555
28732
  return clamped;
28556
28733
  });
28557
28734
  electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_COMMIT, (event) => {
28558
28735
  requireTrusted(event);
28559
- if (isSidebarDetached(windowState)) return;
28736
+ if (isSidebarDetached(windowState2)) return;
28560
28737
  stopSidebarResize();
28561
- setSetting("sidebarWidth", windowState.uiState.sidebarWidth);
28738
+ setSetting("sidebarWidth", windowState2.uiState.sidebarWidth);
28562
28739
  relayout();
28563
28740
  });
28564
28741
  electron.ipcMain.handle(Channels.SIDEBAR_POPOUT, (event) => {
28565
28742
  requireTrusted(event);
28566
28743
  stopSidebarResize();
28567
- return detachSidebar(windowState, {
28744
+ return detachSidebar(windowState2, {
28568
28745
  relayout,
28569
28746
  getWindowIconPath
28570
28747
  });
@@ -28572,26 +28749,26 @@ function registerSidebarHandlers(windowState, requireTrusted) {
28572
28749
  electron.ipcMain.handle(Channels.SIDEBAR_DOCK, (event) => {
28573
28750
  requireTrusted(event);
28574
28751
  stopSidebarResize();
28575
- return dockSidebar(windowState, { relayout });
28752
+ return dockSidebar(windowState2, { relayout });
28576
28753
  });
28577
28754
  electron.ipcMain.on(
28578
28755
  Channels.RENDERER_VIEW_READY,
28579
28756
  (event, view) => {
28580
28757
  requireTrusted(event);
28581
28758
  if (view !== "sidebar") return;
28582
- emitSidebarPanelState(windowState);
28759
+ emitSidebarPanelState(windowState2);
28583
28760
  }
28584
28761
  );
28585
28762
  electron.ipcMain.handle(Channels.SETTINGS_VISIBILITY, (event, open) => {
28586
28763
  requireTrusted(event);
28587
- windowState.uiState.settingsOpen = open;
28764
+ windowState2.uiState.settingsOpen = open;
28588
28765
  if (open) {
28589
- closeSidebar(windowState, relayout, "temporary");
28766
+ closeSidebar(windowState2, relayout, "temporary");
28590
28767
  } else {
28591
28768
  relayout();
28592
- emitSidebarPanelState(windowState);
28769
+ emitSidebarPanelState(windowState2);
28593
28770
  }
28594
- return windowState.uiState.settingsOpen;
28771
+ return windowState2.uiState.settingsOpen;
28595
28772
  });
28596
28773
  }
28597
28774
  function assertVaultUnlocked() {
@@ -29131,7 +29308,7 @@ function sanitizeAutofillUpdates(value) {
29131
29308
  }
29132
29309
  return updates;
29133
29310
  }
29134
- function registerAutofillHandlers(windowState) {
29311
+ function registerAutofillHandlers(windowState2) {
29135
29312
  electron.ipcMain.handle(Channels.AUTOFILL_LIST, (event) => {
29136
29313
  assertTrustedIpcSender(event);
29137
29314
  return listProfiles();
@@ -29158,7 +29335,7 @@ function registerAutofillHandlers(windowState) {
29158
29335
  assertString(profileId, "profileId");
29159
29336
  const profile = getProfile(profileId);
29160
29337
  if (!profile) throw new Error("Profile not found");
29161
- const activeTab = windowState.tabManager.getActiveTab();
29338
+ const activeTab = windowState2.tabManager.getActiveTab();
29162
29339
  const wc = activeTab?.view.webContents;
29163
29340
  if (!wc) throw new Error("No active tab");
29164
29341
  const content = await extractContent$1(wc);
@@ -29188,9 +29365,9 @@ function registerAutofillHandlers(windowState) {
29188
29365
  };
29189
29366
  });
29190
29367
  }
29191
- function registerPageDiffHandlers(windowState, sendToRendererViews) {
29368
+ function registerPageDiffHandlers(windowState2, sendToRendererViews) {
29192
29369
  const pageEventBuckets = /* @__PURE__ */ new Map();
29193
- const isActiveWebContents = (webContentsId) => windowState.tabManager.getActiveTab()?.view.webContents.id === webContentsId;
29370
+ const isActiveWebContents = (webContentsId) => windowState2.tabManager.getActiveTab()?.view.webContents.id === webContentsId;
29194
29371
  const allowPageEvent = (webContentsId) => {
29195
29372
  const now = Date.now();
29196
29373
  const bucket = pageEventBuckets.get(webContentsId);
@@ -29203,7 +29380,7 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
29203
29380
  };
29204
29381
  electron.ipcMain.handle(Channels.PAGE_DIFF_GET, (event) => {
29205
29382
  assertTrustedIpcSender(event);
29206
- const activeTab = windowState.tabManager.getActiveTab();
29383
+ const activeTab = windowState2.tabManager.getActiveTab();
29207
29384
  const wc = activeTab?.view.webContents;
29208
29385
  if (!wc) return null;
29209
29386
  return getLatestPageDiff(wc.getURL());
@@ -29214,7 +29391,7 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
29214
29391
  if (!isPremiumActiveState(getPremiumState())) {
29215
29392
  return { error: "Premium required" };
29216
29393
  }
29217
- const activeTab = windowState.tabManager.getActiveTab();
29394
+ const activeTab = windowState2.tabManager.getActiveTab();
29218
29395
  const wc = activeTab?.view.webContents;
29219
29396
  if (!wc) return [];
29220
29397
  return getPageDiffBursts(wc.getURL());
@@ -29225,7 +29402,7 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
29225
29402
  electron.ipcMain.on(Channels.PAGE_DIFF_ACTIVITY, (event) => {
29226
29403
  const wc = event.sender;
29227
29404
  if (!wc || wc.isDestroyed()) return;
29228
- if (!isManagedTabIpcSender(event, windowState.tabManager)) return;
29405
+ if (!isManagedTabIpcSender(event, windowState2.tabManager)) return;
29229
29406
  if (!allowPageEvent(wc.id)) return;
29230
29407
  invalidateExtractionCache(wc);
29231
29408
  notePageMutationActivity(wc, sendToRendererViews, {
@@ -29235,7 +29412,7 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
29235
29412
  electron.ipcMain.on(Channels.PAGE_DIFF_DIRTY, (event) => {
29236
29413
  const wc = event.sender;
29237
29414
  if (!wc || wc.isDestroyed()) return;
29238
- if (!isManagedTabIpcSender(event, windowState.tabManager)) return;
29415
+ if (!isManagedTabIpcSender(event, windowState2.tabManager)) return;
29239
29416
  if (!allowPageEvent(wc.id)) return;
29240
29417
  invalidateExtractionCache(wc);
29241
29418
  schedulePageSnapshotCapture(wc, sendToRendererViews, 0, {
@@ -29417,16 +29594,16 @@ function registerResearchHandlers(getOrchestrator) {
29417
29594
  }
29418
29595
  });
29419
29596
  }
29420
- function registerIpcHandlers(windowState, runtime2) {
29421
- const { tabManager, chromeView, sidebarView, devtoolsPanelView, mainWindow } = windowState;
29597
+ function registerIpcHandlers(windowState2, runtime2) {
29598
+ const { tabManager, chromeView, sidebarView, devtoolsPanelView, mainWindow } = windowState2;
29422
29599
  registerTrustedIpcSender(chromeView.webContents);
29423
29600
  registerTrustedIpcSender(sidebarView.webContents);
29424
29601
  registerTrustedIpcSender(devtoolsPanelView.webContents);
29425
- const sendToRendererViews = (channel, ...args) => {
29426
- sendSafe(chromeView.webContents, channel, ...args);
29427
- sendSafe(sidebarView.webContents, channel, ...args);
29428
- sendSafe(devtoolsPanelView.webContents, channel, ...args);
29429
- };
29602
+ const sendToRendererViews = createWindowStateMessenger(
29603
+ chromeView,
29604
+ sidebarView,
29605
+ devtoolsPanelView
29606
+ );
29430
29607
  let researchOrchestrator = null;
29431
29608
  const getResearchOrchestrator = () => {
29432
29609
  if (!researchOrchestrator) {
@@ -29440,10 +29617,10 @@ function registerIpcHandlers(windowState, runtime2) {
29440
29617
  return researchOrchestrator;
29441
29618
  };
29442
29619
  const getExistingResearchOrchestrator = () => researchOrchestrator;
29443
- registerTabHandlers(windowState);
29620
+ registerTabHandlers(windowState2);
29444
29621
  registerAIHandlers(tabManager, runtime2, sendToRendererViews, getResearchOrchestrator);
29445
- registerContentHandlers(windowState);
29446
- registerHighlightHandlers(windowState, sendToRendererViews);
29622
+ registerContentHandlers(windowState2);
29623
+ registerHighlightHandlers(windowState2, sendToRendererViews);
29447
29624
  registerAgentRuntimeHandlers(
29448
29625
  runtime2,
29449
29626
  chromeView.webContents,
@@ -29463,15 +29640,15 @@ function registerIpcHandlers(windowState, runtime2) {
29463
29640
  registerSecurityHandlers(tabManager);
29464
29641
  registerCodexHandlers();
29465
29642
  registerOpenRouterHandlers(applySettingChange);
29466
- registerSidebarHandlers(windowState, (event) => assertTrustedIpcSender(event));
29643
+ registerSidebarHandlers(windowState2, (event) => assertTrustedIpcSender(event));
29467
29644
  registerVaultHandlers();
29468
29645
  registerHumanVaultHandlers();
29469
29646
  registerWindowControlHandlers(mainWindow);
29470
- registerAutofillHandlers(windowState);
29471
- registerPageDiffHandlers(windowState, sendToRendererViews);
29647
+ registerAutofillHandlers(windowState2);
29648
+ registerPageDiffHandlers(windowState2, sendToRendererViews);
29472
29649
  registerResearchHandlers(() => getResearchOrchestrator());
29473
- registerScheduleHandlers(windowState, runtime2, sendToRendererViews);
29474
- registerSystemHandlers(windowState, sendToRendererViews);
29650
+ registerScheduleHandlers(windowState2, runtime2, sendToRendererViews);
29651
+ registerSystemHandlers(windowState2, sendToRendererViews);
29475
29652
  }
29476
29653
  const UNDOABLE_ACTIONS = /* @__PURE__ */ new Set([
29477
29654
  "accept_cookies",
@@ -29912,19 +30089,39 @@ class AgentRuntime {
29912
30089
  constructor(tabManager) {
29913
30090
  this.tabManager = tabManager;
29914
30091
  this.state = this.loadPersistedState();
29915
- onMcpStatusChange(() => this.emit());
30092
+ this.mcpUnsubscribe = onMcpStatusChange(() => this.emit());
29916
30093
  }
29917
30094
  tabManager;
29918
30095
  state;
29919
30096
  updateListener = null;
29920
30097
  pendingResolvers = /* @__PURE__ */ new Map();
29921
30098
  undoSnapshots = [];
30099
+ mcpUnsubscribe = null;
29922
30100
  setUpdateListener(listener) {
29923
30101
  this.updateListener = listener;
29924
30102
  if (listener) {
29925
30103
  listener(this.getState());
29926
30104
  }
29927
30105
  }
30106
+ /**
30107
+ * Release all resources, listeners, and pending promises.
30108
+ * Call when the window is closing to prevent memory leaks.
30109
+ */
30110
+ dispose() {
30111
+ this.mcpUnsubscribe?.();
30112
+ this.mcpUnsubscribe = null;
30113
+ for (const [id, resolve] of this.pendingResolvers) {
30114
+ resolve(false);
30115
+ this.pendingResolvers.delete(id);
30116
+ }
30117
+ this.undoSnapshots = [];
30118
+ this.state.actions = [];
30119
+ this.state.transcript = [];
30120
+ this.state.supervisor.pendingApprovals = [];
30121
+ this.state.flowState = null;
30122
+ this.state.taskTracker = null;
30123
+ this.updateListener = null;
30124
+ }
29928
30125
  getState() {
29929
30126
  const snapshot2 = clone(this.state);
29930
30127
  snapshot2.mcpStatus = getMcpStatus();
@@ -29978,6 +30175,7 @@ class AgentRuntime {
29978
30175
  -20
29979
30176
  );
29980
30177
  this.emit();
30178
+ void this.flushPersist();
29981
30179
  return clone(checkpoint);
29982
30180
  }
29983
30181
  restoreCheckpoint(checkpointId) {
@@ -30769,7 +30967,7 @@ Action: ${issue.action}` : "";
30769
30967
  return `${issue.title}
30770
30968
  ${issue.detail}${action}`;
30771
30969
  }
30772
- async function maybeShowStartupHealthDialog(windowState) {
30970
+ async function maybeShowStartupHealthDialog(windowState2) {
30773
30971
  const health = getRuntimeHealth();
30774
30972
  const hasIssues = health.startupIssues.length > 0 || health.mcp.status === "error";
30775
30973
  if (!hasIssues) return;
@@ -30781,7 +30979,7 @@ ${health.mcp.message}
30781
30979
  Action: Open Settings (Ctrl+,) to choose a different port, then save to restart the MCP server.`
30782
30980
  );
30783
30981
  }
30784
- await electron.dialog.showMessageBox(windowState.mainWindow, {
30982
+ await electron.dialog.showMessageBox(windowState2.mainWindow, {
30785
30983
  type: health.startupIssues.some((issue) => issue.severity === "error") || health.mcp.status === "error" ? "warning" : "info",
30786
30984
  title: "Vessel Startup Checks",
30787
30985
  message: "Vessel launched with runtime warnings.",
@@ -30817,10 +31015,10 @@ async function bootstrap() {
30817
31015
  sendSafe(state2.sidebarView.webContents, Channels.HIGHLIGHT_COUNT_UPDATE, count);
30818
31016
  sendSafe(state2.devtoolsPanelView.webContents, Channels.HIGHLIGHT_COUNT_UPDATE, count);
30819
31017
  };
30820
- const windowState = createMainWindow((tabs, activeId, meta) => {
30821
- sendSafe(windowState.chromeView.webContents, Channels.TAB_STATE_UPDATE, tabs, activeId);
30822
- void syncActiveHighlightCount(windowState);
30823
- layoutViews(windowState);
31018
+ const windowState2 = createMainWindow((tabs, activeId, meta) => {
31019
+ sendSafe(windowState2.chromeView.webContents, Channels.TAB_STATE_UPDATE, tabs, activeId);
31020
+ void syncActiveHighlightCount(windowState2);
31021
+ layoutViews(windowState2);
30824
31022
  if (meta.persistSession) {
30825
31023
  runtime?.onTabStateChanged();
30826
31024
  }
@@ -30829,7 +31027,7 @@ async function bootstrap() {
30829
31027
  const revealMainWindow = () => {
30830
31028
  if (didRevealMainWindow) return;
30831
31029
  didRevealMainWindow = true;
30832
- windowState.mainWindow.show();
31030
+ windowState2.mainWindow.show();
30833
31031
  closeSplash(splash, 0);
30834
31032
  };
30835
31033
  let didInitializeChromeRenderer = false;
@@ -30837,25 +31035,25 @@ async function bootstrap() {
30837
31035
  logger.warn("Renderer did not finish loading before splash timeout");
30838
31036
  revealMainWindow();
30839
31037
  }, 8e3);
30840
- const { chromeView, sidebarView, devtoolsPanelView, tabManager } = windowState;
31038
+ const { chromeView, sidebarView, devtoolsPanelView, tabManager } = windowState2;
30841
31039
  runtime = new AgentRuntime(tabManager);
30842
31040
  installAdBlocking(tabManager);
30843
31041
  setDevToolsPanelListener((state2) => {
30844
31042
  sendSafe(devtoolsPanelView.webContents, Channels.DEVTOOLS_PANEL_STATE, state2);
30845
31043
  });
30846
- registerIpcHandlers(windowState, runtime);
31044
+ registerIpcHandlers(windowState2, runtime);
30847
31045
  tabManager.onSecurityStateChange((tabId, state2) => {
30848
31046
  sendSafe(chromeView.webContents, Channels.SECURITY_STATE_UPDATE, { tabId, state: state2 });
30849
31047
  sendSafe(sidebarView.webContents, Channels.SECURITY_STATE_UPDATE, { tabId, state: state2 });
30850
31048
  });
30851
- registerHighlightShortcut(windowState.mainWindow, tabManager);
31049
+ registerHighlightShortcut(windowState2.mainWindow, tabManager);
30852
31050
  setupAppMenu({
30853
31051
  newWindow: () => {
30854
31052
  createSecondaryWindow();
30855
31053
  },
30856
31054
  reopenClosedTab: () => {
30857
31055
  const id = tabManager.reopenClosedTab();
30858
- if (id) layoutViews(windowState);
31056
+ if (id) layoutViews(windowState2);
30859
31057
  },
30860
31058
  zoomIn: () => {
30861
31059
  const id = tabManager.getActiveTabId();
@@ -30908,11 +31106,11 @@ async function bootstrap() {
30908
31106
  tabManager.createTab(settings2.defaultUrl);
30909
31107
  runtime.captureSession("Initial session");
30910
31108
  }
30911
- layoutViews(windowState);
30912
- setImmediate(() => layoutViews(windowState));
31109
+ layoutViews(windowState2);
31110
+ setImmediate(() => layoutViews(windowState2));
30913
31111
  clearTimeout(splashTimeout);
30914
31112
  revealMainWindow();
30915
- void maybeShowStartupHealthDialog(windowState);
31113
+ void maybeShowStartupHealthDialog(windowState2);
30916
31114
  };
30917
31115
  chromeView.webContents.once("dom-ready", () => {
30918
31116
  initializeChromeRenderer();
@@ -30957,6 +31155,8 @@ electron.app.on("window-all-closed", () => {
30957
31155
  electron.globalShortcut.unregisterAll();
30958
31156
  stopTelemetry();
30959
31157
  stopBackgroundRevalidation();
31158
+ runtime?.dispose();
31159
+ windowState?.tabManager.dispose();
30960
31160
  void Promise.all([
30961
31161
  runtime?.flushPersist() ?? Promise.resolve(),
30962
31162
  flushPersist$1(),