@quanta-intellect/vessel-browser 0.1.128 → 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");
@@ -94,7 +94,7 @@ const defaults = {
94
94
  clearBookmarksOnLaunch: false,
95
95
  obsidianVaultPath: "",
96
96
  approvalMode: "confirm-dangerous",
97
- agentTranscriptMode: "summary",
97
+ agentTranscriptMode: "off",
98
98
  chatProvider: null,
99
99
  maxToolIterations: 200,
100
100
  domainPolicy: { allowedDomains: [], blockedDomains: [] },
@@ -114,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$t = 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$t.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$t.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$t.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$t.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$t.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$t.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
  }
@@ -293,6 +293,11 @@ function sanitizeChatProvider(provider) {
293
293
  reasoningEffort: sanitizeReasoningEffortLevel$1(provider.reasoningEffort)
294
294
  } : null;
295
295
  }
296
+ function sanitizeAgentTranscriptMode(mode, legacyEnabled) {
297
+ if (mode === "full") return "full";
298
+ if (mode === "off" || mode === "summary") return "off";
299
+ return legacyEnabled === true ? "full" : "off";
300
+ }
296
301
  function loadSettings() {
297
302
  if (settings) return settings;
298
303
  settingsIssues = [];
@@ -315,7 +320,10 @@ function loadSettings() {
315
320
  sourceDoNotAllowList: sanitizeStringList(
316
321
  parsed.sourceDoNotAllowList ?? defaults.sourceDoNotAllowList
317
322
  ),
318
- agentTranscriptMode: parsed.agentTranscriptMode === "off" || parsed.agentTranscriptMode === "summary" || parsed.agentTranscriptMode === "full" ? parsed.agentTranscriptMode : parsed.showAgentTranscript === false ? "off" : defaults.agentTranscriptMode
323
+ agentTranscriptMode: sanitizeAgentTranscriptMode(
324
+ parsed.agentTranscriptMode,
325
+ parsed.showAgentTranscript
326
+ )
319
327
  };
320
328
  } catch (error) {
321
329
  if (fs.existsSync(getSettingsPath())) {
@@ -343,7 +351,7 @@ function persistNow() {
343
351
  JSON.stringify(buildPersistedSettings(settings), null, 2),
344
352
  { encoding: "utf-8", mode: 384 }
345
353
  )
346
- ).then(() => fs.promises.chmod(getSettingsPath(), 384).catch(() => void 0)).catch((err) => logger$t.error("Failed to save settings:", err));
354
+ ).then(() => fs.promises.chmod(getSettingsPath(), 384).catch(() => void 0)).catch((err) => logger$v.error("Failed to save settings:", err));
347
355
  }
348
356
  function saveSettings() {
349
357
  saveDirty = true;
@@ -522,7 +530,7 @@ const urlSafety = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
522
530
  }, Symbol.toStringTag, { value: "Module" }));
523
531
  const MAX_CUSTOM_HISTORY = 50;
524
532
  const READER_MODE_DATA_URL_PREFIX = "data:text/html;charset=utf-8,";
525
- const logger$s = createLogger("Tab");
533
+ const logger$u = createLogger("Tab");
526
534
  const sessionCertExceptions = /* @__PURE__ */ new WeakMap();
527
535
  const sessionsWithVerifyProc = /* @__PURE__ */ new WeakSet();
528
536
  const CERT_VERIFY_TRUST = 0;
@@ -588,7 +596,7 @@ class Tab {
588
596
  guardedLoadURL(url, options) {
589
597
  const blockReason = this.getNavigationBlockReason(url);
590
598
  if (blockReason) {
591
- logger$s.warn(blockReason);
599
+ logger$u.warn(blockReason);
592
600
  return blockReason;
593
601
  }
594
602
  void this.view.webContents.loadURL(url, options);
@@ -672,7 +680,7 @@ class Tab {
672
680
  wc.setWindowOpenHandler(({ url, disposition }) => {
673
681
  const error = this.getNavigationBlockReason(url);
674
682
  if (error) {
675
- logger$s.warn(error);
683
+ logger$u.warn(error);
676
684
  return { action: "deny" };
677
685
  }
678
686
  this.onOpenUrl?.({
@@ -686,7 +694,7 @@ class Tab {
686
694
  const error = this.getNavigationBlockReason(url);
687
695
  if (!error) return;
688
696
  event.preventDefault();
689
- logger$s.warn(`${context}: ${error}`);
697
+ logger$u.warn(`${context}: ${error}`);
690
698
  };
691
699
  wc.on("will-navigate", (event, url) => {
692
700
  blockNavigation(event, url, "Blocked top-level navigation");
@@ -770,7 +778,7 @@ class Tab {
770
778
  ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 999px; }
771
779
  ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.22); }
772
780
  ::-webkit-scrollbar-corner { background: transparent; }
773
- `).catch((err) => logger$s.warn("Failed to inject scrollbar CSS:", err));
781
+ `).catch((err) => logger$u.warn("Failed to inject scrollbar CSS:", err));
774
782
  });
775
783
  wc.on("page-favicon-updated", (_, favicons) => {
776
784
  this._state.favicon = favicons[0] || "";
@@ -806,7 +814,7 @@ class Tab {
806
814
  ).then((highlightedText) => {
807
815
  this.buildContextMenu(wc, params, highlightedText.trim());
808
816
  }).catch((err) => {
809
- logger$s.warn("Failed to inspect highlighted text for context menu:", err);
817
+ logger$u.warn("Failed to inspect highlighted text for context menu:", err);
810
818
  this.buildContextMenu(wc, params, "");
811
819
  });
812
820
  });
@@ -1007,7 +1015,7 @@ class Tab {
1007
1015
  "document.documentElement.outerHTML"
1008
1016
  );
1009
1017
  } catch (err) {
1010
- logger$s.warn("Failed to retrieve page source:", err);
1018
+ logger$u.warn("Failed to retrieve page source:", err);
1011
1019
  return;
1012
1020
  }
1013
1021
  const escaped = html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -1135,7 +1143,7 @@ class Tab {
1135
1143
  document.addEventListener('mouseup', window.__vesselHighlightHandler);
1136
1144
  }
1137
1145
  })()
1138
- `).catch((err) => logger$s.warn("Failed to inject highlight listener:", err));
1146
+ `).catch((err) => logger$u.warn("Failed to inject highlight listener:", err));
1139
1147
  } else {
1140
1148
  void wc.executeJavaScript(`
1141
1149
  (function() {
@@ -1146,13 +1154,13 @@ class Tab {
1146
1154
  delete window.__vesselHighlightHandler;
1147
1155
  }
1148
1156
  })()
1149
- `).catch((err) => logger$s.warn("Failed to remove highlight listener:", err));
1157
+ `).catch((err) => logger$u.warn("Failed to remove highlight listener:", err));
1150
1158
  }
1151
1159
  }
1152
1160
  get webContentsId() {
1153
1161
  return this.view.webContents.id;
1154
1162
  }
1155
- destroy() {
1163
+ dispose() {
1156
1164
  this.setHighlightMode(false);
1157
1165
  this.view.webContents.close();
1158
1166
  }
@@ -1183,7 +1191,7 @@ const SEARCH_ENGINE_PRESETS = {
1183
1191
  ecosia: { label: "Ecosia", url: "https://www.ecosia.org/search?q=" },
1184
1192
  kagi: { label: "Kagi", url: "https://kagi.com/search?q=" }
1185
1193
  };
1186
- const logger$r = createLogger("JsonPersistence");
1194
+ const logger$t = createLogger("JsonPersistence");
1187
1195
  function canUseSafeStorage() {
1188
1196
  try {
1189
1197
  return electron.safeStorage.isEncryptionAvailable();
@@ -1248,7 +1256,7 @@ function createDebouncedJsonPersistence({
1248
1256
  data,
1249
1257
  typeof data === "string" ? { encoding: "utf-8", mode: 384 } : { mode: 384 }
1250
1258
  )
1251
- ).then(() => fs.promises.chmod(filePath2, 384).catch(() => void 0)).catch((err) => logger$r.error(`Failed to save ${logLabel}:`, err));
1259
+ ).then(() => fs.promises.chmod(filePath2, 384).catch(() => void 0)).catch((err) => logger$t.error(`Failed to save ${logLabel}:`, err));
1252
1260
  };
1253
1261
  const schedule = () => {
1254
1262
  saveDirty2 = true;
@@ -2940,7 +2948,7 @@ function destroySession(tabId) {
2940
2948
  sessions.delete(tabId);
2941
2949
  }
2942
2950
  }
2943
- const logger$q = createLogger("TabManager");
2951
+ const logger$s = createLogger("TabManager");
2944
2952
  function sanitizeFilename(title, ext) {
2945
2953
  const clean = title.replace(/[<>:"/\\|?*\x00-\x1f]/g, " ").replace(/\s+/g, " ").trim();
2946
2954
  const escapedExt = ext.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -3040,7 +3048,7 @@ class TabManager {
3040
3048
  }
3041
3049
  destroySession(id);
3042
3050
  this.window.contentView.removeChildView(tab.view);
3043
- tab.destroy();
3051
+ tab.dispose();
3044
3052
  this.tabs.delete(id);
3045
3053
  this.order = this.order.filter((tid) => tid !== id);
3046
3054
  this.removeGroupIfEmpty(groupId);
@@ -3310,7 +3318,7 @@ class TabManager {
3310
3318
  if (!tab) continue;
3311
3319
  destroySession(id);
3312
3320
  this.window.contentView.removeChildView(tab.view);
3313
- tab.destroy();
3321
+ tab.dispose();
3314
3322
  }
3315
3323
  this.tabs.clear();
3316
3324
  this.order = [];
@@ -3319,6 +3327,30 @@ class TabManager {
3319
3327
  this.broadcastState({ persistSession: true });
3320
3328
  }
3321
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
+ }
3322
3354
  reapplyHighlights(url, wc) {
3323
3355
  const wcId = wc.id;
3324
3356
  const now = Date.now();
@@ -3335,7 +3367,7 @@ class TabManager {
3335
3367
  }));
3336
3368
  if (entries.length > 0) {
3337
3369
  void highlightBatchOnPage(wc, entries).catch(
3338
- (err) => logger$q.warn("Failed to batch highlight:", err)
3370
+ (err) => logger$s.warn("Failed to batch highlight:", err)
3339
3371
  );
3340
3372
  }
3341
3373
  }
@@ -3357,12 +3389,12 @@ class TabManager {
3357
3389
  const result = await captureSelectionHighlight(wc);
3358
3390
  if (result.success && result.text) {
3359
3391
  await highlightOnPage(wc, null, result.text, void 0, void 0, "yellow").catch(
3360
- (err) => logger$q.warn("Failed to capture highlight:", err)
3392
+ (err) => logger$s.warn("Failed to capture highlight:", err)
3361
3393
  );
3362
3394
  }
3363
3395
  this.highlightCaptureCallback?.(result);
3364
3396
  } catch (err) {
3365
- logger$q.warn("Failed to capture highlight from page:", err);
3397
+ logger$s.warn("Failed to capture highlight from page:", err);
3366
3398
  this.highlightCaptureCallback?.({
3367
3399
  success: false,
3368
3400
  message: "Could not capture selection"
@@ -3387,7 +3419,7 @@ class TabManager {
3387
3419
  void this.removeHighlightMarksForText(wc, text);
3388
3420
  }
3389
3421
  } catch (err) {
3390
- logger$q.warn("Failed to remove highlight from matching tab:", err);
3422
+ logger$s.warn("Failed to remove highlight from matching tab:", err);
3391
3423
  }
3392
3424
  }
3393
3425
  this.highlightCaptureCallback?.({
@@ -3418,12 +3450,12 @@ class TabManager {
3418
3450
  void 0,
3419
3451
  color
3420
3452
  ).catch(
3421
- (err) => logger$q.warn("Failed to update highlight color:", err)
3453
+ (err) => logger$s.warn("Failed to update highlight color:", err)
3422
3454
  );
3423
3455
  });
3424
3456
  }
3425
3457
  } catch (err) {
3426
- logger$q.warn("Failed to iterate highlights for color change:", err);
3458
+ logger$s.warn("Failed to iterate highlights for color change:", err);
3427
3459
  }
3428
3460
  }
3429
3461
  this.highlightCaptureCallback?.({
@@ -3478,7 +3510,7 @@ class TabManager {
3478
3510
  });
3479
3511
  })()`
3480
3512
  ).catch(
3481
- (err) => logger$q.warn("Failed to remove highlight marks:", err)
3513
+ (err) => logger$s.warn("Failed to remove highlight marks:", err)
3482
3514
  );
3483
3515
  }
3484
3516
  broadcastState(meta = { persistSession: false }) {
@@ -3493,19 +3525,7 @@ class TabManager {
3493
3525
  }
3494
3526
  }
3495
3527
  }
3496
- const Channels = {
3497
- // Tab management
3498
- TAB_CREATE: "tab:create",
3499
- TAB_CLOSE: "tab:close",
3500
- TAB_SWITCH: "tab:switch",
3501
- TAB_NAVIGATE: "tab:navigate",
3502
- TAB_BACK: "tab:back",
3503
- TAB_FORWARD: "tab:forward",
3504
- TAB_RELOAD: "tab:reload",
3505
- TAB_STATE_GET: "tab:state-get",
3506
- TAB_STATE_UPDATE: "tab:state-update",
3507
- RENDERER_VIEW_READY: "renderer:view-ready",
3508
- // AI
3528
+ const AIChannels = {
3509
3529
  AI_QUERY: "ai:query",
3510
3530
  AI_STREAM_START: "ai:stream-start",
3511
3531
  AI_STREAM_CHUNK: "ai:stream-chunk",
@@ -3528,32 +3548,26 @@ const Channels = {
3528
3548
  AGENT_CHECKPOINT_UPDATE_NOTE: "agent:checkpoint-update-note",
3529
3549
  AGENT_UNDO_LAST_ACTION: "agent:undo-last-action",
3530
3550
  AGENT_SESSION_CAPTURE: "agent:session-capture",
3531
- AGENT_SESSION_RESTORE: "agent:session-restore",
3532
- // Content
3533
- CONTENT_EXTRACT: "content:extract",
3534
- READER_MODE_TOGGLE: "reader:toggle",
3535
- // UI state
3536
- SIDEBAR_TOGGLE: "ui:sidebar-toggle",
3537
- SIDEBAR_NAVIGATE: "ui:sidebar-navigate",
3538
- SIDEBAR_RESIZE: "ui:sidebar-resize",
3539
- SIDEBAR_RESIZE_START: "ui:sidebar-resize-start",
3540
- SIDEBAR_RESIZE_COMMIT: "ui:sidebar-resize-commit",
3541
- SIDEBAR_POPOUT: "ui:sidebar-popout",
3542
- SIDEBAR_DOCK: "ui:sidebar-dock",
3543
- SIDEBAR_STATE_UPDATE: "ui:sidebar-state-update",
3544
- SIDEBAR_CONTEXT_MENU: "ui:sidebar-context-menu",
3545
- FOCUS_MODE_TOGGLE: "ui:focus-mode-toggle",
3546
- SETTINGS_VISIBILITY: "ui:settings-visibility",
3547
- // Settings
3548
- SETTINGS_GET: "settings:get",
3549
- SETTINGS_SET: "settings:set",
3550
- SETTINGS_UPDATE: "settings:update",
3551
- SETTINGS_HEALTH_GET: "settings:health:get",
3552
- SETTINGS_HEALTH_UPDATE: "settings:health:update",
3553
- MCP_REGENERATE_TOKEN: "mcp:regenerate-token",
3554
- // Support
3555
- SUPPORT_SUBMIT_FEEDBACK: "support:submit-feedback",
3556
- // 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 = {
3557
3571
  BOOKMARKS_GET: "bookmarks:get",
3558
3572
  BOOKMARKS_UPDATE: "bookmarks:update",
3559
3573
  BOOKMARK_SAVE: "bookmarks:save",
@@ -3567,8 +3581,43 @@ const Channels = {
3567
3581
  FOLDER_CREATE: "bookmarks:folder-create",
3568
3582
  FOLDER_REMOVE: "bookmarks:folder-remove",
3569
3583
  FOLDER_RENAME: "bookmarks:folder-rename",
3570
- FOLDER_EXPORT_HTML: "bookmarks:folder-export-html",
3571
- // 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 = {
3572
3621
  HIGHLIGHT_CAPTURE: "highlights:capture",
3573
3622
  HIGHLIGHT_CAPTURE_RESULT: "highlights:capture-result",
3574
3623
  HIGHLIGHT_SELECTION: "vessel:highlight-selection",
@@ -3577,154 +3626,171 @@ const Channels = {
3577
3626
  HIGHLIGHT_NAV_SCROLL: "highlights:nav-scroll",
3578
3627
  HIGHLIGHT_NAV_REMOVE: "highlights:nav-remove",
3579
3628
  HIGHLIGHT_NAV_CLEAR: "highlights:nav-clear",
3580
- SIDEBAR_HIGHLIGHT_ACTION: "highlights:sidebar-action",
3581
- // DevTools panel
3582
- DEVTOOLS_PANEL_TOGGLE: "devtools-panel:toggle",
3583
- DEVTOOLS_PANEL_STATE: "devtools-panel:state",
3584
- DEVTOOLS_PANEL_RESIZE: "devtools-panel:resize",
3585
- // 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",
3586
3713
  TAB_TOGGLE_AD_BLOCK: "tab:toggle-ad-block",
3587
- // Zoom
3588
3714
  TAB_ZOOM_IN: "tab:zoom-in",
3589
3715
  TAB_ZOOM_OUT: "tab:zoom-out",
3590
3716
  TAB_ZOOM_RESET: "tab:zoom-reset",
3591
- // Security indicator
3592
- SECURITY_STATE_UPDATE: "security:state-update",
3593
- SECURITY_SHOW_DETAILS: "security:show-details",
3594
- SECURITY_PROCEED_ANYWAY: "security:proceed-anyway",
3595
- SECURITY_GO_BACK_TO_SAFETY: "security:go-back-to-safety",
3596
- // Closed tabs / duplication
3597
3717
  TAB_REOPEN_CLOSED: "tab:reopen-closed",
3598
3718
  TAB_DUPLICATE: "tab:duplicate",
3599
3719
  TAB_CONTEXT_MENU: "tab:context-menu",
3600
- // Pin tabs
3601
3720
  TAB_PIN: "tab:pin",
3602
3721
  TAB_UNPIN: "tab:unpin",
3603
- // Tab groups
3604
3722
  TAB_GROUP_CREATE: "tab-group:create",
3605
3723
  TAB_GROUP_ADD_TAB: "tab-group:add-tab",
3606
3724
  TAB_GROUP_REMOVE_TAB: "tab-group:remove-tab",
3607
3725
  TAB_GROUP_TOGGLE_COLLAPSED: "tab-group:toggle-collapsed",
3608
3726
  TAB_GROUP_SET_COLOR: "tab-group:set-color",
3609
3727
  TAB_GROUP_CONTEXT_MENU: "tab-group:context-menu",
3610
- // Audio / mute
3611
3728
  TAB_TOGGLE_MUTE: "tab:toggle-mute",
3612
- // Print
3613
3729
  TAB_PRINT: "tab:print",
3614
3730
  TAB_PRINT_TO_PDF: "tab:print-to-pdf",
3615
- // Windows
3731
+ TAB_TOGGLE_PIP: "tab:toggle-pip",
3616
3732
  OPEN_NEW_WINDOW: "window:open-new",
3617
- // Private browsing
3618
3733
  OPEN_PRIVATE_WINDOW: "private:open-window",
3619
3734
  IS_PRIVATE_MODE: "private:is-private",
3620
- // Find in page
3621
3735
  FIND_IN_PAGE_START: "find:start",
3622
3736
  FIND_IN_PAGE_NEXT: "find:next",
3623
3737
  FIND_IN_PAGE_STOP: "find:stop",
3624
- FIND_IN_PAGE_RESULT: "find:result",
3625
- // Browsing history
3626
- HISTORY_GET: "history:get",
3627
- HISTORY_LIST: "history:list",
3628
- HISTORY_SEARCH: "history:search",
3629
- HISTORY_CLEAR: "history:clear",
3630
- HISTORY_UPDATE: "history:update",
3631
- HISTORY_EXPORT_HTML: "history:export-html",
3632
- HISTORY_EXPORT_JSON: "history:export-json",
3633
- HISTORY_IMPORT: "history:import",
3634
- // Downloads
3635
- DOWNLOAD_STARTED: "download:started",
3636
- DOWNLOAD_PROGRESS: "download:progress",
3637
- DOWNLOAD_DONE: "download:done",
3638
- DOWNLOADS_GET: "downloads:get",
3639
- DOWNLOADS_CLEAR: "downloads:clear",
3640
- DOWNLOADS_OPEN: "downloads:open",
3641
- DOWNLOADS_SHOW_IN_FOLDER: "downloads:show-in-folder",
3642
- DOWNLOADS_UPDATE: "downloads:update",
3643
- // Premium
3644
- PREMIUM_GET_STATE: "premium:get-state",
3645
- PREMIUM_ACTIVATION_START: "premium:activation-start",
3646
- PREMIUM_ACTIVATION_VERIFY: "premium:activation-verify",
3647
- PREMIUM_CHECKOUT: "premium:checkout",
3648
- PREMIUM_PORTAL: "premium:portal",
3649
- PREMIUM_RESET: "premium:reset",
3650
- PREMIUM_TRACK_CONTEXT: "premium:track-context",
3651
- PREMIUM_UPDATE: "premium:update",
3652
- // Named sessions
3653
- SESSION_LIST: "session:list",
3654
- SESSION_SAVE: "session:save",
3655
- SESSION_LOAD: "session:load",
3656
- SESSION_DELETE: "session:delete",
3657
- // 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 = {
3658
3758
  VAULT_LIST: "vault:list",
3659
3759
  VAULT_ADD: "vault:add",
3660
3760
  VAULT_UPDATE: "vault:update",
3661
3761
  VAULT_REMOVE: "vault:remove",
3662
- VAULT_AUDIT_LOG: "vault:audit-log",
3663
- // Human Password Manager
3664
- HUMAN_VAULT_LIST: "human-vault:list",
3665
- HUMAN_VAULT_GET: "human-vault:get",
3666
- HUMAN_VAULT_SAVE: "human-vault:save",
3667
- HUMAN_VAULT_UPDATE: "human-vault:update",
3668
- HUMAN_VAULT_REMOVE: "human-vault:remove",
3669
- HUMAN_VAULT_AUDIT_LOG: "human-vault:audit-log",
3670
- // Automation kits
3671
- AUTOMATION_GET_INSTALLED: "automation:get-installed",
3672
- AUTOMATION_INSTALL_FROM_FILE: "automation:install-from-file",
3673
- AUTOMATION_UNINSTALL: "automation:uninstall",
3674
- // Scheduled jobs
3675
- SCHEDULE_GET_ALL: "schedule:get-all",
3676
- SCHEDULE_CREATE: "schedule:create",
3677
- SCHEDULE_UPDATE: "schedule:update",
3678
- SCHEDULE_DELETE: "schedule:delete",
3679
- SCHEDULE_JOBS_UPDATE: "schedule:jobs-update",
3680
- // Window controls
3762
+ VAULT_AUDIT_LOG: "vault:audit-log"
3763
+ };
3764
+ const WindowControlChannels = {
3681
3765
  WINDOW_MINIMIZE: "window:minimize",
3682
3766
  WINDOW_MAXIMIZE: "window:maximize",
3683
- WINDOW_CLOSE: "window:close",
3684
- // Autofill
3685
- AUTOFILL_LIST: "autofill:list",
3686
- AUTOFILL_ADD: "autofill:add",
3687
- AUTOFILL_UPDATE: "autofill:update",
3688
- AUTOFILL_DELETE: "autofill:delete",
3689
- AUTOFILL_FILL: "autofill:fill",
3690
- // Page snapshots / What Changed
3691
- PAGE_DIFF_ACTIVITY: "page:diff-activity",
3692
- PAGE_CHANGED: "page:changed",
3693
- PAGE_DIFF_GET: "page:diff-get",
3694
- PAGE_DIFF_HISTORY: "page:diff-history",
3695
- PAGE_DIFF_DIRTY: "page:diff-dirty",
3696
- // Clear browsing data
3697
- CLEAR_BROWSING_DATA: "browsing-data:clear",
3698
- CLEAR_BROWSING_DATA_OPEN: "browsing-data:open",
3699
- // Picture-in-Picture
3700
- TAB_TOGGLE_PIP: "tab:toggle-pip",
3701
- // Research Desk
3702
- RESEARCH_STATE_GET: "research:state-get",
3703
- RESEARCH_STATE_UPDATE: "research:state-update",
3704
- RESEARCH_START_BRIEF: "research:start-brief",
3705
- RESEARCH_CONFIRM_BRIEF: "research:confirm-brief",
3706
- RESEARCH_APPROVE_OBJECTIVES: "research:approve-objectives",
3707
- RESEARCH_SET_MODE: "research:set-mode",
3708
- RESEARCH_SET_TRACES: "research:set-traces",
3709
- RESEARCH_CANCEL: "research:cancel",
3710
- RESEARCH_STOP_AND_SYNTHESIZE: "research:stop-and-synthesize",
3711
- RESEARCH_EXPORT_REPORT: "research:export-report",
3712
- // Codex OAuth
3713
- CODEX_START_AUTH: "codex:start-auth",
3714
- CODEX_CANCEL_AUTH: "codex:cancel-auth",
3715
- CODEX_AUTH_STATUS: "codex:auth-status",
3716
- CODEX_DISCONNECT: "codex:disconnect",
3717
- // OpenRouter OAuth
3718
- OPENROUTER_START_AUTH: "openrouter:start-auth",
3719
- OPENROUTER_CANCEL_AUTH: "openrouter:cancel-auth",
3720
- OPENROUTER_AUTH_STATUS: "openrouter:auth-status",
3721
- // Updates
3722
- UPDATES_CHECK: "updates:check",
3723
- UPDATES_OPEN_DOWNLOAD: "updates:open-download",
3724
- // Permissions
3725
- PERMISSIONS_GET: "permissions:get",
3726
- PERMISSIONS_CLEAR: "permissions:clear",
3727
- 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
3728
3794
  };
3729
3795
  const MAX_DETAIL_ITEMS = 3;
3730
3796
  const MIN_BLOCK_SIMILARITY = 0.82;
@@ -4682,7 +4748,7 @@ function errorResult(error, value) {
4682
4748
  function getErrorMessage(error, fallback = "Unknown error") {
4683
4749
  return error instanceof Error && error.message ? error.message : fallback;
4684
4750
  }
4685
- const logger$p = createLogger("Premium");
4751
+ const logger$r = createLogger("Premium");
4686
4752
  const VERIFICATION_API = process.env.VESSEL_PREMIUM_API || "https://vesselpremium.quantaintellect.com";
4687
4753
  const FREE_TOOL_ITERATION_LIMIT = 50;
4688
4754
  const REVALIDATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
@@ -4858,7 +4924,7 @@ async function verifySubscription$1(identifier) {
4858
4924
  });
4859
4925
  if (!res.ok) {
4860
4926
  const detail = await readApiErrorDetail(res);
4861
- logger$p.warn(
4927
+ logger$r.warn(
4862
4928
  "Verification API returned a non-OK status:",
4863
4929
  res.status,
4864
4930
  detail
@@ -4877,7 +4943,7 @@ async function verifySubscription$1(identifier) {
4877
4943
  setSetting("premium", updated);
4878
4944
  return updated;
4879
4945
  } catch (err) {
4880
- logger$p.warn("Verification failed:", err);
4946
+ logger$r.warn("Verification failed:", err);
4881
4947
  return current;
4882
4948
  }
4883
4949
  }
@@ -5532,7 +5598,7 @@ const EXTRACT_TIMEOUT_MAX_MS = 2e4;
5532
5598
  const MUTATION_CAPTURE_INTERVAL_MS = 5e3;
5533
5599
  const MUTATION_SETTLE_AFTER_MS = 1500;
5534
5600
  const AGENT_STREAM_IDLE_TIMEOUT_MS = 3e4;
5535
- const logger$o = createLogger("Extractor");
5601
+ const logger$q = createLogger("Extractor");
5536
5602
  const EXTRACTION_CACHE_TTL_MS = 1500;
5537
5603
  const MAX_EXTRACTION_CACHE_ENTRIES = 50;
5538
5604
  const extractionCache = new BoundedCache(
@@ -6297,9 +6363,9 @@ async function executeScript(webContents, script, options = {}) {
6297
6363
  const message = err instanceof Error ? err.message : String(err);
6298
6364
  const detail = `Failed to execute page script${label} on ${url}: ${message}`;
6299
6365
  if (options.warnOnFailure) {
6300
- logger$o.warn(detail);
6366
+ logger$q.warn(detail);
6301
6367
  } else {
6302
- logger$o.debug(detail);
6368
+ logger$q.debug(detail);
6303
6369
  }
6304
6370
  return null;
6305
6371
  } finally {
@@ -6408,7 +6474,7 @@ async function estimateExtractionTimeout(webContents) {
6408
6474
  return EXTRACT_TIMEOUT_BASE_MS + extra;
6409
6475
  }
6410
6476
  } catch (err) {
6411
- logger$o.warn("Failed to estimate extraction timeout, using base timeout:", err);
6477
+ logger$q.warn("Failed to estimate extraction timeout, using base timeout:", err);
6412
6478
  }
6413
6479
  return EXTRACT_TIMEOUT_BASE_MS;
6414
6480
  }
@@ -6767,11 +6833,21 @@ function getActiveTabInfo(tabManager) {
6767
6833
  if (wc.isDestroyed()) return null;
6768
6834
  return { tab, wc };
6769
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
+ }
6770
6843
  function sendSafe(wc, channel, ...args) {
6771
6844
  if (!wc || wc.isDestroyed()) return;
6772
6845
  try {
6773
6846
  wc.send(channel, ...args);
6774
- } 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
+ }
6775
6851
  }
6776
6852
  }
6777
6853
  function setSidebarPanelMode(state2, mode, reason = "user") {
@@ -7366,6 +7442,82 @@ function isClickReadLoop(names) {
7366
7442
  return clickReadPairs >= 2;
7367
7443
  }
7368
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
+ }
7369
7521
  const ANTHROPIC_MAX_TOKENS = 4096;
7370
7522
  function isRecord$1(value) {
7371
7523
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -7411,13 +7563,19 @@ class AnthropicProvider {
7411
7563
  {
7412
7564
  model: this.model,
7413
7565
  max_tokens: ANTHROPIC_MAX_TOKENS,
7414
- system: systemPrompt,
7566
+ system: anthropicCachedSystem(systemPrompt),
7415
7567
  messages,
7416
7568
  ...thinking ? { thinking } : {}
7417
7569
  },
7418
7570
  { signal: this.abortController.signal }
7419
7571
  );
7420
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
+ }
7421
7579
  if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
7422
7580
  onChunk(event.delta.text);
7423
7581
  }
@@ -7451,9 +7609,9 @@ class AnthropicProvider {
7451
7609
  {
7452
7610
  model: this.model,
7453
7611
  max_tokens: ANTHROPIC_MAX_TOKENS,
7454
- system: systemPrompt,
7612
+ system: anthropicCachedSystem(systemPrompt),
7455
7613
  messages,
7456
- tools,
7614
+ tools: anthropicCachedTools(tools),
7457
7615
  ...thinking ? { thinking } : {}
7458
7616
  },
7459
7617
  { signal: this.abortController.signal }
@@ -7473,6 +7631,12 @@ class AnthropicProvider {
7473
7631
  try {
7474
7632
  for await (const event of stream) {
7475
7633
  resetIdleTimer();
7634
+ if (event.type === "message_start") {
7635
+ logAnthropicPromptCacheUsage(event.message.usage, {
7636
+ model: this.model,
7637
+ mode: "agent"
7638
+ });
7639
+ }
7476
7640
  if (event.type === "content_block_start") {
7477
7641
  if (event.content_block.type === "tool_use") {
7478
7642
  currentToolUse = {
@@ -8239,7 +8403,7 @@ function recoverNarratedActionToolCalls(text, availableToolNames) {
8239
8403
  }
8240
8404
  return recovered;
8241
8405
  }
8242
- const logger$n = createLogger("OpenAIProvider");
8406
+ const logger$o = createLogger("OpenAIProvider");
8243
8407
  function shouldDebugAgentLoop() {
8244
8408
  const value = process.env.VESSEL_DEBUG_AGENT_LOOP;
8245
8409
  return value === "1" || value === "true";
@@ -8507,9 +8671,9 @@ function shouldRetryCompactToolLoop(profile, text, hasToolHistory, userMessage)
8507
8671
  function logAgentLoopDebug(payload) {
8508
8672
  if (!shouldDebugAgentLoop()) return;
8509
8673
  try {
8510
- logger$n.info(`[agent-debug] ${JSON.stringify(payload)}`);
8674
+ logger$o.info(`[agent-debug] ${JSON.stringify(payload)}`);
8511
8675
  } catch (err) {
8512
- logger$n.warn("Failed to serialize debug payload:", err);
8676
+ logger$o.warn("Failed to serialize debug payload:", err);
8513
8677
  }
8514
8678
  }
8515
8679
  function formatOpenAICompatErrorMessage(providerId, message) {
@@ -8565,11 +8729,21 @@ class OpenAICompatProvider {
8565
8729
  max_tokens: 4096,
8566
8730
  stream: true,
8567
8731
  messages,
8732
+ ...openAIPromptCacheOptions({
8733
+ providerId: this.providerId,
8734
+ model: this.model,
8735
+ mode: "chat",
8736
+ profile: this.agentToolProfile
8737
+ }),
8568
8738
  ...reasoningEffort ? { reasoning_effort: reasoningEffort } : {}
8569
8739
  },
8570
8740
  { signal: this.abortController.signal }
8571
8741
  );
8572
8742
  for await (const chunk of stream) {
8743
+ logOpenAIPromptCacheUsage(chunk.usage, {
8744
+ model: this.model,
8745
+ mode: "chat"
8746
+ });
8573
8747
  const choice = chunk.choices[0];
8574
8748
  if (!choice) continue;
8575
8749
  const delta = choice.delta;
@@ -8636,11 +8810,21 @@ class OpenAICompatProvider {
8636
8810
  tools: openAITools,
8637
8811
  tool_choice: "auto",
8638
8812
  temperature: agentTemperatureForProfile(this.agentToolProfile),
8813
+ ...openAIPromptCacheOptions({
8814
+ providerId: this.providerId,
8815
+ model: this.model,
8816
+ mode: "agent",
8817
+ profile: this.agentToolProfile
8818
+ }),
8639
8819
  ...reasoningEffort ? { reasoning_effort: reasoningEffort } : {}
8640
8820
  },
8641
8821
  { signal: this.abortController.signal }
8642
8822
  );
8643
8823
  for await (const chunk of stream) {
8824
+ logOpenAIPromptCacheUsage(chunk.usage, {
8825
+ model: this.model,
8826
+ mode: "agent"
8827
+ });
8644
8828
  const choice = chunk.choices[0];
8645
8829
  if (!choice) continue;
8646
8830
  const delta = choice.delta;
@@ -9113,7 +9297,7 @@ function createLocalPkceOAuthFlow(config) {
9113
9297
  isInProgress: () => activeFlow !== null
9114
9298
  };
9115
9299
  }
9116
- const logger$m = createLogger("CodexOAuth");
9300
+ const logger$n = createLogger("CodexOAuth");
9117
9301
  const ISSUER = "https://auth.openai.com";
9118
9302
  const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
9119
9303
  const SCOPE = "openid profile email offline_access api.connectors.read api.connectors.invoke";
@@ -9244,7 +9428,7 @@ function escapeHtml$1(text) {
9244
9428
  }
9245
9429
  const codexOAuth = createLocalPkceOAuthFlow({
9246
9430
  name: "Codex",
9247
- logger: logger$m,
9431
+ logger: logger$n,
9248
9432
  preferredPorts: [PREFERRED_PORT$1, FALLBACK_PORT$1],
9249
9433
  timeoutMs: AUTH_TIMEOUT_MS$1,
9250
9434
  callbackPath: () => "/auth/callback",
@@ -9268,7 +9452,7 @@ async function startCodexOAuth(onStatus) {
9268
9452
  function cancelCodexOAuth() {
9269
9453
  codexOAuth.cancel();
9270
9454
  }
9271
- const logger$l = createLogger("CodexProvider");
9455
+ const logger$m = createLogger("CodexProvider");
9272
9456
  const REFRESH_WINDOW_MS = 5 * 60 * 1e3;
9273
9457
  const CODEX_BACKEND_BASE_URL = "https://chatgpt.com/backend-api/codex";
9274
9458
  const CODEX_CLIENT_VERSION = "0.129.0";
@@ -9333,7 +9517,7 @@ class CodexProvider {
9333
9517
  async ensureFreshTokens() {
9334
9518
  if (Date.now() < this.tokens.expiresAt - REFRESH_WINDOW_MS) return;
9335
9519
  try {
9336
- logger$l.info("Refreshing Codex access token");
9520
+ logger$m.info("Refreshing Codex access token");
9337
9521
  const fresh = await refreshAccessToken(this.tokens);
9338
9522
  this.tokens = fresh;
9339
9523
  writeStoredCodexTokens(fresh);
@@ -9481,7 +9665,7 @@ class CodexProvider {
9481
9665
  } catch (err) {
9482
9666
  if (err.name !== "AbortError") {
9483
9667
  const msg = err instanceof Error ? err.message : String(err);
9484
- logger$l.error("Codex streamQuery error:", err);
9668
+ logger$m.error("Codex streamQuery error:", err);
9485
9669
  onChunk(`
9486
9670
 
9487
9671
  [Error: ${msg}]`);
@@ -9549,7 +9733,7 @@ class CodexProvider {
9549
9733
  } catch (err) {
9550
9734
  if (err.name !== "AbortError") {
9551
9735
  const msg = err instanceof Error ? err.message : String(err);
9552
- logger$l.error("Codex streamAgentQuery error:", err);
9736
+ logger$m.error("Codex streamAgentQuery error:", err);
9553
9737
  onChunk(`
9554
9738
 
9555
9739
  [Error: ${msg}]`);
@@ -10783,6 +10967,7 @@ function normalizeBookmarkMetadataUpdate(input) {
10783
10967
  }
10784
10968
  return normalized;
10785
10969
  }
10970
+ const logger$l = createLogger("BookmarkManager");
10786
10971
  const UNSORTED_ID = "unsorted";
10787
10972
  const ARCHIVE_FOLDER_NAME = "Archive";
10788
10973
  const NETSCAPE_BOOKMARKS_DOCTYPE = "<!DOCTYPE NETSCAPE-Bookmark-file-1>";
@@ -11354,7 +11539,8 @@ function importBookmarksFromJson(content) {
11354
11539
  save$1();
11355
11540
  emit$2();
11356
11541
  }
11357
- } catch {
11542
+ } catch (err) {
11543
+ logger$l.warn("Failed to import bookmarks from JSON:", err);
11358
11544
  errors++;
11359
11545
  }
11360
11546
  return { imported, skipped, errors };
@@ -15597,6 +15783,19 @@ Click one of these dialog actions. Do NOT click any other element.` : "";
15597
15783
  return `
15598
15784
  ${overlayHint}${actionsSuffix}${cartSummary}`;
15599
15785
  }
15786
+ async function followHrefFromClickResult(wc, beforeUrl, result, logMessage) {
15787
+ const hrefMatch = typeof result === "string" ? result.match(/\nhref: (https?:\/\/\S+)/) : null;
15788
+ if (!hrefMatch) return null;
15789
+ try {
15790
+ await loadPermittedUrl(wc, hrefMatch[1]);
15791
+ await waitForLoad(wc, 8e3);
15792
+ const hrefUrl = wc.getURL();
15793
+ if (hrefUrl !== beforeUrl) return `${result.split("\n")[0]} -> ${hrefUrl}`;
15794
+ } catch (err) {
15795
+ logger$j.warn(logMessage, err);
15796
+ }
15797
+ return null;
15798
+ }
15600
15799
  async function clickResolvedSelector(wc, selector) {
15601
15800
  if (selector.startsWith("__vessel_idx:")) {
15602
15801
  const idx = Number(selector.slice("__vessel_idx:".length));
@@ -15639,16 +15838,13 @@ Go back and select a different product.`;
15639
15838
  return `${result}${await buildCartSuccessSuffix(wc, beforeUrl2, idxOverlay)}`;
15640
15839
  }
15641
15840
  if (!idxOverlay) {
15642
- const hrefMatch = typeof result === "string" ? result.match(/\nhref: (https?:\/\/\S+)/) : null;
15643
- if (hrefMatch) {
15644
- try {
15645
- await loadPermittedUrl(wc, hrefMatch[1]);
15646
- await waitForLoad(wc, 8e3);
15647
- const hrefUrl = wc.getURL();
15648
- if (hrefUrl !== beforeUrl2) return `${result.split("\n")[0]} -> ${hrefUrl}`;
15649
- } catch {
15650
- }
15651
- }
15841
+ const hrefFallback = await followHrefFromClickResult(
15842
+ wc,
15843
+ beforeUrl2,
15844
+ result,
15845
+ "Failed to follow href fallback after click:"
15846
+ );
15847
+ if (hrefFallback) return hrefFallback;
15652
15848
  }
15653
15849
  return idxOverlay ? `${result}
15654
15850
  ${idxOverlay}` : `${result}
@@ -15706,16 +15902,13 @@ Go back and select a different product.`;
15706
15902
  return `${result}${await buildCartSuccessSuffix(wc, beforeUrl2, shadowOverlay)}`;
15707
15903
  }
15708
15904
  if (!shadowOverlay) {
15709
- const hrefMatch = typeof result === "string" ? result.match(/\nhref: (https?:\/\/\S+)/) : null;
15710
- if (hrefMatch) {
15711
- try {
15712
- await loadPermittedUrl(wc, hrefMatch[1]);
15713
- await waitForLoad(wc, 8e3);
15714
- const hrefUrl = wc.getURL();
15715
- if (hrefUrl !== beforeUrl2) return `${result.split("\n")[0]} -> ${hrefUrl}`;
15716
- } catch {
15717
- }
15718
- }
15905
+ const hrefFallback = await followHrefFromClickResult(
15906
+ wc,
15907
+ beforeUrl2,
15908
+ result,
15909
+ "Failed to follow href fallback after shadow click:"
15910
+ );
15911
+ if (hrefFallback) return hrefFallback;
15719
15912
  }
15720
15913
  return shadowOverlay ? `${result}
15721
15914
  ${shadowOverlay}` : `${result}
@@ -19828,15 +20021,14 @@ function installAdBlocking(tabManager) {
19828
20021
  callback(getRequestFilterDecision(details, false) ?? {});
19829
20022
  return;
19830
20023
  }
19831
- const manager = [...defaultSessionTabManagers].find(
19832
- (candidate) => candidate.findTabByWebContentsId(webContentsId)
19833
- );
19834
- callback(
19835
- getRequestFilterDecision(
19836
- details,
19837
- manager?.isAdBlockingEnabledForWebContents(webContentsId) ?? false
19838
- ) ?? {}
19839
- );
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) ?? {});
19840
20032
  });
19841
20033
  }
19842
20034
  function unregisterAdBlockingTabManager(tabManager) {
@@ -20499,8 +20691,8 @@ const GroupIdSchema = zod.z.string().min(1);
20499
20691
  const UrlSchema = zod.z.string().min(1);
20500
20692
  const ColorSchema = zod.z.string().min(1);
20501
20693
  const FindActionSchema = zod.z.enum(["clearSelection", "keepSelection", "activateSelection"]);
20502
- function registerTabHandlers(windowState, _sendToRendererViews) {
20503
- const { tabManager, mainWindow } = windowState;
20694
+ function registerTabHandlers(windowState2, _sendToRendererViews) {
20695
+ const { tabManager, mainWindow } = windowState2;
20504
20696
  electron.ipcMain.handle(Channels.OPEN_PRIVATE_WINDOW, (event) => {
20505
20697
  assertTrustedIpcSender(event);
20506
20698
  createPrivateWindow();
@@ -20516,20 +20708,20 @@ function registerTabHandlers(windowState, _sendToRendererViews) {
20516
20708
  electron.ipcMain.handle(Channels.TAB_CREATE, (event, url) => {
20517
20709
  assertTrustedIpcSender(event);
20518
20710
  const id = tabManager.createTab(url || loadSettings().defaultUrl);
20519
- layoutViews(windowState);
20711
+ layoutViews(windowState2);
20520
20712
  return id;
20521
20713
  });
20522
20714
  electron.ipcMain.handle(Channels.TAB_CLOSE, (event, id) => {
20523
20715
  assertTrustedIpcSender(event);
20524
20716
  const validated = parseIpc(TabIdSchema, id, "tabId");
20525
20717
  tabManager.closeTab(validated);
20526
- layoutViews(windowState);
20718
+ layoutViews(windowState2);
20527
20719
  });
20528
20720
  electron.ipcMain.handle(Channels.TAB_SWITCH, (event, id) => {
20529
20721
  assertTrustedIpcSender(event);
20530
20722
  const validated = parseIpc(TabIdSchema, id, "tabId");
20531
20723
  tabManager.switchTab(validated);
20532
- layoutViews(windowState);
20724
+ layoutViews(windowState2);
20533
20725
  });
20534
20726
  electron.ipcMain.handle(
20535
20727
  Channels.TAB_NAVIGATE,
@@ -20576,14 +20768,14 @@ function registerTabHandlers(windowState, _sendToRendererViews) {
20576
20768
  electron.ipcMain.handle(Channels.TAB_REOPEN_CLOSED, (event) => {
20577
20769
  assertTrustedIpcSender(event);
20578
20770
  const id = tabManager.reopenClosedTab();
20579
- if (id) layoutViews(windowState);
20771
+ if (id) layoutViews(windowState2);
20580
20772
  return id;
20581
20773
  });
20582
20774
  electron.ipcMain.handle(Channels.TAB_DUPLICATE, (event, id) => {
20583
20775
  assertTrustedIpcSender(event);
20584
20776
  const validated = parseIpc(TabIdSchema, id, "id");
20585
20777
  const newId = tabManager.duplicateTab(validated);
20586
- if (newId) layoutViews(windowState);
20778
+ if (newId) layoutViews(windowState2);
20587
20779
  return newId;
20588
20780
  });
20589
20781
  electron.ipcMain.handle(Channels.TAB_PIN, (event, id) => {
@@ -20637,7 +20829,7 @@ function registerTabHandlers(windowState, _sendToRendererViews) {
20637
20829
  });
20638
20830
  electron.ipcMain.on(Channels.TAB_CONTEXT_MENU, (event, id) => {
20639
20831
  assertTrustedIpcSender(event);
20640
- showTabContextMenu(tabManager, parseIpc(TabIdSchema, id, "id"), mainWindow, () => layoutViews(windowState));
20832
+ showTabContextMenu(tabManager, parseIpc(TabIdSchema, id, "id"), mainWindow, () => layoutViews(windowState2));
20641
20833
  });
20642
20834
  electron.ipcMain.on(Channels.TAB_GROUP_CONTEXT_MENU, (event, groupId) => {
20643
20835
  assertTrustedIpcSender(event);
@@ -20650,7 +20842,7 @@ function registerTabHandlers(windowState, _sendToRendererViews) {
20650
20842
  activeId: tabManager.getActiveTabId() || ""
20651
20843
  };
20652
20844
  });
20653
- const findBridge = createFindInPageBridge(tabManager, windowState.chromeView);
20845
+ const findBridge = createFindInPageBridge(tabManager, windowState2.chromeView);
20654
20846
  electron.ipcMain.handle(Channels.FIND_IN_PAGE_START, (event, text, options) => {
20655
20847
  assertTrustedIpcSender(event);
20656
20848
  return findBridge.start(text, options);
@@ -21493,8 +21685,8 @@ function renderReaderContent(page) {
21493
21685
  function escapeHtml(str) {
21494
21686
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
21495
21687
  }
21496
- function registerContentHandlers(windowState) {
21497
- const { tabManager } = windowState;
21688
+ function registerContentHandlers(windowState2) {
21689
+ const { tabManager } = windowState2;
21498
21690
  electron.ipcMain.handle(Channels.CONTENT_EXTRACT, async (event) => {
21499
21691
  assertTrustedIpcSender(event);
21500
21692
  const activeTab = tabManager.getActiveTab();
@@ -21524,14 +21716,14 @@ function registerContentHandlers(windowState) {
21524
21716
  });
21525
21717
  electron.ipcMain.handle(Channels.FOCUS_MODE_TOGGLE, (event) => {
21526
21718
  assertTrustedIpcSender(event);
21527
- windowState.uiState.focusMode = !windowState.uiState.focusMode;
21528
- layoutViews(windowState);
21529
- return windowState.uiState.focusMode;
21719
+ windowState2.uiState.focusMode = !windowState2.uiState.focusMode;
21720
+ layoutViews(windowState2);
21721
+ return windowState2.uiState.focusMode;
21530
21722
  });
21531
21723
  }
21532
21724
  const logger$f = createLogger("HighlightIPC");
21533
- function registerHighlightHandlers(windowState, sendToRendererViews) {
21534
- const { tabManager, chromeView } = windowState;
21725
+ function registerHighlightHandlers(windowState2, sendToRendererViews) {
21726
+ const { tabManager, chromeView } = windowState2;
21535
21727
  const getActiveHighlightCountSafe = async () => {
21536
21728
  const info = getActiveTabInfo(tabManager);
21537
21729
  if (!info) return 0;
@@ -26993,7 +27185,8 @@ function registerSettingsHandlers(tabManager, runtime2, sendToRendererViews, get
26993
27185
  if (key2 === "chatProvider" && researchOrchestrator) {
26994
27186
  try {
26995
27187
  researchOrchestrator.setProvider(createProvider(value));
26996
- } catch {
27188
+ } catch (err) {
27189
+ console.warn("Research provider config invalid, retaining current provider:", err);
26997
27190
  }
26998
27191
  }
26999
27192
  const rendererSettings = getRendererSettings();
@@ -27254,7 +27447,8 @@ async function togglePictureInPicture(tabManager) {
27254
27447
  }
27255
27448
  })()
27256
27449
  `);
27257
- } catch {
27450
+ } catch (err) {
27451
+ console.warn("Picture-in-picture toggle failed:", err);
27258
27452
  return false;
27259
27453
  }
27260
27454
  }
@@ -27300,7 +27494,8 @@ function getInstalledKits() {
27300
27494
  let files;
27301
27495
  try {
27302
27496
  files = fs$1.readdirSync(dir).filter((f) => f.endsWith(".kit.json"));
27303
- } catch {
27497
+ } catch (err) {
27498
+ logger$9.warn("Failed to read kit directory:", err);
27304
27499
  return [];
27305
27500
  }
27306
27501
  const kits = [];
@@ -27331,13 +27526,15 @@ async function installKitFromFile() {
27331
27526
  let raw;
27332
27527
  try {
27333
27528
  raw = fs$1.readFileSync(filePaths[0], "utf-8");
27334
- } catch {
27529
+ } catch (err) {
27530
+ logger$9.warn("Failed to read selected kit file:", err);
27335
27531
  return errorResult("Could not read the selected file.");
27336
27532
  }
27337
27533
  let parsed;
27338
27534
  try {
27339
27535
  parsed = JSON.parse(raw);
27340
- } catch {
27536
+ } catch (err) {
27537
+ logger$9.warn("Selected kit file is not valid JSON:", err);
27341
27538
  return errorResult("File is not valid JSON.");
27342
27539
  }
27343
27540
  if (!isValidKit(parsed)) {
@@ -27357,7 +27554,8 @@ async function installKitFromFile() {
27357
27554
  }
27358
27555
  try {
27359
27556
  fs$1.writeFileSync(dest, JSON.stringify(parsed, null, 2), "utf-8");
27360
- } catch {
27557
+ } catch (err) {
27558
+ logger$9.warn("Failed to save kit file:", err);
27361
27559
  return errorResult("Failed to save the kit file.");
27362
27560
  }
27363
27561
  return okResult({ kit: parsed });
@@ -27382,7 +27580,8 @@ function uninstallKit(id, scheduledKitIds) {
27382
27580
  try {
27383
27581
  fs$1.unlinkSync(target);
27384
27582
  return okResult();
27385
- } catch {
27583
+ } catch (err) {
27584
+ logger$9.warn("Failed to remove kit file:", err);
27386
27585
  return errorResult("Failed to remove the kit file.");
27387
27586
  }
27388
27587
  }
@@ -27507,8 +27706,8 @@ function isValidJobData(v) {
27507
27706
  const j = v;
27508
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";
27509
27708
  }
27510
- async function fireJob(job, windowState, runtime2) {
27511
- const { chromeView, sidebarView, devtoolsPanelView, tabManager } = windowState;
27709
+ async function fireJob(job, windowState2, runtime2) {
27710
+ const { chromeView, sidebarView, devtoolsPanelView, tabManager } = windowState2;
27512
27711
  const send = (channel, ...args) => {
27513
27712
  if (!chromeView.webContents.isDestroyed())
27514
27713
  chromeView.webContents.send(channel, ...args);
@@ -27572,7 +27771,7 @@ async function fireJob(job, windowState, runtime2) {
27572
27771
  finishActivity("failed");
27573
27772
  }
27574
27773
  }
27575
- function tick(windowState, runtime2) {
27774
+ function tick(windowState2, runtime2) {
27576
27775
  if (isAIStreamActive()) return;
27577
27776
  const dueIds = jobs.filter((job) => job.enabled && /* @__PURE__ */ new Date() >= new Date(job.nextRunAt)).map((job) => job.id);
27578
27777
  if (dueIds.length === 0) return;
@@ -27581,7 +27780,7 @@ function tick(windowState, runtime2) {
27581
27780
  const fireNext = () => {
27582
27781
  if (idx >= dueIds.length) {
27583
27782
  endAIStream("scheduled");
27584
- queueMicrotask(() => tick(windowState, runtime2));
27783
+ queueMicrotask(() => tick(windowState2, runtime2));
27585
27784
  return;
27586
27785
  }
27587
27786
  const jobId = dueIds[idx++];
@@ -27603,26 +27802,26 @@ function tick(windowState, runtime2) {
27603
27802
  }
27604
27803
  saveJobs();
27605
27804
  broadcastFn?.(Channels.SCHEDULE_JOBS_UPDATE, jobs);
27606
- void fireJob(job, windowState, runtime2).catch((err) => {
27805
+ void fireJob(job, windowState2, runtime2).catch((err) => {
27607
27806
  logger$8.warn("Unexpected error firing job:", err);
27608
27807
  }).finally(fireNext);
27609
27808
  };
27610
27809
  fireNext();
27611
27810
  }
27612
- function registerScheduleHandlers(windowState, runtime2, sendToAll) {
27811
+ function registerScheduleHandlers(windowState2, runtime2, sendToAll) {
27613
27812
  stopScheduler();
27614
27813
  broadcastFn = sendToAll;
27615
27814
  loadJobs();
27616
27815
  if (normalizeJobs()) {
27617
27816
  saveJobs();
27618
27817
  }
27619
- removeIdleListener = onAIStreamIdle(() => tick(windowState, runtime2));
27818
+ removeIdleListener = onAIStreamIdle(() => tick(windowState2, runtime2));
27620
27819
  const now = /* @__PURE__ */ new Date();
27621
27820
  const msToNextMinute = (60 - now.getSeconds()) * 1e3 - now.getMilliseconds();
27622
27821
  alignStartTimeout = setTimeout(() => {
27623
27822
  alignStartTimeout = null;
27624
- tick(windowState, runtime2);
27625
- pollInterval = setInterval(() => tick(windowState, runtime2), 6e4);
27823
+ tick(windowState2, runtime2);
27824
+ pollInterval = setInterval(() => tick(windowState2, runtime2), 6e4);
27626
27825
  }, msToNextMinute);
27627
27826
  electron.ipcMain.handle(Channels.SCHEDULE_GET_ALL, (event) => {
27628
27827
  assertTrustedIpcSender(event);
@@ -27700,19 +27899,19 @@ function stopScheduler() {
27700
27899
  }
27701
27900
  const KitIdSchema = zod.z.string().min(1);
27702
27901
  const OriginSchema = zod.z.string().min(1);
27703
- function registerSystemHandlers(windowState, sendToRendererViews) {
27704
- const { tabManager } = windowState;
27902
+ function registerSystemHandlers(windowState2, sendToRendererViews) {
27903
+ const { tabManager } = windowState2;
27705
27904
  electron.ipcMain.handle(Channels.DEVTOOLS_PANEL_TOGGLE, (event) => {
27706
27905
  assertTrustedIpcSender(event);
27707
- windowState.uiState.devtoolsPanelOpen = !windowState.uiState.devtoolsPanelOpen;
27708
- layoutViews(windowState);
27709
- return { open: windowState.uiState.devtoolsPanelOpen };
27906
+ windowState2.uiState.devtoolsPanelOpen = !windowState2.uiState.devtoolsPanelOpen;
27907
+ layoutViews(windowState2);
27908
+ return { open: windowState2.uiState.devtoolsPanelOpen };
27710
27909
  });
27711
27910
  electron.ipcMain.handle(Channels.DEVTOOLS_PANEL_RESIZE, (event, height) => {
27712
27911
  assertTrustedIpcSender(event);
27713
27912
  const clamped = Math.max(MIN_DEVTOOLS_PANEL, Math.min(MAX_DEVTOOLS_PANEL, Math.round(height)));
27714
- windowState.uiState.devtoolsPanelHeight = clamped;
27715
- layoutViews(windowState);
27913
+ windowState2.uiState.devtoolsPanelHeight = clamped;
27914
+ layoutViews(windowState2);
27716
27915
  return clamped;
27717
27916
  });
27718
27917
  electron.ipcMain.handle(Channels.AUTOMATION_GET_INSTALLED, (event) => {
@@ -28462,10 +28661,10 @@ function registerOpenRouterHandlers(applySettingChange) {
28462
28661
  return { ok: true };
28463
28662
  });
28464
28663
  }
28465
- function registerSidebarHandlers(windowState, requireTrusted) {
28664
+ function registerSidebarHandlers(windowState2, requireTrusted) {
28466
28665
  let sidebarResizeRecoveryTimer = null;
28467
28666
  let sidebarResizeActive = false;
28468
- const relayout = () => layoutViews(windowState);
28667
+ const relayout = () => layoutViews(windowState2);
28469
28668
  const clearSidebarResizeRecoveryTimer = () => {
28470
28669
  if (!sidebarResizeRecoveryTimer) return;
28471
28670
  clearTimeout(sidebarResizeRecoveryTimer);
@@ -28487,32 +28686,32 @@ function registerSidebarHandlers(windowState, requireTrusted) {
28487
28686
  restoreSidebarLayoutAfterResize();
28488
28687
  }, 1200);
28489
28688
  };
28490
- windowState.mainWindow.once("closed", stopSidebarResize);
28689
+ windowState2.mainWindow.once("closed", stopSidebarResize);
28491
28690
  electron.ipcMain.handle(Channels.SIDEBAR_TOGGLE, (event) => {
28492
28691
  requireTrusted(event);
28493
- return toggleDockedSidebar(windowState, relayout);
28692
+ return toggleDockedSidebar(windowState2, relayout);
28494
28693
  });
28495
28694
  electron.ipcMain.handle(Channels.SIDEBAR_NAVIGATE, (event, tab) => {
28496
28695
  requireTrusted(event);
28497
28696
  assertString(tab, "tab");
28498
- if (windowState.uiState.sidebarPanelMode === "closed") {
28499
- openDockedSidebar(windowState, relayout);
28697
+ if (windowState2.uiState.sidebarPanelMode === "closed") {
28698
+ openDockedSidebar(windowState2, relayout);
28500
28699
  }
28501
- if (!windowState.sidebarView.webContents.isDestroyed()) {
28502
- windowState.sidebarView.webContents.send(Channels.SIDEBAR_NAVIGATE, tab);
28700
+ if (!windowState2.sidebarView.webContents.isDestroyed()) {
28701
+ windowState2.sidebarView.webContents.send(Channels.SIDEBAR_NAVIGATE, tab);
28503
28702
  }
28504
- windowState.sidebarWindow?.focus();
28505
- return emitSidebarPanelState(windowState);
28703
+ windowState2.sidebarWindow?.focus();
28704
+ return emitSidebarPanelState(windowState2);
28506
28705
  });
28507
28706
  electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_START, (event) => {
28508
28707
  requireTrusted(event);
28509
- if (isSidebarDetached(windowState)) return;
28708
+ if (isSidebarDetached(windowState2)) return;
28510
28709
  sidebarResizeActive = true;
28511
28710
  clearSidebarResizeRecoveryTimer();
28512
- const [width, height] = windowState.mainWindow.getContentSize();
28513
- const chromeHeight = windowState.uiState.focusMode ? 0 : CHROME_HEIGHT;
28514
- const sidebarWidth = windowState.uiState.sidebarWidth;
28515
- 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({
28516
28715
  x: width - sidebarWidth - SIDEBAR_RESIZE_HANDLE_OVERLAP,
28517
28716
  y: chromeHeight,
28518
28717
  width: sidebarWidth + SIDEBAR_RESIZE_HANDLE_OVERLAP,
@@ -28523,26 +28722,26 @@ function registerSidebarHandlers(windowState, requireTrusted) {
28523
28722
  electron.ipcMain.handle(Channels.SIDEBAR_RESIZE, (event, width) => {
28524
28723
  requireTrusted(event);
28525
28724
  assertNumber(width, "width");
28526
- if (isSidebarDetached(windowState)) {
28527
- return windowState.uiState.sidebarWidth;
28725
+ if (isSidebarDetached(windowState2)) {
28726
+ return windowState2.uiState.sidebarWidth;
28528
28727
  }
28529
28728
  const clamped = clampSidebarWidth(width);
28530
- windowState.uiState.sidebarWidth = clamped;
28531
- resizeSidebarViews(windowState);
28532
- emitSidebarPanelState(windowState);
28729
+ windowState2.uiState.sidebarWidth = clamped;
28730
+ resizeSidebarViews(windowState2);
28731
+ emitSidebarPanelState(windowState2);
28533
28732
  return clamped;
28534
28733
  });
28535
28734
  electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_COMMIT, (event) => {
28536
28735
  requireTrusted(event);
28537
- if (isSidebarDetached(windowState)) return;
28736
+ if (isSidebarDetached(windowState2)) return;
28538
28737
  stopSidebarResize();
28539
- setSetting("sidebarWidth", windowState.uiState.sidebarWidth);
28738
+ setSetting("sidebarWidth", windowState2.uiState.sidebarWidth);
28540
28739
  relayout();
28541
28740
  });
28542
28741
  electron.ipcMain.handle(Channels.SIDEBAR_POPOUT, (event) => {
28543
28742
  requireTrusted(event);
28544
28743
  stopSidebarResize();
28545
- return detachSidebar(windowState, {
28744
+ return detachSidebar(windowState2, {
28546
28745
  relayout,
28547
28746
  getWindowIconPath
28548
28747
  });
@@ -28550,26 +28749,26 @@ function registerSidebarHandlers(windowState, requireTrusted) {
28550
28749
  electron.ipcMain.handle(Channels.SIDEBAR_DOCK, (event) => {
28551
28750
  requireTrusted(event);
28552
28751
  stopSidebarResize();
28553
- return dockSidebar(windowState, { relayout });
28752
+ return dockSidebar(windowState2, { relayout });
28554
28753
  });
28555
28754
  electron.ipcMain.on(
28556
28755
  Channels.RENDERER_VIEW_READY,
28557
28756
  (event, view) => {
28558
28757
  requireTrusted(event);
28559
28758
  if (view !== "sidebar") return;
28560
- emitSidebarPanelState(windowState);
28759
+ emitSidebarPanelState(windowState2);
28561
28760
  }
28562
28761
  );
28563
28762
  electron.ipcMain.handle(Channels.SETTINGS_VISIBILITY, (event, open) => {
28564
28763
  requireTrusted(event);
28565
- windowState.uiState.settingsOpen = open;
28764
+ windowState2.uiState.settingsOpen = open;
28566
28765
  if (open) {
28567
- closeSidebar(windowState, relayout, "temporary");
28766
+ closeSidebar(windowState2, relayout, "temporary");
28568
28767
  } else {
28569
28768
  relayout();
28570
- emitSidebarPanelState(windowState);
28769
+ emitSidebarPanelState(windowState2);
28571
28770
  }
28572
- return windowState.uiState.settingsOpen;
28771
+ return windowState2.uiState.settingsOpen;
28573
28772
  });
28574
28773
  }
28575
28774
  function assertVaultUnlocked() {
@@ -29109,7 +29308,7 @@ function sanitizeAutofillUpdates(value) {
29109
29308
  }
29110
29309
  return updates;
29111
29310
  }
29112
- function registerAutofillHandlers(windowState) {
29311
+ function registerAutofillHandlers(windowState2) {
29113
29312
  electron.ipcMain.handle(Channels.AUTOFILL_LIST, (event) => {
29114
29313
  assertTrustedIpcSender(event);
29115
29314
  return listProfiles();
@@ -29136,7 +29335,7 @@ function registerAutofillHandlers(windowState) {
29136
29335
  assertString(profileId, "profileId");
29137
29336
  const profile = getProfile(profileId);
29138
29337
  if (!profile) throw new Error("Profile not found");
29139
- const activeTab = windowState.tabManager.getActiveTab();
29338
+ const activeTab = windowState2.tabManager.getActiveTab();
29140
29339
  const wc = activeTab?.view.webContents;
29141
29340
  if (!wc) throw new Error("No active tab");
29142
29341
  const content = await extractContent$1(wc);
@@ -29166,9 +29365,9 @@ function registerAutofillHandlers(windowState) {
29166
29365
  };
29167
29366
  });
29168
29367
  }
29169
- function registerPageDiffHandlers(windowState, sendToRendererViews) {
29368
+ function registerPageDiffHandlers(windowState2, sendToRendererViews) {
29170
29369
  const pageEventBuckets = /* @__PURE__ */ new Map();
29171
- const isActiveWebContents = (webContentsId) => windowState.tabManager.getActiveTab()?.view.webContents.id === webContentsId;
29370
+ const isActiveWebContents = (webContentsId) => windowState2.tabManager.getActiveTab()?.view.webContents.id === webContentsId;
29172
29371
  const allowPageEvent = (webContentsId) => {
29173
29372
  const now = Date.now();
29174
29373
  const bucket = pageEventBuckets.get(webContentsId);
@@ -29181,7 +29380,7 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
29181
29380
  };
29182
29381
  electron.ipcMain.handle(Channels.PAGE_DIFF_GET, (event) => {
29183
29382
  assertTrustedIpcSender(event);
29184
- const activeTab = windowState.tabManager.getActiveTab();
29383
+ const activeTab = windowState2.tabManager.getActiveTab();
29185
29384
  const wc = activeTab?.view.webContents;
29186
29385
  if (!wc) return null;
29187
29386
  return getLatestPageDiff(wc.getURL());
@@ -29192,7 +29391,7 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
29192
29391
  if (!isPremiumActiveState(getPremiumState())) {
29193
29392
  return { error: "Premium required" };
29194
29393
  }
29195
- const activeTab = windowState.tabManager.getActiveTab();
29394
+ const activeTab = windowState2.tabManager.getActiveTab();
29196
29395
  const wc = activeTab?.view.webContents;
29197
29396
  if (!wc) return [];
29198
29397
  return getPageDiffBursts(wc.getURL());
@@ -29203,7 +29402,7 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
29203
29402
  electron.ipcMain.on(Channels.PAGE_DIFF_ACTIVITY, (event) => {
29204
29403
  const wc = event.sender;
29205
29404
  if (!wc || wc.isDestroyed()) return;
29206
- if (!isManagedTabIpcSender(event, windowState.tabManager)) return;
29405
+ if (!isManagedTabIpcSender(event, windowState2.tabManager)) return;
29207
29406
  if (!allowPageEvent(wc.id)) return;
29208
29407
  invalidateExtractionCache(wc);
29209
29408
  notePageMutationActivity(wc, sendToRendererViews, {
@@ -29213,7 +29412,7 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
29213
29412
  electron.ipcMain.on(Channels.PAGE_DIFF_DIRTY, (event) => {
29214
29413
  const wc = event.sender;
29215
29414
  if (!wc || wc.isDestroyed()) return;
29216
- if (!isManagedTabIpcSender(event, windowState.tabManager)) return;
29415
+ if (!isManagedTabIpcSender(event, windowState2.tabManager)) return;
29217
29416
  if (!allowPageEvent(wc.id)) return;
29218
29417
  invalidateExtractionCache(wc);
29219
29418
  schedulePageSnapshotCapture(wc, sendToRendererViews, 0, {
@@ -29395,16 +29594,16 @@ function registerResearchHandlers(getOrchestrator) {
29395
29594
  }
29396
29595
  });
29397
29596
  }
29398
- function registerIpcHandlers(windowState, runtime2) {
29399
- const { tabManager, chromeView, sidebarView, devtoolsPanelView, mainWindow } = windowState;
29597
+ function registerIpcHandlers(windowState2, runtime2) {
29598
+ const { tabManager, chromeView, sidebarView, devtoolsPanelView, mainWindow } = windowState2;
29400
29599
  registerTrustedIpcSender(chromeView.webContents);
29401
29600
  registerTrustedIpcSender(sidebarView.webContents);
29402
29601
  registerTrustedIpcSender(devtoolsPanelView.webContents);
29403
- const sendToRendererViews = (channel, ...args) => {
29404
- sendSafe(chromeView.webContents, channel, ...args);
29405
- sendSafe(sidebarView.webContents, channel, ...args);
29406
- sendSafe(devtoolsPanelView.webContents, channel, ...args);
29407
- };
29602
+ const sendToRendererViews = createWindowStateMessenger(
29603
+ chromeView,
29604
+ sidebarView,
29605
+ devtoolsPanelView
29606
+ );
29408
29607
  let researchOrchestrator = null;
29409
29608
  const getResearchOrchestrator = () => {
29410
29609
  if (!researchOrchestrator) {
@@ -29418,10 +29617,10 @@ function registerIpcHandlers(windowState, runtime2) {
29418
29617
  return researchOrchestrator;
29419
29618
  };
29420
29619
  const getExistingResearchOrchestrator = () => researchOrchestrator;
29421
- registerTabHandlers(windowState);
29620
+ registerTabHandlers(windowState2);
29422
29621
  registerAIHandlers(tabManager, runtime2, sendToRendererViews, getResearchOrchestrator);
29423
- registerContentHandlers(windowState);
29424
- registerHighlightHandlers(windowState, sendToRendererViews);
29622
+ registerContentHandlers(windowState2);
29623
+ registerHighlightHandlers(windowState2, sendToRendererViews);
29425
29624
  registerAgentRuntimeHandlers(
29426
29625
  runtime2,
29427
29626
  chromeView.webContents,
@@ -29441,15 +29640,15 @@ function registerIpcHandlers(windowState, runtime2) {
29441
29640
  registerSecurityHandlers(tabManager);
29442
29641
  registerCodexHandlers();
29443
29642
  registerOpenRouterHandlers(applySettingChange);
29444
- registerSidebarHandlers(windowState, (event) => assertTrustedIpcSender(event));
29643
+ registerSidebarHandlers(windowState2, (event) => assertTrustedIpcSender(event));
29445
29644
  registerVaultHandlers();
29446
29645
  registerHumanVaultHandlers();
29447
29646
  registerWindowControlHandlers(mainWindow);
29448
- registerAutofillHandlers(windowState);
29449
- registerPageDiffHandlers(windowState, sendToRendererViews);
29647
+ registerAutofillHandlers(windowState2);
29648
+ registerPageDiffHandlers(windowState2, sendToRendererViews);
29450
29649
  registerResearchHandlers(() => getResearchOrchestrator());
29451
- registerScheduleHandlers(windowState, runtime2, sendToRendererViews);
29452
- registerSystemHandlers(windowState, sendToRendererViews);
29650
+ registerScheduleHandlers(windowState2, runtime2, sendToRendererViews);
29651
+ registerSystemHandlers(windowState2, sendToRendererViews);
29453
29652
  }
29454
29653
  const UNDOABLE_ACTIONS = /* @__PURE__ */ new Set([
29455
29654
  "accept_cookies",
@@ -29890,19 +30089,39 @@ class AgentRuntime {
29890
30089
  constructor(tabManager) {
29891
30090
  this.tabManager = tabManager;
29892
30091
  this.state = this.loadPersistedState();
29893
- onMcpStatusChange(() => this.emit());
30092
+ this.mcpUnsubscribe = onMcpStatusChange(() => this.emit());
29894
30093
  }
29895
30094
  tabManager;
29896
30095
  state;
29897
30096
  updateListener = null;
29898
30097
  pendingResolvers = /* @__PURE__ */ new Map();
29899
30098
  undoSnapshots = [];
30099
+ mcpUnsubscribe = null;
29900
30100
  setUpdateListener(listener) {
29901
30101
  this.updateListener = listener;
29902
30102
  if (listener) {
29903
30103
  listener(this.getState());
29904
30104
  }
29905
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
+ }
29906
30125
  getState() {
29907
30126
  const snapshot2 = clone(this.state);
29908
30127
  snapshot2.mcpStatus = getMcpStatus();
@@ -29956,6 +30175,7 @@ class AgentRuntime {
29956
30175
  -20
29957
30176
  );
29958
30177
  this.emit();
30178
+ void this.flushPersist();
29959
30179
  return clone(checkpoint);
29960
30180
  }
29961
30181
  restoreCheckpoint(checkpointId) {
@@ -30747,7 +30967,7 @@ Action: ${issue.action}` : "";
30747
30967
  return `${issue.title}
30748
30968
  ${issue.detail}${action}`;
30749
30969
  }
30750
- async function maybeShowStartupHealthDialog(windowState) {
30970
+ async function maybeShowStartupHealthDialog(windowState2) {
30751
30971
  const health = getRuntimeHealth();
30752
30972
  const hasIssues = health.startupIssues.length > 0 || health.mcp.status === "error";
30753
30973
  if (!hasIssues) return;
@@ -30759,7 +30979,7 @@ ${health.mcp.message}
30759
30979
  Action: Open Settings (Ctrl+,) to choose a different port, then save to restart the MCP server.`
30760
30980
  );
30761
30981
  }
30762
- await electron.dialog.showMessageBox(windowState.mainWindow, {
30982
+ await electron.dialog.showMessageBox(windowState2.mainWindow, {
30763
30983
  type: health.startupIssues.some((issue) => issue.severity === "error") || health.mcp.status === "error" ? "warning" : "info",
30764
30984
  title: "Vessel Startup Checks",
30765
30985
  message: "Vessel launched with runtime warnings.",
@@ -30795,10 +31015,10 @@ async function bootstrap() {
30795
31015
  sendSafe(state2.sidebarView.webContents, Channels.HIGHLIGHT_COUNT_UPDATE, count);
30796
31016
  sendSafe(state2.devtoolsPanelView.webContents, Channels.HIGHLIGHT_COUNT_UPDATE, count);
30797
31017
  };
30798
- const windowState = createMainWindow((tabs, activeId, meta) => {
30799
- sendSafe(windowState.chromeView.webContents, Channels.TAB_STATE_UPDATE, tabs, activeId);
30800
- void syncActiveHighlightCount(windowState);
30801
- 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);
30802
31022
  if (meta.persistSession) {
30803
31023
  runtime?.onTabStateChanged();
30804
31024
  }
@@ -30807,7 +31027,7 @@ async function bootstrap() {
30807
31027
  const revealMainWindow = () => {
30808
31028
  if (didRevealMainWindow) return;
30809
31029
  didRevealMainWindow = true;
30810
- windowState.mainWindow.show();
31030
+ windowState2.mainWindow.show();
30811
31031
  closeSplash(splash, 0);
30812
31032
  };
30813
31033
  let didInitializeChromeRenderer = false;
@@ -30815,25 +31035,25 @@ async function bootstrap() {
30815
31035
  logger.warn("Renderer did not finish loading before splash timeout");
30816
31036
  revealMainWindow();
30817
31037
  }, 8e3);
30818
- const { chromeView, sidebarView, devtoolsPanelView, tabManager } = windowState;
31038
+ const { chromeView, sidebarView, devtoolsPanelView, tabManager } = windowState2;
30819
31039
  runtime = new AgentRuntime(tabManager);
30820
31040
  installAdBlocking(tabManager);
30821
31041
  setDevToolsPanelListener((state2) => {
30822
31042
  sendSafe(devtoolsPanelView.webContents, Channels.DEVTOOLS_PANEL_STATE, state2);
30823
31043
  });
30824
- registerIpcHandlers(windowState, runtime);
31044
+ registerIpcHandlers(windowState2, runtime);
30825
31045
  tabManager.onSecurityStateChange((tabId, state2) => {
30826
31046
  sendSafe(chromeView.webContents, Channels.SECURITY_STATE_UPDATE, { tabId, state: state2 });
30827
31047
  sendSafe(sidebarView.webContents, Channels.SECURITY_STATE_UPDATE, { tabId, state: state2 });
30828
31048
  });
30829
- registerHighlightShortcut(windowState.mainWindow, tabManager);
31049
+ registerHighlightShortcut(windowState2.mainWindow, tabManager);
30830
31050
  setupAppMenu({
30831
31051
  newWindow: () => {
30832
31052
  createSecondaryWindow();
30833
31053
  },
30834
31054
  reopenClosedTab: () => {
30835
31055
  const id = tabManager.reopenClosedTab();
30836
- if (id) layoutViews(windowState);
31056
+ if (id) layoutViews(windowState2);
30837
31057
  },
30838
31058
  zoomIn: () => {
30839
31059
  const id = tabManager.getActiveTabId();
@@ -30886,11 +31106,11 @@ async function bootstrap() {
30886
31106
  tabManager.createTab(settings2.defaultUrl);
30887
31107
  runtime.captureSession("Initial session");
30888
31108
  }
30889
- layoutViews(windowState);
30890
- setImmediate(() => layoutViews(windowState));
31109
+ layoutViews(windowState2);
31110
+ setImmediate(() => layoutViews(windowState2));
30891
31111
  clearTimeout(splashTimeout);
30892
31112
  revealMainWindow();
30893
- void maybeShowStartupHealthDialog(windowState);
31113
+ void maybeShowStartupHealthDialog(windowState2);
30894
31114
  };
30895
31115
  chromeView.webContents.once("dom-ready", () => {
30896
31116
  initializeChromeRenderer();
@@ -30935,6 +31155,8 @@ electron.app.on("window-all-closed", () => {
30935
31155
  electron.globalShortcut.unregisterAll();
30936
31156
  stopTelemetry();
30937
31157
  stopBackgroundRevalidation();
31158
+ runtime?.dispose();
31159
+ windowState?.tabManager.dispose();
30938
31160
  void Promise.all([
30939
31161
  runtime?.flushPersist() ?? Promise.resolve(),
30940
31162
  flushPersist$1(),