@quanta-intellect/vessel-browser 0.1.130 → 0.1.133

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$x = 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$x.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$x.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$x.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$x.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$x.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$x.warn("Could not delete Codex token file:", err);
225
225
  }
226
226
  }
227
227
  }
@@ -351,7 +351,9 @@ 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((err) => {
355
+ logger$x.warn("Failed to chmod settings file:", err);
356
+ })).catch((err) => logger$x.error("Failed to save settings:", err));
355
357
  }
356
358
  function saveSettings() {
357
359
  saveDirty = true;
@@ -530,7 +532,7 @@ const urlSafety = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
530
532
  }, Symbol.toStringTag, { value: "Module" }));
531
533
  const MAX_CUSTOM_HISTORY = 50;
532
534
  const READER_MODE_DATA_URL_PREFIX = "data:text/html;charset=utf-8,";
533
- const logger$t = createLogger("Tab");
535
+ const logger$w = createLogger("Tab");
534
536
  const sessionCertExceptions = /* @__PURE__ */ new WeakMap();
535
537
  const sessionsWithVerifyProc = /* @__PURE__ */ new WeakSet();
536
538
  const CERT_VERIFY_TRUST = 0;
@@ -596,7 +598,7 @@ class Tab {
596
598
  guardedLoadURL(url, options) {
597
599
  const blockReason = this.getNavigationBlockReason(url);
598
600
  if (blockReason) {
599
- logger$t.warn(blockReason);
601
+ logger$w.warn(blockReason);
600
602
  return blockReason;
601
603
  }
602
604
  void this.view.webContents.loadURL(url, options);
@@ -680,7 +682,7 @@ class Tab {
680
682
  wc.setWindowOpenHandler(({ url, disposition }) => {
681
683
  const error = this.getNavigationBlockReason(url);
682
684
  if (error) {
683
- logger$t.warn(error);
685
+ logger$w.warn(error);
684
686
  return { action: "deny" };
685
687
  }
686
688
  this.onOpenUrl?.({
@@ -694,7 +696,7 @@ class Tab {
694
696
  const error = this.getNavigationBlockReason(url);
695
697
  if (!error) return;
696
698
  event.preventDefault();
697
- logger$t.warn(`${context}: ${error}`);
699
+ logger$w.warn(`${context}: ${error}`);
698
700
  };
699
701
  wc.on("will-navigate", (event, url) => {
700
702
  blockNavigation(event, url, "Blocked top-level navigation");
@@ -778,7 +780,7 @@ class Tab {
778
780
  ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 999px; }
779
781
  ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.22); }
780
782
  ::-webkit-scrollbar-corner { background: transparent; }
781
- `).catch((err) => logger$t.warn("Failed to inject scrollbar CSS:", err));
783
+ `).catch((err) => logger$w.warn("Failed to inject scrollbar CSS:", err));
782
784
  });
783
785
  wc.on("page-favicon-updated", (_, favicons) => {
784
786
  this._state.favicon = favicons[0] || "";
@@ -814,7 +816,7 @@ class Tab {
814
816
  ).then((highlightedText) => {
815
817
  this.buildContextMenu(wc, params, highlightedText.trim());
816
818
  }).catch((err) => {
817
- logger$t.warn("Failed to inspect highlighted text for context menu:", err);
819
+ logger$w.warn("Failed to inspect highlighted text for context menu:", err);
818
820
  this.buildContextMenu(wc, params, "");
819
821
  });
820
822
  });
@@ -1015,7 +1017,7 @@ class Tab {
1015
1017
  "document.documentElement.outerHTML"
1016
1018
  );
1017
1019
  } catch (err) {
1018
- logger$t.warn("Failed to retrieve page source:", err);
1020
+ logger$w.warn("Failed to retrieve page source:", err);
1019
1021
  return;
1020
1022
  }
1021
1023
  const escaped = html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -1135,7 +1137,9 @@ class Tab {
1135
1137
  range.insertNode(mark);
1136
1138
  }
1137
1139
  }
1138
- } catch(e) {}
1140
+ } catch {
1141
+ // Swallow — complex cross-node selections may fail to wrap
1142
+ }
1139
1143
  sel.removeAllRanges();
1140
1144
  // Notify main process for persistence
1141
1145
  window.__vessel.notifyHighlightSelection(text);
@@ -1143,7 +1147,7 @@ class Tab {
1143
1147
  document.addEventListener('mouseup', window.__vesselHighlightHandler);
1144
1148
  }
1145
1149
  })()
1146
- `).catch((err) => logger$t.warn("Failed to inject highlight listener:", err));
1150
+ `).catch((err) => logger$w.warn("Failed to inject highlight listener:", err));
1147
1151
  } else {
1148
1152
  void wc.executeJavaScript(`
1149
1153
  (function() {
@@ -1154,13 +1158,13 @@ class Tab {
1154
1158
  delete window.__vesselHighlightHandler;
1155
1159
  }
1156
1160
  })()
1157
- `).catch((err) => logger$t.warn("Failed to remove highlight listener:", err));
1161
+ `).catch((err) => logger$w.warn("Failed to remove highlight listener:", err));
1158
1162
  }
1159
1163
  }
1160
1164
  get webContentsId() {
1161
1165
  return this.view.webContents.id;
1162
1166
  }
1163
- destroy() {
1167
+ dispose() {
1164
1168
  this.setHighlightMode(false);
1165
1169
  this.view.webContents.close();
1166
1170
  }
@@ -1191,7 +1195,7 @@ const SEARCH_ENGINE_PRESETS = {
1191
1195
  ecosia: { label: "Ecosia", url: "https://www.ecosia.org/search?q=" },
1192
1196
  kagi: { label: "Kagi", url: "https://kagi.com/search?q=" }
1193
1197
  };
1194
- const logger$s = createLogger("JsonPersistence");
1198
+ const logger$v = createLogger("JsonPersistence");
1195
1199
  function canUseSafeStorage() {
1196
1200
  try {
1197
1201
  return electron.safeStorage.isEncryptionAvailable();
@@ -1256,7 +1260,9 @@ function createDebouncedJsonPersistence({
1256
1260
  data,
1257
1261
  typeof data === "string" ? { encoding: "utf-8", mode: 384 } : { mode: 384 }
1258
1262
  )
1259
- ).then(() => fs.promises.chmod(filePath2, 384).catch(() => void 0)).catch((err) => logger$s.error(`Failed to save ${logLabel}:`, err));
1263
+ ).then(() => fs.promises.chmod(filePath2, 384).catch((err) => {
1264
+ logger$v.warn(`Failed to chmod ${logLabel}:`, err);
1265
+ })).catch((err) => logger$v.error(`Failed to save ${logLabel}:`, err));
1260
1266
  };
1261
1267
  const schedule = () => {
1262
1268
  saveDirty2 = true;
@@ -2948,7 +2954,7 @@ function destroySession(tabId) {
2948
2954
  sessions.delete(tabId);
2949
2955
  }
2950
2956
  }
2951
- const logger$r = createLogger("TabManager");
2957
+ const logger$u = createLogger("TabManager");
2952
2958
  function sanitizeFilename(title, ext) {
2953
2959
  const clean = title.replace(/[<>:"/\\|?*\x00-\x1f]/g, " ").replace(/\s+/g, " ").trim();
2954
2960
  const escapedExt = ext.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -3048,7 +3054,7 @@ class TabManager {
3048
3054
  }
3049
3055
  destroySession(id);
3050
3056
  this.window.contentView.removeChildView(tab.view);
3051
- tab.destroy();
3057
+ tab.dispose();
3052
3058
  this.tabs.delete(id);
3053
3059
  this.order = this.order.filter((tid) => tid !== id);
3054
3060
  this.removeGroupIfEmpty(groupId);
@@ -3318,7 +3324,7 @@ class TabManager {
3318
3324
  if (!tab) continue;
3319
3325
  destroySession(id);
3320
3326
  this.window.contentView.removeChildView(tab.view);
3321
- tab.destroy();
3327
+ tab.dispose();
3322
3328
  }
3323
3329
  this.tabs.clear();
3324
3330
  this.order = [];
@@ -3327,6 +3333,30 @@ class TabManager {
3327
3333
  this.broadcastState({ persistSession: true });
3328
3334
  }
3329
3335
  lastReapply = /* @__PURE__ */ new Map();
3336
+ /**
3337
+ * Clean up all tabs, event listeners, and internal state.
3338
+ * Call when the window is closing or when the TabManager is being replaced
3339
+ * (e.g. secondary / private windows).
3340
+ */
3341
+ dispose() {
3342
+ for (const id of [...this.order]) {
3343
+ const tab = this.tabs.get(id);
3344
+ if (!tab) continue;
3345
+ destroySession(id);
3346
+ this.window.contentView.removeChildView(tab.view);
3347
+ tab.dispose();
3348
+ }
3349
+ this.tabs.clear();
3350
+ this.order = [];
3351
+ this.tabGroups.clear();
3352
+ this.activeTabId = null;
3353
+ this.closedTabs = [];
3354
+ this.lastReapply.clear();
3355
+ this.highlightCaptureCallback = null;
3356
+ this.pageLoadCallback = null;
3357
+ this.securityStateCallback = null;
3358
+ this.lastSessionSignature = "";
3359
+ }
3330
3360
  reapplyHighlights(url, wc) {
3331
3361
  const wcId = wc.id;
3332
3362
  const now = Date.now();
@@ -3343,7 +3373,7 @@ class TabManager {
3343
3373
  }));
3344
3374
  if (entries.length > 0) {
3345
3375
  void highlightBatchOnPage(wc, entries).catch(
3346
- (err) => logger$r.warn("Failed to batch highlight:", err)
3376
+ (err) => logger$u.warn("Failed to batch highlight:", err)
3347
3377
  );
3348
3378
  }
3349
3379
  }
@@ -3365,12 +3395,12 @@ class TabManager {
3365
3395
  const result = await captureSelectionHighlight(wc);
3366
3396
  if (result.success && result.text) {
3367
3397
  await highlightOnPage(wc, null, result.text, void 0, void 0, "yellow").catch(
3368
- (err) => logger$r.warn("Failed to capture highlight:", err)
3398
+ (err) => logger$u.warn("Failed to capture highlight:", err)
3369
3399
  );
3370
3400
  }
3371
3401
  this.highlightCaptureCallback?.(result);
3372
3402
  } catch (err) {
3373
- logger$r.warn("Failed to capture highlight from page:", err);
3403
+ logger$u.warn("Failed to capture highlight from page:", err);
3374
3404
  this.highlightCaptureCallback?.({
3375
3405
  success: false,
3376
3406
  message: "Could not capture selection"
@@ -3395,7 +3425,7 @@ class TabManager {
3395
3425
  void this.removeHighlightMarksForText(wc, text);
3396
3426
  }
3397
3427
  } catch (err) {
3398
- logger$r.warn("Failed to remove highlight from matching tab:", err);
3428
+ logger$u.warn("Failed to remove highlight from matching tab:", err);
3399
3429
  }
3400
3430
  }
3401
3431
  this.highlightCaptureCallback?.({
@@ -3426,12 +3456,12 @@ class TabManager {
3426
3456
  void 0,
3427
3457
  color
3428
3458
  ).catch(
3429
- (err) => logger$r.warn("Failed to update highlight color:", err)
3459
+ (err) => logger$u.warn("Failed to update highlight color:", err)
3430
3460
  );
3431
3461
  });
3432
3462
  }
3433
3463
  } catch (err) {
3434
- logger$r.warn("Failed to iterate highlights for color change:", err);
3464
+ logger$u.warn("Failed to iterate highlights for color change:", err);
3435
3465
  }
3436
3466
  }
3437
3467
  this.highlightCaptureCallback?.({
@@ -3486,7 +3516,7 @@ class TabManager {
3486
3516
  });
3487
3517
  })()`
3488
3518
  ).catch(
3489
- (err) => logger$r.warn("Failed to remove highlight marks:", err)
3519
+ (err) => logger$u.warn("Failed to remove highlight marks:", err)
3490
3520
  );
3491
3521
  }
3492
3522
  broadcastState(meta = { persistSession: false }) {
@@ -3501,28 +3531,13 @@ class TabManager {
3501
3531
  }
3502
3532
  }
3503
3533
  }
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
3534
+ const AIChannels = {
3517
3535
  AI_QUERY: "ai:query",
3518
3536
  AI_STREAM_START: "ai:stream-start",
3519
3537
  AI_STREAM_CHUNK: "ai:stream-chunk",
3520
3538
  AI_STREAM_END: "ai:stream-end",
3521
3539
  AI_STREAM_IDLE: "ai:stream-idle",
3522
3540
  AI_RESEARCH_CLARIFICATION: "ai:research-clarification",
3523
- AUTOMATION_ACTIVITY_START: "automation:activity-start",
3524
- AUTOMATION_ACTIVITY_CHUNK: "automation:activity-chunk",
3525
- AUTOMATION_ACTIVITY_END: "automation:activity-end",
3526
3541
  AI_CANCEL: "ai:cancel",
3527
3542
  AI_FETCH_MODELS: "ai:fetch-models",
3528
3543
  AGENT_RUNTIME_GET: "agent-runtime:get",
@@ -3536,32 +3551,29 @@ const Channels = {
3536
3551
  AGENT_CHECKPOINT_UPDATE_NOTE: "agent:checkpoint-update-note",
3537
3552
  AGENT_UNDO_LAST_ACTION: "agent:undo-last-action",
3538
3553
  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
3554
+ AGENT_SESSION_RESTORE: "agent:session-restore"
3555
+ };
3556
+ const AutofillChannels = {
3557
+ AUTOFILL_LIST: "autofill:list",
3558
+ AUTOFILL_ADD: "autofill:add",
3559
+ AUTOFILL_UPDATE: "autofill:update",
3560
+ AUTOFILL_DELETE: "autofill:delete",
3561
+ AUTOFILL_FILL: "autofill:fill"
3562
+ };
3563
+ const AutomationChannels = {
3564
+ AUTOMATION_GET_INSTALLED: "automation:get-installed",
3565
+ AUTOMATION_INSTALL_FROM_FILE: "automation:install-from-file",
3566
+ AUTOMATION_UNINSTALL: "automation:uninstall",
3567
+ AUTOMATION_ACTIVITY_START: "automation:activity-start",
3568
+ AUTOMATION_ACTIVITY_CHUNK: "automation:activity-chunk",
3569
+ AUTOMATION_ACTIVITY_END: "automation:activity-end",
3570
+ SCHEDULE_GET_ALL: "schedule:get-all",
3571
+ SCHEDULE_CREATE: "schedule:create",
3572
+ SCHEDULE_UPDATE: "schedule:update",
3573
+ SCHEDULE_DELETE: "schedule:delete",
3574
+ SCHEDULE_JOBS_UPDATE: "schedule:jobs-update"
3575
+ };
3576
+ const BookmarkChannels = {
3565
3577
  BOOKMARKS_GET: "bookmarks:get",
3566
3578
  BOOKMARKS_UPDATE: "bookmarks:update",
3567
3579
  BOOKMARK_SAVE: "bookmarks:save",
@@ -3575,8 +3587,43 @@ const Channels = {
3575
3587
  FOLDER_CREATE: "bookmarks:folder-create",
3576
3588
  FOLDER_REMOVE: "bookmarks:folder-remove",
3577
3589
  FOLDER_RENAME: "bookmarks:folder-rename",
3578
- FOLDER_EXPORT_HTML: "bookmarks:folder-export-html",
3579
- // Highlights
3590
+ FOLDER_EXPORT_HTML: "bookmarks:folder-export-html"
3591
+ };
3592
+ const BrowsingDataChannels = {
3593
+ CLEAR_BROWSING_DATA: "browsing-data:clear",
3594
+ CLEAR_BROWSING_DATA_OPEN: "browsing-data:open"
3595
+ };
3596
+ const CodexChannels = {
3597
+ CODEX_START_AUTH: "codex:start-auth",
3598
+ CODEX_CANCEL_AUTH: "codex:cancel-auth",
3599
+ CODEX_AUTH_STATUS: "codex:auth-status",
3600
+ CODEX_DISCONNECT: "codex:disconnect"
3601
+ };
3602
+ const ContentChannels = {
3603
+ CONTENT_EXTRACT: "content:extract",
3604
+ READER_MODE_TOGGLE: "reader:toggle",
3605
+ PAGE_DIFF_ACTIVITY: "page:diff-activity",
3606
+ PAGE_CHANGED: "page:changed",
3607
+ PAGE_DIFF_GET: "page:diff-get",
3608
+ PAGE_DIFF_HISTORY: "page:diff-history",
3609
+ PAGE_DIFF_DIRTY: "page:diff-dirty"
3610
+ };
3611
+ const DevToolsChannels = {
3612
+ DEVTOOLS_PANEL_TOGGLE: "devtools-panel:toggle",
3613
+ DEVTOOLS_PANEL_STATE: "devtools-panel:state",
3614
+ DEVTOOLS_PANEL_RESIZE: "devtools-panel:resize"
3615
+ };
3616
+ const DownloadChannels = {
3617
+ DOWNLOAD_STARTED: "download:started",
3618
+ DOWNLOAD_PROGRESS: "download:progress",
3619
+ DOWNLOAD_DONE: "download:done",
3620
+ DOWNLOADS_GET: "downloads:get",
3621
+ DOWNLOADS_CLEAR: "downloads:clear",
3622
+ DOWNLOADS_OPEN: "downloads:open",
3623
+ DOWNLOADS_SHOW_IN_FOLDER: "downloads:show-in-folder",
3624
+ DOWNLOADS_UPDATE: "downloads:update"
3625
+ };
3626
+ const HighlightChannels = {
3580
3627
  HIGHLIGHT_CAPTURE: "highlights:capture",
3581
3628
  HIGHLIGHT_CAPTURE_RESULT: "highlights:capture-result",
3582
3629
  HIGHLIGHT_SELECTION: "vessel:highlight-selection",
@@ -3585,154 +3632,177 @@ const Channels = {
3585
3632
  HIGHLIGHT_NAV_SCROLL: "highlights:nav-scroll",
3586
3633
  HIGHLIGHT_NAV_REMOVE: "highlights:nav-remove",
3587
3634
  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
3635
+ SIDEBAR_HIGHLIGHT_ACTION: "highlights:sidebar-action"
3636
+ };
3637
+ const HistoryChannels = {
3638
+ HISTORY_GET: "history:get",
3639
+ HISTORY_LIST: "history:list",
3640
+ HISTORY_SEARCH: "history:search",
3641
+ HISTORY_CLEAR: "history:clear",
3642
+ HISTORY_UPDATE: "history:update",
3643
+ HISTORY_EXPORT_HTML: "history:export-html",
3644
+ HISTORY_EXPORT_JSON: "history:export-json",
3645
+ HISTORY_IMPORT: "history:import"
3646
+ };
3647
+ const HumanVaultChannels = {
3648
+ HUMAN_VAULT_LIST: "human-vault:list",
3649
+ HUMAN_VAULT_GET: "human-vault:get",
3650
+ HUMAN_VAULT_SAVE: "human-vault:save",
3651
+ HUMAN_VAULT_UPDATE: "human-vault:update",
3652
+ HUMAN_VAULT_REMOVE: "human-vault:remove",
3653
+ HUMAN_VAULT_AUDIT_LOG: "human-vault:audit-log"
3654
+ };
3655
+ const McpChannels = {
3656
+ MCP_REGENERATE_TOKEN: "mcp:regenerate-token"
3657
+ };
3658
+ const OpenRouterChannels = {
3659
+ OPENROUTER_START_AUTH: "openrouter:start-auth",
3660
+ OPENROUTER_CANCEL_AUTH: "openrouter:cancel-auth",
3661
+ OPENROUTER_AUTH_STATUS: "openrouter:auth-status"
3662
+ };
3663
+ const PermissionChannels = {
3664
+ PERMISSIONS_GET: "permissions:get",
3665
+ PERMISSIONS_CLEAR: "permissions:clear",
3666
+ PERMISSIONS_CLEAR_ORIGIN: "permissions:clear-origin"
3667
+ };
3668
+ const PremiumChannels = {
3669
+ PREMIUM_GET_STATE: "premium:get-state",
3670
+ PREMIUM_ACTIVATION_START: "premium:activation-start",
3671
+ PREMIUM_ACTIVATION_VERIFY: "premium:activation-verify",
3672
+ PREMIUM_CHECKOUT: "premium:checkout",
3673
+ PREMIUM_PORTAL: "premium:portal",
3674
+ PREMIUM_RESET: "premium:reset",
3675
+ PREMIUM_TRACK_CONTEXT: "premium:track-context",
3676
+ PREMIUM_UPDATE: "premium:update"
3677
+ };
3678
+ const ResearchChannels = {
3679
+ RESEARCH_STATE_GET: "research:state-get",
3680
+ RESEARCH_STATE_UPDATE: "research:state-update",
3681
+ RESEARCH_START_BRIEF: "research:start-brief",
3682
+ RESEARCH_CONFIRM_BRIEF: "research:confirm-brief",
3683
+ RESEARCH_APPROVE_OBJECTIVES: "research:approve-objectives",
3684
+ RESEARCH_SET_MODE: "research:set-mode",
3685
+ RESEARCH_SET_TRACES: "research:set-traces",
3686
+ RESEARCH_CANCEL: "research:cancel",
3687
+ RESEARCH_STOP_AND_SYNTHESIZE: "research:stop-and-synthesize",
3688
+ RESEARCH_EXPORT_REPORT: "research:export-report"
3689
+ };
3690
+ const SecurityChannels = {
3691
+ SECURITY_STATE_UPDATE: "security:state-update",
3692
+ SECURITY_SHOW_DETAILS: "security:show-details",
3693
+ SECURITY_PROCEED_ANYWAY: "security:proceed-anyway",
3694
+ SECURITY_GO_BACK_TO_SAFETY: "security:go-back-to-safety"
3695
+ };
3696
+ const SessionChannels = {
3697
+ SESSION_LIST: "session:list",
3698
+ SESSION_SAVE: "session:save",
3699
+ SESSION_LOAD: "session:load",
3700
+ SESSION_DELETE: "session:delete"
3701
+ };
3702
+ const SettingsChannels = {
3703
+ SETTINGS_GET: "settings:get",
3704
+ SETTINGS_SET: "settings:set",
3705
+ SETTINGS_UPDATE: "settings:update",
3706
+ SETTINGS_HEALTH_GET: "settings:health:get",
3707
+ SETTINGS_HEALTH_UPDATE: "settings:health:update"
3708
+ };
3709
+ const SupportChannels = {
3710
+ SUPPORT_SUBMIT_FEEDBACK: "support:submit-feedback"
3711
+ };
3712
+ const TabChannels = {
3713
+ TAB_CREATE: "tab:create",
3714
+ TAB_CLOSE: "tab:close",
3715
+ TAB_SWITCH: "tab:switch",
3716
+ TAB_NAVIGATE: "tab:navigate",
3717
+ TAB_BACK: "tab:back",
3718
+ TAB_FORWARD: "tab:forward",
3719
+ TAB_RELOAD: "tab:reload",
3720
+ TAB_STATE_GET: "tab:state-get",
3721
+ TAB_STATE_UPDATE: "tab:state-update",
3722
+ RENDERER_VIEW_READY: "renderer:view-ready",
3594
3723
  TAB_TOGGLE_AD_BLOCK: "tab:toggle-ad-block",
3595
- // Zoom
3596
3724
  TAB_ZOOM_IN: "tab:zoom-in",
3597
3725
  TAB_ZOOM_OUT: "tab:zoom-out",
3598
3726
  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
3727
  TAB_REOPEN_CLOSED: "tab:reopen-closed",
3606
3728
  TAB_DUPLICATE: "tab:duplicate",
3607
3729
  TAB_CONTEXT_MENU: "tab:context-menu",
3608
- // Pin tabs
3609
3730
  TAB_PIN: "tab:pin",
3610
3731
  TAB_UNPIN: "tab:unpin",
3611
- // Tab groups
3612
3732
  TAB_GROUP_CREATE: "tab-group:create",
3613
3733
  TAB_GROUP_ADD_TAB: "tab-group:add-tab",
3614
3734
  TAB_GROUP_REMOVE_TAB: "tab-group:remove-tab",
3615
3735
  TAB_GROUP_TOGGLE_COLLAPSED: "tab-group:toggle-collapsed",
3616
3736
  TAB_GROUP_SET_COLOR: "tab-group:set-color",
3617
3737
  TAB_GROUP_CONTEXT_MENU: "tab-group:context-menu",
3618
- // Audio / mute
3619
3738
  TAB_TOGGLE_MUTE: "tab:toggle-mute",
3620
- // Print
3621
3739
  TAB_PRINT: "tab:print",
3622
3740
  TAB_PRINT_TO_PDF: "tab:print-to-pdf",
3623
- // Windows
3624
- OPEN_NEW_WINDOW: "window:open-new",
3625
- // Private browsing
3626
- OPEN_PRIVATE_WINDOW: "private:open-window",
3627
- IS_PRIVATE_MODE: "private:is-private",
3628
- // Find in page
3629
- FIND_IN_PAGE_START: "find:start",
3630
- FIND_IN_PAGE_NEXT: "find:next",
3631
- 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
3741
+ TAB_TOGGLE_PIP: "tab:toggle-pip"
3742
+ };
3743
+ const UIChannels = {
3744
+ SIDEBAR_TOGGLE: "ui:sidebar-toggle",
3745
+ SIDEBAR_NAVIGATE: "ui:sidebar-navigate",
3746
+ SIDEBAR_RESIZE: "ui:sidebar-resize",
3747
+ SIDEBAR_RESIZE_START: "ui:sidebar-resize-start",
3748
+ SIDEBAR_RESIZE_COMMIT: "ui:sidebar-resize-commit",
3749
+ SIDEBAR_POPOUT: "ui:sidebar-popout",
3750
+ SIDEBAR_DOCK: "ui:sidebar-dock",
3751
+ SIDEBAR_STATE_UPDATE: "ui:sidebar-state-update",
3752
+ SIDEBAR_CONTEXT_MENU: "ui:sidebar-context-menu",
3753
+ FOCUS_MODE_TOGGLE: "ui:focus-mode-toggle",
3754
+ SETTINGS_VISIBILITY: "ui:settings-visibility"
3755
+ };
3756
+ const UpdateChannels = {
3757
+ UPDATES_CHECK: "updates:check",
3758
+ UPDATES_OPEN_DOWNLOAD: "updates:open-download"
3759
+ };
3760
+ const VaultChannels = {
3666
3761
  VAULT_LIST: "vault:list",
3667
3762
  VAULT_ADD: "vault:add",
3668
3763
  VAULT_UPDATE: "vault:update",
3669
3764
  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
3765
+ VAULT_AUDIT_LOG: "vault:audit-log"
3766
+ };
3767
+ const WindowControlChannels = {
3689
3768
  WINDOW_MINIMIZE: "window:minimize",
3690
3769
  WINDOW_MAXIMIZE: "window:maximize",
3691
3770
  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"
3771
+ OPEN_NEW_WINDOW: "window:open-new",
3772
+ OPEN_PRIVATE_WINDOW: "private:open-window",
3773
+ IS_PRIVATE_MODE: "private:is-private",
3774
+ FIND_IN_PAGE_START: "find:start",
3775
+ FIND_IN_PAGE_NEXT: "find:next",
3776
+ FIND_IN_PAGE_STOP: "find:stop",
3777
+ FIND_IN_PAGE_RESULT: "find:result"
3778
+ };
3779
+ const Channels = {
3780
+ ...AIChannels,
3781
+ ...AutofillChannels,
3782
+ ...AutomationChannels,
3783
+ ...BookmarkChannels,
3784
+ ...BrowsingDataChannels,
3785
+ ...CodexChannels,
3786
+ ...ContentChannels,
3787
+ ...DevToolsChannels,
3788
+ ...DownloadChannels,
3789
+ ...HighlightChannels,
3790
+ ...HistoryChannels,
3791
+ ...HumanVaultChannels,
3792
+ ...McpChannels,
3793
+ ...OpenRouterChannels,
3794
+ ...PermissionChannels,
3795
+ ...PremiumChannels,
3796
+ ...ResearchChannels,
3797
+ ...SecurityChannels,
3798
+ ...SessionChannels,
3799
+ ...SettingsChannels,
3800
+ ...SupportChannels,
3801
+ ...TabChannels,
3802
+ ...UIChannels,
3803
+ ...UpdateChannels,
3804
+ ...VaultChannels,
3805
+ ...WindowControlChannels
3736
3806
  };
3737
3807
  const MAX_DETAIL_ITEMS = 3;
3738
3808
  const MIN_BLOCK_SIMILARITY = 0.82;
@@ -4690,7 +4760,15 @@ function errorResult(error, value) {
4690
4760
  function getErrorMessage(error, fallback = "Unknown error") {
4691
4761
  return error instanceof Error && error.message ? error.message : fallback;
4692
4762
  }
4693
- const logger$q = createLogger("Premium");
4763
+ async function readJsonResponse(response, fallback, onError) {
4764
+ try {
4765
+ return await response.json();
4766
+ } catch (err) {
4767
+ onError(getErrorMessage(err, "JSON parse failed"));
4768
+ return fallback;
4769
+ }
4770
+ }
4771
+ const logger$t = createLogger("Premium");
4694
4772
  const VERIFICATION_API = process.env.VESSEL_PREMIUM_API || "https://vesselpremium.quantaintellect.com";
4695
4773
  const FREE_TOOL_ITERATION_LIMIT = 50;
4696
4774
  const REVALIDATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
@@ -4866,7 +4944,7 @@ async function verifySubscription$1(identifier) {
4866
4944
  });
4867
4945
  if (!res.ok) {
4868
4946
  const detail = await readApiErrorDetail(res);
4869
- logger$q.warn(
4947
+ logger$t.warn(
4870
4948
  "Verification API returned a non-OK status:",
4871
4949
  res.status,
4872
4950
  detail
@@ -4885,7 +4963,7 @@ async function verifySubscription$1(identifier) {
4885
4963
  setSetting("premium", updated);
4886
4964
  return updated;
4887
4965
  } catch (err) {
4888
- logger$q.warn("Verification failed:", err);
4966
+ logger$t.warn("Verification failed:", err);
4889
4967
  return current;
4890
4968
  }
4891
4969
  }
@@ -4903,7 +4981,11 @@ async function requestActivationCode(email) {
4903
4981
  headers: { "Content-Type": "application/json" },
4904
4982
  body: JSON.stringify({ email: normalizedEmail })
4905
4983
  });
4906
- const data = await res.json().catch(() => ({}));
4984
+ const data = await readJsonResponse(
4985
+ res,
4986
+ {},
4987
+ (msg) => logger$t.warn("Failed to parse premium activation response:", msg)
4988
+ );
4907
4989
  if (!res.ok || !data.challengeToken) {
4908
4990
  return errorResult(data.error || `HTTP ${res.status}`);
4909
4991
  }
@@ -4944,7 +5026,11 @@ async function verifyActivationCode(email, code, challengeToken) {
4944
5026
  challengeToken: challengeToken.trim()
4945
5027
  })
4946
5028
  });
4947
- const data = await res.json().catch(() => ({}));
5029
+ const data = await readJsonResponse(
5030
+ res,
5031
+ {},
5032
+ (msg) => logger$t.warn("Failed to parse premium verification response:", msg)
5033
+ );
4948
5034
  if (!res.ok) {
4949
5035
  return errorResult(data.error || `HTTP ${res.status}`, {
4950
5036
  state: getPremiumState()
@@ -5540,7 +5626,7 @@ const EXTRACT_TIMEOUT_MAX_MS = 2e4;
5540
5626
  const MUTATION_CAPTURE_INTERVAL_MS = 5e3;
5541
5627
  const MUTATION_SETTLE_AFTER_MS = 1500;
5542
5628
  const AGENT_STREAM_IDLE_TIMEOUT_MS = 3e4;
5543
- const logger$p = createLogger("Extractor");
5629
+ const logger$s = createLogger("Extractor");
5544
5630
  const EXTRACTION_CACHE_TTL_MS = 1500;
5545
5631
  const MAX_EXTRACTION_CACHE_ENTRIES = 50;
5546
5632
  const extractionCache = new BoundedCache(
@@ -6305,9 +6391,9 @@ async function executeScript(webContents, script, options = {}) {
6305
6391
  const message = err instanceof Error ? err.message : String(err);
6306
6392
  const detail = `Failed to execute page script${label} on ${url}: ${message}`;
6307
6393
  if (options.warnOnFailure) {
6308
- logger$p.warn(detail);
6394
+ logger$s.warn(detail);
6309
6395
  } else {
6310
- logger$p.debug(detail);
6396
+ logger$s.debug(detail);
6311
6397
  }
6312
6398
  return null;
6313
6399
  } finally {
@@ -6416,7 +6502,7 @@ async function estimateExtractionTimeout(webContents) {
6416
6502
  return EXTRACT_TIMEOUT_BASE_MS + extra;
6417
6503
  }
6418
6504
  } catch (err) {
6419
- logger$p.warn("Failed to estimate extraction timeout, using base timeout:", err);
6505
+ logger$s.warn("Failed to estimate extraction timeout, using base timeout:", err);
6420
6506
  }
6421
6507
  return EXTRACT_TIMEOUT_BASE_MS;
6422
6508
  }
@@ -6775,11 +6861,21 @@ function getActiveTabInfo(tabManager) {
6775
6861
  if (wc.isDestroyed()) return null;
6776
6862
  return { tab, wc };
6777
6863
  }
6864
+ function createWindowStateMessenger(chromeView, sidebarView, devtoolsPanelView) {
6865
+ return (channel, ...args) => {
6866
+ sendSafe(chromeView.webContents, channel, ...args);
6867
+ sendSafe(sidebarView.webContents, channel, ...args);
6868
+ sendSafe(devtoolsPanelView.webContents, channel, ...args);
6869
+ };
6870
+ }
6778
6871
  function sendSafe(wc, channel, ...args) {
6779
6872
  if (!wc || wc.isDestroyed()) return;
6780
6873
  try {
6781
6874
  wc.send(channel, ...args);
6782
- } catch {
6875
+ } catch (err) {
6876
+ if (process.env.VESSEL_DEBUG === "1" || process.env.VESSEL_DEBUG === "true") {
6877
+ console.debug("sendSafe failed for channel", channel, err);
6878
+ }
6783
6879
  }
6784
6880
  }
6785
6881
  function setSidebarPanelMode(state2, mode, reason = "user") {
@@ -7374,6 +7470,82 @@ function isClickReadLoop(names) {
7374
7470
  return clickReadPairs >= 2;
7375
7471
  }
7376
7472
  const TERMINAL_TOOL_RESULT = "__VESSEL_TERMINAL_TOOL_RESULT__";
7473
+ const logger$r = createLogger("PromptCache");
7474
+ function shortHash(value) {
7475
+ return crypto$2.createHash("sha256").update(value).digest("hex").slice(0, 12);
7476
+ }
7477
+ function buildPromptCacheKey(input) {
7478
+ return [
7479
+ "vessel",
7480
+ input.providerId,
7481
+ input.mode,
7482
+ input.profile ?? "default",
7483
+ shortHash(input.model.trim().toLowerCase())
7484
+ ].join(":");
7485
+ }
7486
+ function openAIPromptCacheOptions(input) {
7487
+ if (input.providerId !== "openai") return {};
7488
+ return {
7489
+ prompt_cache_key: buildPromptCacheKey(input),
7490
+ prompt_cache_retention: "in_memory",
7491
+ stream_options: { include_usage: true }
7492
+ };
7493
+ }
7494
+ function withAnthropicCacheControl(value) {
7495
+ return {
7496
+ ...value,
7497
+ cache_control: { type: "ephemeral" }
7498
+ };
7499
+ }
7500
+ function anthropicCachedSystem(systemPrompt) {
7501
+ return [
7502
+ withAnthropicCacheControl({
7503
+ type: "text",
7504
+ text: systemPrompt
7505
+ })
7506
+ ];
7507
+ }
7508
+ function anthropicCachedTools(tools) {
7509
+ if (tools.length === 0) return tools;
7510
+ return tools.map(
7511
+ (tool, index) => index === tools.length - 1 ? withAnthropicCacheControl(tool) : tool
7512
+ );
7513
+ }
7514
+ function numericField(record, key2) {
7515
+ const value = record[key2];
7516
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
7517
+ }
7518
+ function logOpenAIPromptCacheUsage(usage, context) {
7519
+ if (!usage || typeof usage !== "object") return;
7520
+ const record = usage;
7521
+ const promptTokens = numericField(record, "prompt_tokens");
7522
+ const details = record.prompt_tokens_details;
7523
+ const cachedTokens = details && typeof details === "object" ? numericField(details, "cached_tokens") : null;
7524
+ if (promptTokens === null && cachedTokens === null) return;
7525
+ logger$r.debug("OpenAI prompt cache usage", {
7526
+ model: context.model,
7527
+ mode: context.mode,
7528
+ promptTokens,
7529
+ cachedTokens
7530
+ });
7531
+ }
7532
+ function logAnthropicPromptCacheUsage(usage, context) {
7533
+ if (!usage || typeof usage !== "object") return;
7534
+ const record = usage;
7535
+ const inputTokens = numericField(record, "input_tokens");
7536
+ const cacheCreationTokens = numericField(record, "cache_creation_input_tokens");
7537
+ const cacheReadTokens = numericField(record, "cache_read_input_tokens");
7538
+ if (inputTokens === null && cacheCreationTokens === null && cacheReadTokens === null) {
7539
+ return;
7540
+ }
7541
+ logger$r.debug("Anthropic prompt cache usage", {
7542
+ model: context.model,
7543
+ mode: context.mode,
7544
+ inputTokens,
7545
+ cacheCreationTokens,
7546
+ cacheReadTokens
7547
+ });
7548
+ }
7377
7549
  const ANTHROPIC_MAX_TOKENS = 4096;
7378
7550
  function isRecord$1(value) {
7379
7551
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -7419,13 +7591,19 @@ class AnthropicProvider {
7419
7591
  {
7420
7592
  model: this.model,
7421
7593
  max_tokens: ANTHROPIC_MAX_TOKENS,
7422
- system: systemPrompt,
7594
+ system: anthropicCachedSystem(systemPrompt),
7423
7595
  messages,
7424
7596
  ...thinking ? { thinking } : {}
7425
7597
  },
7426
7598
  { signal: this.abortController.signal }
7427
7599
  );
7428
7600
  for await (const event of stream) {
7601
+ if (event.type === "message_start") {
7602
+ logAnthropicPromptCacheUsage(event.message.usage, {
7603
+ model: this.model,
7604
+ mode: "chat"
7605
+ });
7606
+ }
7429
7607
  if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
7430
7608
  onChunk(event.delta.text);
7431
7609
  }
@@ -7459,9 +7637,9 @@ class AnthropicProvider {
7459
7637
  {
7460
7638
  model: this.model,
7461
7639
  max_tokens: ANTHROPIC_MAX_TOKENS,
7462
- system: systemPrompt,
7640
+ system: anthropicCachedSystem(systemPrompt),
7463
7641
  messages,
7464
- tools,
7642
+ tools: anthropicCachedTools(tools),
7465
7643
  ...thinking ? { thinking } : {}
7466
7644
  },
7467
7645
  { signal: this.abortController.signal }
@@ -7481,6 +7659,12 @@ class AnthropicProvider {
7481
7659
  try {
7482
7660
  for await (const event of stream) {
7483
7661
  resetIdleTimer();
7662
+ if (event.type === "message_start") {
7663
+ logAnthropicPromptCacheUsage(event.message.usage, {
7664
+ model: this.model,
7665
+ mode: "agent"
7666
+ });
7667
+ }
7484
7668
  if (event.type === "content_block_start") {
7485
7669
  if (event.content_block.type === "tool_use") {
7486
7670
  currentToolUse = {
@@ -8247,7 +8431,7 @@ function recoverNarratedActionToolCalls(text, availableToolNames) {
8247
8431
  }
8248
8432
  return recovered;
8249
8433
  }
8250
- const logger$o = createLogger("OpenAIProvider");
8434
+ const logger$q = createLogger("OpenAIProvider");
8251
8435
  function shouldDebugAgentLoop() {
8252
8436
  const value = process.env.VESSEL_DEBUG_AGENT_LOOP;
8253
8437
  return value === "1" || value === "true";
@@ -8515,9 +8699,9 @@ function shouldRetryCompactToolLoop(profile, text, hasToolHistory, userMessage)
8515
8699
  function logAgentLoopDebug(payload) {
8516
8700
  if (!shouldDebugAgentLoop()) return;
8517
8701
  try {
8518
- logger$o.info(`[agent-debug] ${JSON.stringify(payload)}`);
8702
+ logger$q.info(`[agent-debug] ${JSON.stringify(payload)}`);
8519
8703
  } catch (err) {
8520
- logger$o.warn("Failed to serialize debug payload:", err);
8704
+ logger$q.warn("Failed to serialize debug payload:", err);
8521
8705
  }
8522
8706
  }
8523
8707
  function formatOpenAICompatErrorMessage(providerId, message) {
@@ -8573,11 +8757,21 @@ class OpenAICompatProvider {
8573
8757
  max_tokens: 4096,
8574
8758
  stream: true,
8575
8759
  messages,
8760
+ ...openAIPromptCacheOptions({
8761
+ providerId: this.providerId,
8762
+ model: this.model,
8763
+ mode: "chat",
8764
+ profile: this.agentToolProfile
8765
+ }),
8576
8766
  ...reasoningEffort ? { reasoning_effort: reasoningEffort } : {}
8577
8767
  },
8578
8768
  { signal: this.abortController.signal }
8579
8769
  );
8580
8770
  for await (const chunk of stream) {
8771
+ logOpenAIPromptCacheUsage(chunk.usage, {
8772
+ model: this.model,
8773
+ mode: "chat"
8774
+ });
8581
8775
  const choice = chunk.choices[0];
8582
8776
  if (!choice) continue;
8583
8777
  const delta = choice.delta;
@@ -8644,11 +8838,21 @@ class OpenAICompatProvider {
8644
8838
  tools: openAITools,
8645
8839
  tool_choice: "auto",
8646
8840
  temperature: agentTemperatureForProfile(this.agentToolProfile),
8841
+ ...openAIPromptCacheOptions({
8842
+ providerId: this.providerId,
8843
+ model: this.model,
8844
+ mode: "agent",
8845
+ profile: this.agentToolProfile
8846
+ }),
8647
8847
  ...reasoningEffort ? { reasoning_effort: reasoningEffort } : {}
8648
8848
  },
8649
8849
  { signal: this.abortController.signal }
8650
8850
  );
8651
8851
  for await (const chunk of stream) {
8852
+ logOpenAIPromptCacheUsage(chunk.usage, {
8853
+ model: this.model,
8854
+ mode: "agent"
8855
+ });
8652
8856
  const choice = chunk.choices[0];
8653
8857
  if (!choice) continue;
8654
8858
  const delta = choice.delta;
@@ -9121,7 +9325,7 @@ function createLocalPkceOAuthFlow(config) {
9121
9325
  isInProgress: () => activeFlow !== null
9122
9326
  };
9123
9327
  }
9124
- const logger$n = createLogger("CodexOAuth");
9328
+ const logger$p = createLogger("CodexOAuth");
9125
9329
  const ISSUER = "https://auth.openai.com";
9126
9330
  const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
9127
9331
  const SCOPE = "openid profile email offline_access api.connectors.read api.connectors.invoke";
@@ -9252,7 +9456,7 @@ function escapeHtml$1(text) {
9252
9456
  }
9253
9457
  const codexOAuth = createLocalPkceOAuthFlow({
9254
9458
  name: "Codex",
9255
- logger: logger$n,
9459
+ logger: logger$p,
9256
9460
  preferredPorts: [PREFERRED_PORT$1, FALLBACK_PORT$1],
9257
9461
  timeoutMs: AUTH_TIMEOUT_MS$1,
9258
9462
  callbackPath: () => "/auth/callback",
@@ -9276,7 +9480,7 @@ async function startCodexOAuth(onStatus) {
9276
9480
  function cancelCodexOAuth() {
9277
9481
  codexOAuth.cancel();
9278
9482
  }
9279
- const logger$m = createLogger("CodexProvider");
9483
+ const logger$o = createLogger("CodexProvider");
9280
9484
  const REFRESH_WINDOW_MS = 5 * 60 * 1e3;
9281
9485
  const CODEX_BACKEND_BASE_URL = "https://chatgpt.com/backend-api/codex";
9282
9486
  const CODEX_CLIENT_VERSION = "0.129.0";
@@ -9341,7 +9545,7 @@ class CodexProvider {
9341
9545
  async ensureFreshTokens() {
9342
9546
  if (Date.now() < this.tokens.expiresAt - REFRESH_WINDOW_MS) return;
9343
9547
  try {
9344
- logger$m.info("Refreshing Codex access token");
9548
+ logger$o.info("Refreshing Codex access token");
9345
9549
  const fresh = await refreshAccessToken(this.tokens);
9346
9550
  this.tokens = fresh;
9347
9551
  writeStoredCodexTokens(fresh);
@@ -9489,7 +9693,7 @@ class CodexProvider {
9489
9693
  } catch (err) {
9490
9694
  if (err.name !== "AbortError") {
9491
9695
  const msg = err instanceof Error ? err.message : String(err);
9492
- logger$m.error("Codex streamQuery error:", err);
9696
+ logger$o.error("Codex streamQuery error:", err);
9493
9697
  onChunk(`
9494
9698
 
9495
9699
  [Error: ${msg}]`);
@@ -9557,7 +9761,7 @@ class CodexProvider {
9557
9761
  } catch (err) {
9558
9762
  if (err.name !== "AbortError") {
9559
9763
  const msg = err instanceof Error ? err.message : String(err);
9560
- logger$m.error("Codex streamAgentQuery error:", err);
9764
+ logger$o.error("Codex streamAgentQuery error:", err);
9561
9765
  onChunk(`
9562
9766
 
9563
9767
  [Error: ${msg}]`);
@@ -10791,7 +10995,7 @@ function normalizeBookmarkMetadataUpdate(input) {
10791
10995
  }
10792
10996
  return normalized;
10793
10997
  }
10794
- const logger$l = createLogger("BookmarkManager");
10998
+ const logger$n = createLogger("BookmarkManager");
10795
10999
  const UNSORTED_ID = "unsorted";
10796
11000
  const ARCHIVE_FOLDER_NAME = "Archive";
10797
11001
  const NETSCAPE_BOOKMARKS_DOCTYPE = "<!DOCTYPE NETSCAPE-Bookmark-file-1>";
@@ -11364,7 +11568,7 @@ function importBookmarksFromJson(content) {
11364
11568
  emit$2();
11365
11569
  }
11366
11570
  } catch (err) {
11367
- logger$l.warn("Failed to import bookmarks from JSON:", err);
11571
+ logger$n.warn("Failed to import bookmarks from JSON:", err);
11368
11572
  errors++;
11369
11573
  }
11370
11574
  return { imported, skipped, errors };
@@ -11769,6 +11973,7 @@ async function resolveSelector(wc, index, selector) {
11769
11973
  if (extractedSelector) return extractedSelector;
11770
11974
  return null;
11771
11975
  }
11976
+ const logger$m = createLogger("LinkValidation");
11772
11977
  const DEAD_STATUS_CODES = /* @__PURE__ */ new Set([404, 410, 451]);
11773
11978
  const HEAD_FALLBACK_STATUS_CODES = /* @__PURE__ */ new Set([400, 403, 404, 405, 406, 500, 501]);
11774
11979
  function isHttpUrl(value) {
@@ -11791,7 +11996,9 @@ async function requestUrl(url, method, timeoutMs) {
11791
11996
  "user-agent": "Vessel/0.1.0 (+https://github.com/unmodeled-tyler/vessel-browser)"
11792
11997
  }
11793
11998
  });
11794
- await response.body?.cancel().catch(() => void 0);
11999
+ await response.body?.cancel().catch((err) => {
12000
+ logger$m.debug("Failed to cancel response body:", err);
12001
+ });
11795
12002
  return response;
11796
12003
  } finally {
11797
12004
  clearTimeout(timer);
@@ -11854,7 +12061,7 @@ function formatDeadLinkMessage(label, result) {
11854
12061
  const status = result.statusCode ? `HTTP ${result.statusCode}` : "dead link";
11855
12062
  return `Skipped stale link "${label}" because ${destination} returned ${status}. Try a different link or URL instead.`;
11856
12063
  }
11857
- const logger$k = createLogger("Screenshot");
12064
+ const logger$l = createLogger("Screenshot");
11858
12065
  const SCREENSHOT_RETRY_COUNT = 3;
11859
12066
  const SCREENSHOT_RETRY_BASE_DELAY_MS = 120;
11860
12067
  async function captureScreenshot(wc) {
@@ -11876,7 +12083,7 @@ async function captureScreenshot(wc) {
11876
12083
  }
11877
12084
  }
11878
12085
  } catch (err) {
11879
- logger$k.debug(
12086
+ logger$l.debug(
11880
12087
  `capturePage attempt ${attempt + 1} failed; retrying if attempts remain.`,
11881
12088
  getErrorMessage(err)
11882
12089
  );
@@ -14353,7 +14560,7 @@ class TabMutex {
14353
14560
  return run;
14354
14561
  }
14355
14562
  }
14356
- const logger$j = createLogger("PageActions");
14563
+ const logger$k = createLogger("PageActions");
14357
14564
  const DEFAULT_PAGE_SCRIPT_TIMEOUT_MS = 1500;
14358
14565
  const PAGE_SCRIPT_TIMEOUT = /* @__PURE__ */ Symbol("page-script-timeout");
14359
14566
  function pageBusyError(action) {
@@ -14381,7 +14588,7 @@ async function executePageScript(wc, script, options) {
14381
14588
  return result;
14382
14589
  } catch (err) {
14383
14590
  const label = options?.label ? ` (${options.label})` : "";
14384
- logger$j.warn(`Failed to execute page script${label}:`, err);
14591
+ logger$k.warn(`Failed to execute page script${label}:`, err);
14385
14592
  return null;
14386
14593
  } finally {
14387
14594
  if (timer) {
@@ -14578,7 +14785,9 @@ Page title: ${title}` : "";
14578
14785
  signals.push('consent-banner:' + consentSelectors[i]);
14579
14786
  break;
14580
14787
  }
14581
- } catch(e) {}
14788
+ } catch {
14789
+ // Swallow — cross-origin frames may block selector access
14790
+ }
14582
14791
  }
14583
14792
  var vw = window.innerWidth || 0;
14584
14793
  var vh = window.innerHeight || 0;
@@ -14621,7 +14830,7 @@ Search results snapshot:
14621
14830
  ${truncated}`;
14622
14831
  }
14623
14832
  } catch (err) {
14624
- logger$j.warn("Failed to build post-search summary, falling back to nav summary:", err);
14833
+ logger$k.warn("Failed to build post-search summary, falling back to nav summary:", err);
14625
14834
  }
14626
14835
  const fallback = await getPostNavSummary(wc);
14627
14836
  return fallback ? `${fallback}
@@ -14644,7 +14853,7 @@ Page snapshot after navigation:
14644
14853
  ${truncated}`;
14645
14854
  }
14646
14855
  } catch (err) {
14647
- logger$j.warn("Failed to build post-click navigation summary:", err);
14856
+ logger$k.warn("Failed to build post-click navigation summary:", err);
14648
14857
  }
14649
14858
  return "";
14650
14859
  }
@@ -15534,7 +15743,7 @@ async function restoreLocaleSnapshot(wc, snapshot2) {
15534
15743
  }
15535
15744
  }
15536
15745
  } catch (err) {
15537
- logger$j.warn("Failed to restore locale via history navigation, trying URL reload fallback:", err);
15746
+ logger$k.warn("Failed to restore locale via history navigation, trying URL reload fallback:", err);
15538
15747
  }
15539
15748
  if (snapshot2.url && snapshot2.url !== wc.getURL()) {
15540
15749
  try {
@@ -15543,7 +15752,7 @@ async function restoreLocaleSnapshot(wc, snapshot2) {
15543
15752
  await waitForLoad(wc, 3e3);
15544
15753
  return;
15545
15754
  } catch (err) {
15546
- logger$j.warn("Failed to restore locale via safe URL load, trying page reload fallback:", err);
15755
+ logger$k.warn("Failed to restore locale via safe URL load, trying page reload fallback:", err);
15547
15756
  }
15548
15757
  }
15549
15758
  if (snapshot2.url) {
@@ -15551,7 +15760,7 @@ async function restoreLocaleSnapshot(wc, snapshot2) {
15551
15760
  await wc.reload();
15552
15761
  await waitForLoad(wc, 3e3);
15553
15762
  } catch (err) {
15554
- logger$j.warn("Failed to restore locale via page reload:", err);
15763
+ logger$k.warn("Failed to restore locale via page reload:", err);
15555
15764
  }
15556
15765
  }
15557
15766
  }
@@ -15616,7 +15825,7 @@ async function followHrefFromClickResult(wc, beforeUrl, result, logMessage) {
15616
15825
  const hrefUrl = wc.getURL();
15617
15826
  if (hrefUrl !== beforeUrl) return `${result.split("\n")[0]} -> ${hrefUrl}`;
15618
15827
  } catch (err) {
15619
- logger$j.warn(logMessage, err);
15828
+ logger$k.warn(logMessage, err);
15620
15829
  }
15621
15830
  return null;
15622
15831
  }
@@ -15828,7 +16037,7 @@ ${postActivationOverlayHint}`;
15828
16037
  return `${clickText} -> ${hrefFallbackUrl} (recovered via href fallback)`;
15829
16038
  }
15830
16039
  } catch (err) {
15831
- logger$j.warn("Failed href fallback after click, returning generic click result:", err);
16040
+ logger$k.warn("Failed href fallback after click, returning generic click result:", err);
15832
16041
  }
15833
16042
  }
15834
16043
  }
@@ -15873,7 +16082,7 @@ async function tryAutoDismissCartDialog(wc) {
15873
16082
  return result;
15874
16083
  }
15875
16084
  } catch (err) {
15876
- logger$j.warn("Failed to auto-dismiss cart dialog, falling back to dialog actions:", err);
16085
+ logger$k.warn("Failed to auto-dismiss cart dialog, falling back to dialog actions:", err);
15877
16086
  }
15878
16087
  return null;
15879
16088
  }
@@ -16326,7 +16535,9 @@ async function tryDismissConsentIframe(wc) {
16326
16535
  return 'Clicked iframe consent button: ' + text.slice(0, 60);
16327
16536
  }
16328
16537
  }
16329
- } catch(e) {}
16538
+ } catch {
16539
+ // Swallow — selector may be invalid or cross-origin frame may block access
16540
+ }
16330
16541
  }
16331
16542
  // Text-match fallback on all buttons
16332
16543
  var buttons = document.querySelectorAll('button, [role="button"], a.message-component');
@@ -18028,7 +18239,7 @@ async function executeAction(name, args, ctx) {
18028
18239
  )
18029
18240
  ]);
18030
18241
  } catch (err) {
18031
- logger$j.warn("Failed to extract content for read_page, falling back to lighter recovery:", err);
18242
+ logger$k.warn("Failed to extract content for read_page, falling back to lighter recovery:", err);
18032
18243
  content = null;
18033
18244
  }
18034
18245
  if (!content || content.content.length === 0) {
@@ -18045,12 +18256,12 @@ async function executeAction(name, args, ctx) {
18045
18256
  new Promise((resolve) => setTimeout(() => resolve(null), 3e3))
18046
18257
  ]);
18047
18258
  } catch (err) {
18048
- logger$j.warn("Failed to re-extract content after iframe consent dismissal:", err);
18259
+ logger$k.warn("Failed to re-extract content after iframe consent dismissal:", err);
18049
18260
  content = null;
18050
18261
  }
18051
18262
  }
18052
18263
  } catch (err) {
18053
- logger$j.warn("Failed iframe consent dismissal during read_page recovery:", err);
18264
+ logger$k.warn("Failed iframe consent dismissal during read_page recovery:", err);
18054
18265
  }
18055
18266
  }
18056
18267
  if (content && content.content.length > 0) {
@@ -18463,7 +18674,7 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
18463
18674
  try {
18464
18675
  page = await extractContent(wc);
18465
18676
  } catch (err) {
18466
- logger$j.warn("Failed to extract content for suggest:", err);
18677
+ logger$k.warn("Failed to extract content for suggest:", err);
18467
18678
  return "Could not read page. Try navigate to a working URL.";
18468
18679
  }
18469
18680
  const suggestions = [];
@@ -18851,7 +19062,7 @@ WARNING: You have clicked ${clickStreakCount} elements on this page without veri
18851
19062
  }
18852
19063
  return formattedResult + await getPostActionState$1(ctx, name) + clickNavSummary + streakWarning + flowCtx;
18853
19064
  }
18854
- const logger$i = createLogger("ResearchOrchestrator");
19065
+ const logger$j = createLogger("ResearchOrchestrator");
18855
19066
  const MAX_THREADS = 5;
18856
19067
  const MAX_TRACE_ARGS_CHARS = 1200;
18857
19068
  const MAX_TRACE_RESULT_CHARS = 2e3;
@@ -19040,7 +19251,7 @@ class ResearchOrchestrator {
19040
19251
  }
19041
19252
  stopAndSynthesizeCurrentFindings() {
19042
19253
  if (this.state.phase !== "executing") {
19043
- logger$i.warn("Not executing, ignoring stopAndSynthesizeCurrentFindings");
19254
+ logger$j.warn("Not executing, ignoring stopAndSynthesizeCurrentFindings");
19044
19255
  return;
19045
19256
  }
19046
19257
  this.stopRequested = true;
@@ -19074,23 +19285,23 @@ class ResearchOrchestrator {
19074
19285
  async startBrief(userQuery) {
19075
19286
  const query = userQuery.trim();
19076
19287
  if (!query) {
19077
- logger$i.warn("Ignoring empty Research Desk query");
19288
+ logger$j.warn("Ignoring empty Research Desk query");
19078
19289
  return;
19079
19290
  }
19080
19291
  if (this.state.phase !== "idle") {
19081
- logger$i.warn("Research already in progress, ignoring startBrief");
19292
+ logger$j.warn("Research already in progress, ignoring startBrief");
19082
19293
  return;
19083
19294
  }
19084
19295
  this.state = this.initialState();
19085
19296
  this.state.originalQuery = query;
19086
19297
  this.state.startedAt = (/* @__PURE__ */ new Date()).toISOString();
19087
19298
  this.setPhase("briefing");
19088
- logger$i.info(`Brief started for query: ${query.slice(0, 120)}`);
19299
+ logger$j.info(`Brief started for query: ${query.slice(0, 120)}`);
19089
19300
  }
19090
19301
  // ── phase: briefing → planning ─────────────────────────────────
19091
19302
  confirmBrief() {
19092
19303
  if (this.state.phase !== "briefing") {
19093
- logger$i.warn("Not in briefing phase, ignoring confirmBrief");
19304
+ logger$j.warn("Not in briefing phase, ignoring confirmBrief");
19094
19305
  return;
19095
19306
  }
19096
19307
  this.setPhase("planning");
@@ -19098,7 +19309,7 @@ class ResearchOrchestrator {
19098
19309
  // ── phase: planning → awaiting_approval ────────────────────────
19099
19310
  setObjectives(objectives) {
19100
19311
  if (this.state.phase !== "planning") {
19101
- logger$i.warn("Not in planning phase, ignoring setObjectives");
19312
+ logger$j.warn("Not in planning phase, ignoring setObjectives");
19102
19313
  return;
19103
19314
  }
19104
19315
  const threads = objectives.threads.slice(0, MAX_THREADS).map(mergeBlockedSourceDomains);
@@ -19127,11 +19338,11 @@ class ResearchOrchestrator {
19127
19338
  try {
19128
19339
  const parsed = JSON.parse(json);
19129
19340
  if (typeof parsed.researchQuestion !== "string" || !parsed.researchQuestion.trim()) {
19130
- logger$i.warn("Missing researchQuestion in objectives JSON");
19341
+ logger$j.warn("Missing researchQuestion in objectives JSON");
19131
19342
  return false;
19132
19343
  }
19133
19344
  if (!Array.isArray(parsed.threads) || parsed.threads.length === 0) {
19134
- logger$i.warn("Missing or empty threads array in objectives JSON");
19345
+ logger$j.warn("Missing or empty threads array in objectives JSON");
19135
19346
  return false;
19136
19347
  }
19137
19348
  const threads = parsed.threads.map((t, i) => {
@@ -19149,7 +19360,7 @@ class ResearchOrchestrator {
19149
19360
  };
19150
19361
  }).filter((thread) => thread.question && thread.searchQueries.length > 0).slice(0, MAX_THREADS);
19151
19362
  if (threads.length === 0) {
19152
- logger$i.warn("Objectives JSON did not contain any valid research threads");
19363
+ logger$j.warn("Objectives JSON did not contain any valid research threads");
19153
19364
  return false;
19154
19365
  }
19155
19366
  const objectives = {
@@ -19160,17 +19371,17 @@ class ResearchOrchestrator {
19160
19371
  totalSourceBudget: threads.reduce((sum, t) => sum + t.sourceBudget, 0)
19161
19372
  };
19162
19373
  this.setObjectives(objectives);
19163
- logger$i.info(`Parsed ${objectives.threads.length} threads from objectives`);
19374
+ logger$j.info(`Parsed ${objectives.threads.length} threads from objectives`);
19164
19375
  return true;
19165
19376
  } catch (err) {
19166
- logger$i.warn("Failed to parse objectives JSON", err);
19377
+ logger$j.warn("Failed to parse objectives JSON", err);
19167
19378
  return false;
19168
19379
  }
19169
19380
  }
19170
19381
  // ── phase: awaiting_approval → executing ───────────────────────
19171
19382
  approveObjectives(mode, includeTraces) {
19172
19383
  if (this.state.phase !== "awaiting_approval") {
19173
- logger$i.warn("Not awaiting approval, ignoring approveObjectives");
19384
+ logger$j.warn("Not awaiting approval, ignoring approveObjectives");
19174
19385
  return;
19175
19386
  }
19176
19387
  if (mode) this.state.supervisionMode = mode;
@@ -19205,7 +19416,7 @@ class ResearchOrchestrator {
19205
19416
  this.state.threads.map((thread) => {
19206
19417
  if (this.state.phase !== "executing") return null;
19207
19418
  return this.runSubAgent(thread, tabMutex).catch((err) => {
19208
- logger$i.error(`Sub-agent "${thread.label}" failed`, err);
19419
+ logger$j.error(`Sub-agent "${thread.label}" failed`, err);
19209
19420
  return {
19210
19421
  threadLabel: thread.label,
19211
19422
  threadQuestion: thread.question,
@@ -19234,7 +19445,7 @@ class ResearchOrchestrator {
19234
19445
  try {
19235
19446
  await this.synthesizeReport();
19236
19447
  } catch (err) {
19237
- logger$i.error("Auto-synthesis failed", err);
19448
+ logger$j.error("Auto-synthesis failed", err);
19238
19449
  this.state.error = `Synthesis failed: ${String(err)}`;
19239
19450
  this.setPhase("delivered");
19240
19451
  }
@@ -19345,7 +19556,7 @@ Start by searching for: ${thread.searchQueries.join(" or ")}`;
19345
19556
  try {
19346
19557
  this.tabManager.closeTab(tabId);
19347
19558
  } catch (err) {
19348
- logger$i.warn(`Failed to close sub-agent tab ${tabId}`, err);
19559
+ logger$j.warn(`Failed to close sub-agent tab ${tabId}`, err);
19349
19560
  }
19350
19561
  }
19351
19562
  }
@@ -19354,7 +19565,7 @@ Start by searching for: ${thread.searchQueries.join(" or ")}`;
19354
19565
  try {
19355
19566
  claims = await this.extractClaimsFromTranscript(thread, transcript);
19356
19567
  } catch (err) {
19357
- logger$i.warn(`Claim extraction failed for "${thread.label}"`, err);
19568
+ logger$j.warn(`Claim extraction failed for "${thread.label}"`, err);
19358
19569
  }
19359
19570
  }
19360
19571
  if (this.state.phase === "executing" && this.state.includeTraces) {
@@ -19444,7 +19655,7 @@ ${transcript.slice(0, 32e3)}`;
19444
19655
  (claim) => claim.claim && claim.sourceUrl && claim.extractedQuote
19445
19656
  );
19446
19657
  } catch {
19447
- logger$i.warn(`Failed to parse claims JSON for "${thread.label}"`);
19658
+ logger$j.warn(`Failed to parse claims JSON for "${thread.label}"`);
19448
19659
  return [];
19449
19660
  }
19450
19661
  }
@@ -19529,7 +19740,7 @@ ${transcript.slice(0, 32e3)}`;
19529
19740
  objectives
19530
19741
  };
19531
19742
  } catch (err) {
19532
- logger$i.warn("Failed to parse synthesis JSON, using sourced fallback report", err);
19743
+ logger$j.warn("Failed to parse synthesis JSON, using sourced fallback report", err);
19533
19744
  return buildFallbackReport(objectives, findings, String(err));
19534
19745
  }
19535
19746
  }
@@ -19845,15 +20056,14 @@ function installAdBlocking(tabManager) {
19845
20056
  callback(getRequestFilterDecision(details, false) ?? {});
19846
20057
  return;
19847
20058
  }
19848
- const manager = [...defaultSessionTabManagers].find(
19849
- (candidate) => candidate.findTabByWebContentsId(webContentsId)
19850
- );
19851
- callback(
19852
- getRequestFilterDecision(
19853
- details,
19854
- manager?.isAdBlockingEnabledForWebContents(webContentsId) ?? false
19855
- ) ?? {}
19856
- );
20059
+ let enabled = false;
20060
+ for (const candidate of defaultSessionTabManagers) {
20061
+ if (candidate.findTabByWebContentsId(webContentsId)) {
20062
+ enabled = candidate.isAdBlockingEnabledForWebContents(webContentsId);
20063
+ break;
20064
+ }
20065
+ }
20066
+ callback(getRequestFilterDecision(details, enabled) ?? {});
19857
20067
  });
19858
20068
  }
19859
20069
  function unregisterAdBlockingTabManager(tabManager) {
@@ -20077,7 +20287,7 @@ function loadRenderers(chromeView, sidebarView, devtoolsPanelView) {
20077
20287
  });
20078
20288
  }
20079
20289
  }
20080
- const logger$h = createLogger("PrivateWindow");
20290
+ const logger$i = createLogger("PrivateWindow");
20081
20291
  const privateWindows = /* @__PURE__ */ new Set();
20082
20292
  function layoutPrivateViews(state2) {
20083
20293
  const { window: win, chromeView, tabManager } = state2;
@@ -20298,7 +20508,7 @@ function createPrivateWindow() {
20298
20508
  privateSession.clearStorageData(),
20299
20509
  privateSession.clearCache()
20300
20510
  ]).catch((error) => {
20301
- logger$h.warn("Failed to clear private browsing session:", error);
20511
+ logger$i.warn("Failed to clear private browsing session:", error);
20302
20512
  });
20303
20513
  });
20304
20514
  privateWindows.add(state2);
@@ -20308,7 +20518,7 @@ function createPrivateWindow() {
20308
20518
  });
20309
20519
  loadPrivateRenderer(chromeView);
20310
20520
  win.show();
20311
- logger$h.info("Private browsing window opened");
20521
+ logger$i.info("Private browsing window opened");
20312
20522
  return state2;
20313
20523
  }
20314
20524
  const window$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
@@ -20516,8 +20726,8 @@ const GroupIdSchema = zod.z.string().min(1);
20516
20726
  const UrlSchema = zod.z.string().min(1);
20517
20727
  const ColorSchema = zod.z.string().min(1);
20518
20728
  const FindActionSchema = zod.z.enum(["clearSelection", "keepSelection", "activateSelection"]);
20519
- function registerTabHandlers(windowState, _sendToRendererViews) {
20520
- const { tabManager, mainWindow } = windowState;
20729
+ function registerTabHandlers(windowState2, _sendToRendererViews) {
20730
+ const { tabManager, mainWindow } = windowState2;
20521
20731
  electron.ipcMain.handle(Channels.OPEN_PRIVATE_WINDOW, (event) => {
20522
20732
  assertTrustedIpcSender(event);
20523
20733
  createPrivateWindow();
@@ -20533,20 +20743,20 @@ function registerTabHandlers(windowState, _sendToRendererViews) {
20533
20743
  electron.ipcMain.handle(Channels.TAB_CREATE, (event, url) => {
20534
20744
  assertTrustedIpcSender(event);
20535
20745
  const id = tabManager.createTab(url || loadSettings().defaultUrl);
20536
- layoutViews(windowState);
20746
+ layoutViews(windowState2);
20537
20747
  return id;
20538
20748
  });
20539
20749
  electron.ipcMain.handle(Channels.TAB_CLOSE, (event, id) => {
20540
20750
  assertTrustedIpcSender(event);
20541
20751
  const validated = parseIpc(TabIdSchema, id, "tabId");
20542
20752
  tabManager.closeTab(validated);
20543
- layoutViews(windowState);
20753
+ layoutViews(windowState2);
20544
20754
  });
20545
20755
  electron.ipcMain.handle(Channels.TAB_SWITCH, (event, id) => {
20546
20756
  assertTrustedIpcSender(event);
20547
20757
  const validated = parseIpc(TabIdSchema, id, "tabId");
20548
20758
  tabManager.switchTab(validated);
20549
- layoutViews(windowState);
20759
+ layoutViews(windowState2);
20550
20760
  });
20551
20761
  electron.ipcMain.handle(
20552
20762
  Channels.TAB_NAVIGATE,
@@ -20593,14 +20803,14 @@ function registerTabHandlers(windowState, _sendToRendererViews) {
20593
20803
  electron.ipcMain.handle(Channels.TAB_REOPEN_CLOSED, (event) => {
20594
20804
  assertTrustedIpcSender(event);
20595
20805
  const id = tabManager.reopenClosedTab();
20596
- if (id) layoutViews(windowState);
20806
+ if (id) layoutViews(windowState2);
20597
20807
  return id;
20598
20808
  });
20599
20809
  electron.ipcMain.handle(Channels.TAB_DUPLICATE, (event, id) => {
20600
20810
  assertTrustedIpcSender(event);
20601
20811
  const validated = parseIpc(TabIdSchema, id, "id");
20602
20812
  const newId = tabManager.duplicateTab(validated);
20603
- if (newId) layoutViews(windowState);
20813
+ if (newId) layoutViews(windowState2);
20604
20814
  return newId;
20605
20815
  });
20606
20816
  electron.ipcMain.handle(Channels.TAB_PIN, (event, id) => {
@@ -20654,7 +20864,7 @@ function registerTabHandlers(windowState, _sendToRendererViews) {
20654
20864
  });
20655
20865
  electron.ipcMain.on(Channels.TAB_CONTEXT_MENU, (event, id) => {
20656
20866
  assertTrustedIpcSender(event);
20657
- showTabContextMenu(tabManager, parseIpc(TabIdSchema, id, "id"), mainWindow, () => layoutViews(windowState));
20867
+ showTabContextMenu(tabManager, parseIpc(TabIdSchema, id, "id"), mainWindow, () => layoutViews(windowState2));
20658
20868
  });
20659
20869
  electron.ipcMain.on(Channels.TAB_GROUP_CONTEXT_MENU, (event, groupId) => {
20660
20870
  assertTrustedIpcSender(event);
@@ -20667,7 +20877,7 @@ function registerTabHandlers(windowState, _sendToRendererViews) {
20667
20877
  activeId: tabManager.getActiveTabId() || ""
20668
20878
  };
20669
20879
  });
20670
- const findBridge = createFindInPageBridge(tabManager, windowState.chromeView);
20880
+ const findBridge = createFindInPageBridge(tabManager, windowState2.chromeView);
20671
20881
  electron.ipcMain.handle(Channels.FIND_IN_PAGE_START, (event, text, options) => {
20672
20882
  assertTrustedIpcSender(event);
20673
20883
  return findBridge.start(text, options);
@@ -20682,7 +20892,7 @@ function registerTabHandlers(windowState, _sendToRendererViews) {
20682
20892
  });
20683
20893
  }
20684
20894
  const require$1 = node_module.createRequire(require("url").pathToFileURL(__filename).href);
20685
- const logger$g = createLogger("DevTrace");
20895
+ const logger$h = createLogger("DevTrace");
20686
20896
  let cachedFactory;
20687
20897
  function createNoopTraceSession() {
20688
20898
  return {
@@ -20715,7 +20925,7 @@ function loadLocalFactory() {
20715
20925
  return cachedFactory;
20716
20926
  }
20717
20927
  } catch (err) {
20718
- logger$g.warn("Failed to load local trace logger:", err);
20928
+ logger$h.warn("Failed to load local trace logger:", err);
20719
20929
  }
20720
20930
  }
20721
20931
  return cachedFactory;
@@ -21510,8 +21720,8 @@ function renderReaderContent(page) {
21510
21720
  function escapeHtml(str) {
21511
21721
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
21512
21722
  }
21513
- function registerContentHandlers(windowState) {
21514
- const { tabManager } = windowState;
21723
+ function registerContentHandlers(windowState2) {
21724
+ const { tabManager } = windowState2;
21515
21725
  electron.ipcMain.handle(Channels.CONTENT_EXTRACT, async (event) => {
21516
21726
  assertTrustedIpcSender(event);
21517
21727
  const activeTab = tabManager.getActiveTab();
@@ -21541,21 +21751,21 @@ function registerContentHandlers(windowState) {
21541
21751
  });
21542
21752
  electron.ipcMain.handle(Channels.FOCUS_MODE_TOGGLE, (event) => {
21543
21753
  assertTrustedIpcSender(event);
21544
- windowState.uiState.focusMode = !windowState.uiState.focusMode;
21545
- layoutViews(windowState);
21546
- return windowState.uiState.focusMode;
21754
+ windowState2.uiState.focusMode = !windowState2.uiState.focusMode;
21755
+ layoutViews(windowState2);
21756
+ return windowState2.uiState.focusMode;
21547
21757
  });
21548
21758
  }
21549
- const logger$f = createLogger("HighlightIPC");
21550
- function registerHighlightHandlers(windowState, sendToRendererViews) {
21551
- const { tabManager, chromeView } = windowState;
21759
+ const logger$g = createLogger("HighlightIPC");
21760
+ function registerHighlightHandlers(windowState2, sendToRendererViews) {
21761
+ const { tabManager, chromeView } = windowState2;
21552
21762
  const getActiveHighlightCountSafe = async () => {
21553
21763
  const info = getActiveTabInfo(tabManager);
21554
21764
  if (!info) return 0;
21555
21765
  try {
21556
21766
  return await getHighlightCount(info.wc) ?? 0;
21557
21767
  } catch (err) {
21558
- logger$f.warn("Failed to get active highlight count:", err);
21768
+ logger$g.warn("Failed to get active highlight count:", err);
21559
21769
  return 0;
21560
21770
  }
21561
21771
  };
@@ -21580,13 +21790,13 @@ function registerHighlightHandlers(windowState, sendToRendererViews) {
21580
21790
  const result = await captureSelectionHighlight(wc);
21581
21791
  if (result.success && result.text) {
21582
21792
  await highlightOnPage(wc, null, result.text, void 0, void 0, "yellow").catch(
21583
- (err) => logger$f.warn("Failed to highlight captured selection:", err)
21793
+ (err) => logger$g.warn("Failed to highlight captured selection:", err)
21584
21794
  );
21585
21795
  await emitHighlightCount();
21586
21796
  }
21587
21797
  return result;
21588
21798
  } catch (err) {
21589
- logger$f.warn("Failed to capture highlight from active tab:", err);
21799
+ logger$g.warn("Failed to capture highlight from active tab:", err);
21590
21800
  return { success: false, message: "Could not capture selection" };
21591
21801
  }
21592
21802
  });
@@ -21603,7 +21813,7 @@ function registerHighlightHandlers(windowState, sendToRendererViews) {
21603
21813
  }
21604
21814
  });
21605
21815
  } catch (err) {
21606
- logger$f.warn("Failed to persist auto-highlight selection:", err);
21816
+ logger$g.warn("Failed to persist auto-highlight selection:", err);
21607
21817
  }
21608
21818
  });
21609
21819
  electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_COUNT, (event) => {
@@ -21617,7 +21827,7 @@ function registerHighlightHandlers(windowState, sendToRendererViews) {
21617
21827
  try {
21618
21828
  return scrollToHighlight(info.wc, index);
21619
21829
  } catch (err) {
21620
- logger$f.warn("Failed to scroll to highlight:", err);
21830
+ logger$g.warn("Failed to scroll to highlight:", err);
21621
21831
  return false;
21622
21832
  }
21623
21833
  });
@@ -21632,7 +21842,7 @@ function registerHighlightHandlers(windowState, sendToRendererViews) {
21632
21842
  }
21633
21843
  return removed;
21634
21844
  } catch (err) {
21635
- logger$f.warn("Failed to remove highlight at index:", err);
21845
+ logger$g.warn("Failed to remove highlight at index:", err);
21636
21846
  return false;
21637
21847
  }
21638
21848
  });
@@ -21647,7 +21857,7 @@ function registerHighlightHandlers(windowState, sendToRendererViews) {
21647
21857
  }
21648
21858
  return cleared;
21649
21859
  } catch (err) {
21650
- logger$f.warn("Failed to clear highlight elements:", err);
21860
+ logger$g.warn("Failed to clear highlight elements:", err);
21651
21861
  return false;
21652
21862
  }
21653
21863
  });
@@ -22616,7 +22826,7 @@ Exception: ${result.exceptionDetails}`);
22616
22826
  }
22617
22827
  );
22618
22828
  }
22619
- const logger$e = createLogger("MCP");
22829
+ const logger$f = createLogger("MCP");
22620
22830
  function asTextResponse(text) {
22621
22831
  return { content: [{ type: "text", text }] };
22622
22832
  }
@@ -22717,7 +22927,7 @@ async function getPostActionState(tabManager, name) {
22717
22927
  }
22718
22928
  }
22719
22929
  } catch (err) {
22720
- logger$e.warn("Failed to compute post-action state warning:", err);
22930
+ logger$f.warn("Failed to compute post-action state warning:", err);
22721
22931
  }
22722
22932
  return `${warning}
22723
22933
  [state: url=${wc.getURL()}, canGoBack=${tab.canGoBack()}, canGoForward=${tab.canGoForward()}, loading=${wc.isLoading()}]`;
@@ -22822,7 +23032,7 @@ async function waitForConditionMcp(wc, text, selector, timeoutMs) {
22822
23032
  }
22823
23033
  })()
22824
23034
  `).catch((err) => {
22825
- logger$e.warn("Failed to gather wait_for timeout diagnostic:", err);
23035
+ logger$f.warn("Failed to gather wait_for timeout diagnostic:", err);
22826
23036
  return null;
22827
23037
  });
22828
23038
  if (typeof diagnostic === "string" && diagnostic.trim()) {
@@ -23538,7 +23748,7 @@ function registerSessionTools(server, tabManager, runtime2) {
23538
23748
  )
23539
23749
  );
23540
23750
  }
23541
- const logger$d = createLogger("VaultShared");
23751
+ const logger$e = createLogger("VaultShared");
23542
23752
  const ALGORITHM = "aes-256-gcm";
23543
23753
  const IV_LENGTH = 12;
23544
23754
  const AUTH_TAG_LENGTH = 16;
@@ -23632,7 +23842,7 @@ function createVaultIO(vaultFilename, encrypt2, decrypt2) {
23632
23842
  cachedEntries = JSON.parse(json);
23633
23843
  return cachedEntries;
23634
23844
  } catch (err) {
23635
- logger$d.error("Failed to load vault:", err);
23845
+ logger$e.error("Failed to load vault:", err);
23636
23846
  throw new Error("Could not unlock the vault. Check OS secret storage availability.");
23637
23847
  }
23638
23848
  }
@@ -23715,7 +23925,7 @@ function createAuditLog(filename, maxEntries) {
23715
23925
  } catch {
23716
23926
  }
23717
23927
  } catch (err) {
23718
- logger$d.error("Failed to write audit log:", err);
23928
+ logger$e.error("Failed to write audit log:", err);
23719
23929
  }
23720
23930
  }
23721
23931
  function readAuditLog2(limit = 100) {
@@ -23725,7 +23935,7 @@ function createAuditLog(filename, maxEntries) {
23725
23935
  const lines = fs$1.readFileSync(auditPath, "utf-8").split("\n").filter((l) => l.trim());
23726
23936
  return lines.slice(-Math.min(limit, maxEntries)).map((line) => JSON.parse(line)).reverse();
23727
23937
  } catch (err) {
23728
- logger$d.error("Failed to read audit log:", err);
23938
+ logger$e.error("Failed to read audit log:", err);
23729
23939
  return [];
23730
23940
  }
23731
23941
  }
@@ -23829,7 +24039,7 @@ async function requestConsent(request) {
23829
24039
  }
23830
24040
  const AUDIT_FILENAME = "vessel-vault-audit.jsonl";
23831
24041
  const MAX_ENTRIES = 1e3;
23832
- const logger$c = createLogger("VaultAudit");
24042
+ const logger$d = createLogger("VaultAudit");
23833
24043
  function getAuditPath() {
23834
24044
  return path$1.join(electron.app.getPath("userData"), AUDIT_FILENAME);
23835
24045
  }
@@ -23843,7 +24053,7 @@ function appendAuditEntry(entry) {
23843
24053
  });
23844
24054
  fs$1.chmodSync(auditPath, 384);
23845
24055
  } catch (err) {
23846
- logger$c.error("Failed to write audit log:", err);
24056
+ logger$d.error("Failed to write audit log:", err);
23847
24057
  }
23848
24058
  }
23849
24059
  function readAuditLog$1(limit = 100) {
@@ -23853,7 +24063,7 @@ function readAuditLog$1(limit = 100) {
23853
24063
  const lines = fs$1.readFileSync(auditPath, "utf-8").split("\n").filter((l) => l.trim());
23854
24064
  return lines.slice(-Math.min(limit, MAX_ENTRIES)).map((line) => JSON.parse(line)).reverse();
23855
24065
  } catch (err) {
23856
- logger$c.error("Failed to read audit log:", err);
24066
+ logger$d.error("Failed to read audit log:", err);
23857
24067
  return [];
23858
24068
  }
23859
24069
  }
@@ -24023,7 +24233,7 @@ const mcpRuntimeState = {
24023
24233
  httpServer: null,
24024
24234
  authToken: null
24025
24235
  };
24026
- const logger$b = createLogger("MCP");
24236
+ const logger$c = createLogger("MCP");
24027
24237
  const MCP_AUTH_FILENAME = "mcp-auth.json";
24028
24238
  function getMcpAuthFilePath() {
24029
24239
  const configDir = process.env.VESSEL_CONFIG_DIR || path$1.join(
@@ -24060,7 +24270,7 @@ function writeMcpAuthFile(endpoint, token) {
24060
24270
  );
24061
24271
  fs$1.chmodSync(filePath2, 384);
24062
24272
  } catch (err) {
24063
- logger$b.warn("Failed to write auth file:", err);
24273
+ logger$c.warn("Failed to write auth file:", err);
24064
24274
  }
24065
24275
  }
24066
24276
  function clearMcpAuthFile() {
@@ -24086,7 +24296,7 @@ function clearMcpAuthFile() {
24086
24296
  );
24087
24297
  fs$1.chmodSync(filePath2, 384);
24088
24298
  } catch (err) {
24089
- logger$b.warn("Failed to clear auth file:", err);
24299
+ logger$c.warn("Failed to clear auth file:", err);
24090
24300
  }
24091
24301
  }
24092
24302
  function regenerateMcpAuthToken() {
@@ -24096,7 +24306,7 @@ function regenerateMcpAuthToken() {
24096
24306
  writeMcpAuthFile(endpoint, mcpRuntimeState.authToken);
24097
24307
  return { endpoint };
24098
24308
  }
24099
- const logger$a = createLogger("MCP");
24309
+ const logger$b = createLogger("MCP");
24100
24310
  function registerTools(server, tabManager, runtime2) {
24101
24311
  server.registerPrompt(
24102
24312
  "vessel-supervisor-brief",
@@ -24175,7 +24385,7 @@ function registerTools(server, tabManager, runtime2) {
24175
24385
  const page = await extractContent$1(wc);
24176
24386
  pageType = detectPageType(page);
24177
24387
  } catch (err) {
24178
- logger$a.warn("Failed to detect page type for tool scoring, falling back to GENERAL:", err);
24388
+ logger$b.warn("Failed to detect page type for tool scoring, falling back to GENERAL:", err);
24179
24389
  }
24180
24390
  }
24181
24391
  const scored = TOOL_DEFINITIONS.map((def) => {
@@ -25463,7 +25673,7 @@ ${JSON.stringify(otherHighlights, null, 2)}`
25463
25673
  void 0,
25464
25674
  h.color
25465
25675
  ).catch(
25466
- (err) => logger$a.warn("Failed to restore highlight after removal:", err)
25676
+ (err) => logger$b.warn("Failed to restore highlight after removal:", err)
25467
25677
  );
25468
25678
  }
25469
25679
  }
@@ -25730,7 +25940,7 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
25730
25940
  try {
25731
25941
  page = await extractContent$1(wc);
25732
25942
  } catch (err) {
25733
- logger$a.warn("Failed to extract page while generating suggestions:", err);
25943
+ logger$b.warn("Failed to extract page while generating suggestions:", err);
25734
25944
  return asTextResponse(
25735
25945
  "Could not read page. Try navigate to a working URL."
25736
25946
  );
@@ -26340,7 +26550,7 @@ ${JSON.stringify(tableJson, null, 2)}`;
26340
26550
  try {
26341
26551
  targetDomain = new URL(tab.state.url).hostname;
26342
26552
  } catch (err) {
26343
- logger$a.warn("Failed to parse active tab URL for vault_status:", err);
26553
+ logger$b.warn("Failed to parse active tab URL for vault_status:", err);
26344
26554
  return asErrorTextResponse("Could not parse active tab URL");
26345
26555
  }
26346
26556
  }
@@ -26408,7 +26618,7 @@ Use vault_login to fill the login form. Credentials are filled directly — you
26408
26618
  try {
26409
26619
  hostname = new URL(tab.state.url).hostname;
26410
26620
  } catch (err) {
26411
- logger$a.warn("Failed to parse active tab URL for vault_login:", err);
26621
+ logger$b.warn("Failed to parse active tab URL for vault_login:", err);
26412
26622
  return asErrorTextResponse("Could not parse active tab URL");
26413
26623
  }
26414
26624
  const matches = findEntriesForDomain(`https://${hostname}`);
@@ -26504,7 +26714,7 @@ Use vault_login to fill the login form. Credentials are filled directly — you
26504
26714
  try {
26505
26715
  hostname = new URL(tab.state.url).hostname;
26506
26716
  } catch (err) {
26507
- logger$a.warn("Failed to parse active tab URL for vault_totp:", err);
26717
+ logger$b.warn("Failed to parse active tab URL for vault_totp:", err);
26508
26718
  return asErrorTextResponse("Could not parse active tab URL");
26509
26719
  }
26510
26720
  const matches = findEntriesForDomain(`https://${hostname}`);
@@ -26852,7 +27062,7 @@ function startMcpServer(tabManager, runtime2, port) {
26852
27062
  await mcpServer.connect(transport);
26853
27063
  await transport.handleRequest(req, res);
26854
27064
  } catch (error) {
26855
- logger$a.error("Error handling request:", error);
27065
+ logger$b.error("Error handling request:", error);
26856
27066
  if (!res.headersSent) {
26857
27067
  res.writeHead(500, { "Content-Type": "application/json" });
26858
27068
  res.end(
@@ -26871,7 +27081,7 @@ function startMcpServer(tabManager, runtime2, port) {
26871
27081
  };
26872
27082
  server.once("error", (error) => {
26873
27083
  const message = error.code === "EADDRINUSE" ? `Port ${port} is already in use. MCP server not started.` : error.message;
26874
- logger$a.error("Server error:", error);
27084
+ logger$b.error("Server error:", error);
26875
27085
  clearMcpAuthFile();
26876
27086
  setMcpHealth({
26877
27087
  configuredPort: port,
@@ -26903,7 +27113,7 @@ function startMcpServer(tabManager, runtime2, port) {
26903
27113
  message: `MCP server listening on ${endpoint}.`
26904
27114
  });
26905
27115
  if (process.env.VESSEL_DEBUG_MCP === "1" || process.env.VESSEL_DEBUG_MCP === "true") {
26906
- logger$a.info(`Server listening on ${endpoint} (auth enabled)`);
27116
+ logger$b.info(`Server listening on ${endpoint} (auth enabled)`);
26907
27117
  }
26908
27118
  if (mcpRuntimeState.authToken) {
26909
27119
  writeMcpAuthFile(endpoint, mcpRuntimeState.authToken);
@@ -26942,12 +27152,13 @@ function stopMcpServer() {
26942
27152
  message: "MCP server is stopped."
26943
27153
  });
26944
27154
  if (process.env.VESSEL_DEBUG_MCP === "1" || process.env.VESSEL_DEBUG_MCP === "true") {
26945
- logger$a.info("Server stopped");
27155
+ logger$b.info("Server stopped");
26946
27156
  }
26947
27157
  resolve();
26948
27158
  });
26949
27159
  });
26950
27160
  }
27161
+ const logger$a = createLogger("Feedback");
26951
27162
  const SUPPORT_API = process.env.VESSEL_SUPPORT_API || process.env.VESSEL_PREMIUM_API || "https://vesselpremium.quantaintellect.com";
26952
27163
  const MAX_FEEDBACK_MESSAGE_LENGTH = 5e3;
26953
27164
  const FEEDBACK_REQUEST_TIMEOUT_MS = 15e3;
@@ -26983,7 +27194,11 @@ async function submitFeedback(payload) {
26983
27194
  source: payload.source
26984
27195
  })
26985
27196
  });
26986
- const data = await res.json().catch(() => ({}));
27197
+ const data = await readJsonResponse(
27198
+ res,
27199
+ {},
27200
+ (msg) => logger$a.warn("Failed to parse feedback response:", msg)
27201
+ );
26987
27202
  if (!res.ok) {
26988
27203
  return errorResult(data.error || `HTTP ${res.status}`);
26989
27204
  }
@@ -27010,7 +27225,8 @@ function registerSettingsHandlers(tabManager, runtime2, sendToRendererViews, get
27010
27225
  if (key2 === "chatProvider" && researchOrchestrator) {
27011
27226
  try {
27012
27227
  researchOrchestrator.setProvider(createProvider(value));
27013
- } catch {
27228
+ } catch (err) {
27229
+ console.warn("Research provider config invalid, retaining current provider:", err);
27014
27230
  }
27015
27231
  }
27016
27232
  const rendererSettings = getRendererSettings();
@@ -27271,7 +27487,8 @@ async function togglePictureInPicture(tabManager) {
27271
27487
  }
27272
27488
  })()
27273
27489
  `);
27274
- } catch {
27490
+ } catch (err) {
27491
+ console.warn("Picture-in-picture toggle failed:", err);
27275
27492
  return false;
27276
27493
  }
27277
27494
  }
@@ -27529,8 +27746,8 @@ function isValidJobData(v) {
27529
27746
  const j = v;
27530
27747
  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
27748
  }
27532
- async function fireJob(job, windowState, runtime2) {
27533
- const { chromeView, sidebarView, devtoolsPanelView, tabManager } = windowState;
27749
+ async function fireJob(job, windowState2, runtime2) {
27750
+ const { chromeView, sidebarView, devtoolsPanelView, tabManager } = windowState2;
27534
27751
  const send = (channel, ...args) => {
27535
27752
  if (!chromeView.webContents.isDestroyed())
27536
27753
  chromeView.webContents.send(channel, ...args);
@@ -27594,7 +27811,7 @@ async function fireJob(job, windowState, runtime2) {
27594
27811
  finishActivity("failed");
27595
27812
  }
27596
27813
  }
27597
- function tick(windowState, runtime2) {
27814
+ function tick(windowState2, runtime2) {
27598
27815
  if (isAIStreamActive()) return;
27599
27816
  const dueIds = jobs.filter((job) => job.enabled && /* @__PURE__ */ new Date() >= new Date(job.nextRunAt)).map((job) => job.id);
27600
27817
  if (dueIds.length === 0) return;
@@ -27603,7 +27820,7 @@ function tick(windowState, runtime2) {
27603
27820
  const fireNext = () => {
27604
27821
  if (idx >= dueIds.length) {
27605
27822
  endAIStream("scheduled");
27606
- queueMicrotask(() => tick(windowState, runtime2));
27823
+ queueMicrotask(() => tick(windowState2, runtime2));
27607
27824
  return;
27608
27825
  }
27609
27826
  const jobId = dueIds[idx++];
@@ -27625,26 +27842,26 @@ function tick(windowState, runtime2) {
27625
27842
  }
27626
27843
  saveJobs();
27627
27844
  broadcastFn?.(Channels.SCHEDULE_JOBS_UPDATE, jobs);
27628
- void fireJob(job, windowState, runtime2).catch((err) => {
27845
+ void fireJob(job, windowState2, runtime2).catch((err) => {
27629
27846
  logger$8.warn("Unexpected error firing job:", err);
27630
27847
  }).finally(fireNext);
27631
27848
  };
27632
27849
  fireNext();
27633
27850
  }
27634
- function registerScheduleHandlers(windowState, runtime2, sendToAll) {
27851
+ function registerScheduleHandlers(windowState2, runtime2, sendToAll) {
27635
27852
  stopScheduler();
27636
27853
  broadcastFn = sendToAll;
27637
27854
  loadJobs();
27638
27855
  if (normalizeJobs()) {
27639
27856
  saveJobs();
27640
27857
  }
27641
- removeIdleListener = onAIStreamIdle(() => tick(windowState, runtime2));
27858
+ removeIdleListener = onAIStreamIdle(() => tick(windowState2, runtime2));
27642
27859
  const now = /* @__PURE__ */ new Date();
27643
27860
  const msToNextMinute = (60 - now.getSeconds()) * 1e3 - now.getMilliseconds();
27644
27861
  alignStartTimeout = setTimeout(() => {
27645
27862
  alignStartTimeout = null;
27646
- tick(windowState, runtime2);
27647
- pollInterval = setInterval(() => tick(windowState, runtime2), 6e4);
27863
+ tick(windowState2, runtime2);
27864
+ pollInterval = setInterval(() => tick(windowState2, runtime2), 6e4);
27648
27865
  }, msToNextMinute);
27649
27866
  electron.ipcMain.handle(Channels.SCHEDULE_GET_ALL, (event) => {
27650
27867
  assertTrustedIpcSender(event);
@@ -27722,19 +27939,19 @@ function stopScheduler() {
27722
27939
  }
27723
27940
  const KitIdSchema = zod.z.string().min(1);
27724
27941
  const OriginSchema = zod.z.string().min(1);
27725
- function registerSystemHandlers(windowState, sendToRendererViews) {
27726
- const { tabManager } = windowState;
27942
+ function registerSystemHandlers(windowState2, sendToRendererViews) {
27943
+ const { tabManager } = windowState2;
27727
27944
  electron.ipcMain.handle(Channels.DEVTOOLS_PANEL_TOGGLE, (event) => {
27728
27945
  assertTrustedIpcSender(event);
27729
- windowState.uiState.devtoolsPanelOpen = !windowState.uiState.devtoolsPanelOpen;
27730
- layoutViews(windowState);
27731
- return { open: windowState.uiState.devtoolsPanelOpen };
27946
+ windowState2.uiState.devtoolsPanelOpen = !windowState2.uiState.devtoolsPanelOpen;
27947
+ layoutViews(windowState2);
27948
+ return { open: windowState2.uiState.devtoolsPanelOpen };
27732
27949
  });
27733
27950
  electron.ipcMain.handle(Channels.DEVTOOLS_PANEL_RESIZE, (event, height) => {
27734
27951
  assertTrustedIpcSender(event);
27735
27952
  const clamped = Math.max(MIN_DEVTOOLS_PANEL, Math.min(MAX_DEVTOOLS_PANEL, Math.round(height)));
27736
- windowState.uiState.devtoolsPanelHeight = clamped;
27737
- layoutViews(windowState);
27953
+ windowState2.uiState.devtoolsPanelHeight = clamped;
27954
+ layoutViews(windowState2);
27738
27955
  return clamped;
27739
27956
  });
27740
27957
  electron.ipcMain.handle(Channels.AUTOMATION_GET_INSTALLED, (event) => {
@@ -28484,10 +28701,10 @@ function registerOpenRouterHandlers(applySettingChange) {
28484
28701
  return { ok: true };
28485
28702
  });
28486
28703
  }
28487
- function registerSidebarHandlers(windowState, requireTrusted) {
28704
+ function registerSidebarHandlers(windowState2, requireTrusted) {
28488
28705
  let sidebarResizeRecoveryTimer = null;
28489
28706
  let sidebarResizeActive = false;
28490
- const relayout = () => layoutViews(windowState);
28707
+ const relayout = () => layoutViews(windowState2);
28491
28708
  const clearSidebarResizeRecoveryTimer = () => {
28492
28709
  if (!sidebarResizeRecoveryTimer) return;
28493
28710
  clearTimeout(sidebarResizeRecoveryTimer);
@@ -28509,32 +28726,32 @@ function registerSidebarHandlers(windowState, requireTrusted) {
28509
28726
  restoreSidebarLayoutAfterResize();
28510
28727
  }, 1200);
28511
28728
  };
28512
- windowState.mainWindow.once("closed", stopSidebarResize);
28729
+ windowState2.mainWindow.once("closed", stopSidebarResize);
28513
28730
  electron.ipcMain.handle(Channels.SIDEBAR_TOGGLE, (event) => {
28514
28731
  requireTrusted(event);
28515
- return toggleDockedSidebar(windowState, relayout);
28732
+ return toggleDockedSidebar(windowState2, relayout);
28516
28733
  });
28517
28734
  electron.ipcMain.handle(Channels.SIDEBAR_NAVIGATE, (event, tab) => {
28518
28735
  requireTrusted(event);
28519
28736
  assertString(tab, "tab");
28520
- if (windowState.uiState.sidebarPanelMode === "closed") {
28521
- openDockedSidebar(windowState, relayout);
28737
+ if (windowState2.uiState.sidebarPanelMode === "closed") {
28738
+ openDockedSidebar(windowState2, relayout);
28522
28739
  }
28523
- if (!windowState.sidebarView.webContents.isDestroyed()) {
28524
- windowState.sidebarView.webContents.send(Channels.SIDEBAR_NAVIGATE, tab);
28740
+ if (!windowState2.sidebarView.webContents.isDestroyed()) {
28741
+ windowState2.sidebarView.webContents.send(Channels.SIDEBAR_NAVIGATE, tab);
28525
28742
  }
28526
- windowState.sidebarWindow?.focus();
28527
- return emitSidebarPanelState(windowState);
28743
+ windowState2.sidebarWindow?.focus();
28744
+ return emitSidebarPanelState(windowState2);
28528
28745
  });
28529
28746
  electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_START, (event) => {
28530
28747
  requireTrusted(event);
28531
- if (isSidebarDetached(windowState)) return;
28748
+ if (isSidebarDetached(windowState2)) return;
28532
28749
  sidebarResizeActive = true;
28533
28750
  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({
28751
+ const [width, height] = windowState2.mainWindow.getContentSize();
28752
+ const chromeHeight = windowState2.uiState.focusMode ? 0 : CHROME_HEIGHT;
28753
+ const sidebarWidth = windowState2.uiState.sidebarWidth;
28754
+ windowState2.sidebarView.setBounds({
28538
28755
  x: width - sidebarWidth - SIDEBAR_RESIZE_HANDLE_OVERLAP,
28539
28756
  y: chromeHeight,
28540
28757
  width: sidebarWidth + SIDEBAR_RESIZE_HANDLE_OVERLAP,
@@ -28545,26 +28762,26 @@ function registerSidebarHandlers(windowState, requireTrusted) {
28545
28762
  electron.ipcMain.handle(Channels.SIDEBAR_RESIZE, (event, width) => {
28546
28763
  requireTrusted(event);
28547
28764
  assertNumber(width, "width");
28548
- if (isSidebarDetached(windowState)) {
28549
- return windowState.uiState.sidebarWidth;
28765
+ if (isSidebarDetached(windowState2)) {
28766
+ return windowState2.uiState.sidebarWidth;
28550
28767
  }
28551
28768
  const clamped = clampSidebarWidth(width);
28552
- windowState.uiState.sidebarWidth = clamped;
28553
- resizeSidebarViews(windowState);
28554
- emitSidebarPanelState(windowState);
28769
+ windowState2.uiState.sidebarWidth = clamped;
28770
+ resizeSidebarViews(windowState2);
28771
+ emitSidebarPanelState(windowState2);
28555
28772
  return clamped;
28556
28773
  });
28557
28774
  electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_COMMIT, (event) => {
28558
28775
  requireTrusted(event);
28559
- if (isSidebarDetached(windowState)) return;
28776
+ if (isSidebarDetached(windowState2)) return;
28560
28777
  stopSidebarResize();
28561
- setSetting("sidebarWidth", windowState.uiState.sidebarWidth);
28778
+ setSetting("sidebarWidth", windowState2.uiState.sidebarWidth);
28562
28779
  relayout();
28563
28780
  });
28564
28781
  electron.ipcMain.handle(Channels.SIDEBAR_POPOUT, (event) => {
28565
28782
  requireTrusted(event);
28566
28783
  stopSidebarResize();
28567
- return detachSidebar(windowState, {
28784
+ return detachSidebar(windowState2, {
28568
28785
  relayout,
28569
28786
  getWindowIconPath
28570
28787
  });
@@ -28572,26 +28789,26 @@ function registerSidebarHandlers(windowState, requireTrusted) {
28572
28789
  electron.ipcMain.handle(Channels.SIDEBAR_DOCK, (event) => {
28573
28790
  requireTrusted(event);
28574
28791
  stopSidebarResize();
28575
- return dockSidebar(windowState, { relayout });
28792
+ return dockSidebar(windowState2, { relayout });
28576
28793
  });
28577
28794
  electron.ipcMain.on(
28578
28795
  Channels.RENDERER_VIEW_READY,
28579
28796
  (event, view) => {
28580
28797
  requireTrusted(event);
28581
28798
  if (view !== "sidebar") return;
28582
- emitSidebarPanelState(windowState);
28799
+ emitSidebarPanelState(windowState2);
28583
28800
  }
28584
28801
  );
28585
28802
  electron.ipcMain.handle(Channels.SETTINGS_VISIBILITY, (event, open) => {
28586
28803
  requireTrusted(event);
28587
- windowState.uiState.settingsOpen = open;
28804
+ windowState2.uiState.settingsOpen = open;
28588
28805
  if (open) {
28589
- closeSidebar(windowState, relayout, "temporary");
28806
+ closeSidebar(windowState2, relayout, "temporary");
28590
28807
  } else {
28591
28808
  relayout();
28592
- emitSidebarPanelState(windowState);
28809
+ emitSidebarPanelState(windowState2);
28593
28810
  }
28594
- return windowState.uiState.settingsOpen;
28811
+ return windowState2.uiState.settingsOpen;
28595
28812
  });
28596
28813
  }
28597
28814
  function assertVaultUnlocked() {
@@ -29131,7 +29348,7 @@ function sanitizeAutofillUpdates(value) {
29131
29348
  }
29132
29349
  return updates;
29133
29350
  }
29134
- function registerAutofillHandlers(windowState) {
29351
+ function registerAutofillHandlers(windowState2) {
29135
29352
  electron.ipcMain.handle(Channels.AUTOFILL_LIST, (event) => {
29136
29353
  assertTrustedIpcSender(event);
29137
29354
  return listProfiles();
@@ -29158,7 +29375,7 @@ function registerAutofillHandlers(windowState) {
29158
29375
  assertString(profileId, "profileId");
29159
29376
  const profile = getProfile(profileId);
29160
29377
  if (!profile) throw new Error("Profile not found");
29161
- const activeTab = windowState.tabManager.getActiveTab();
29378
+ const activeTab = windowState2.tabManager.getActiveTab();
29162
29379
  const wc = activeTab?.view.webContents;
29163
29380
  if (!wc) throw new Error("No active tab");
29164
29381
  const content = await extractContent$1(wc);
@@ -29188,9 +29405,9 @@ function registerAutofillHandlers(windowState) {
29188
29405
  };
29189
29406
  });
29190
29407
  }
29191
- function registerPageDiffHandlers(windowState, sendToRendererViews) {
29408
+ function registerPageDiffHandlers(windowState2, sendToRendererViews) {
29192
29409
  const pageEventBuckets = /* @__PURE__ */ new Map();
29193
- const isActiveWebContents = (webContentsId) => windowState.tabManager.getActiveTab()?.view.webContents.id === webContentsId;
29410
+ const isActiveWebContents = (webContentsId) => windowState2.tabManager.getActiveTab()?.view.webContents.id === webContentsId;
29194
29411
  const allowPageEvent = (webContentsId) => {
29195
29412
  const now = Date.now();
29196
29413
  const bucket = pageEventBuckets.get(webContentsId);
@@ -29203,7 +29420,7 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
29203
29420
  };
29204
29421
  electron.ipcMain.handle(Channels.PAGE_DIFF_GET, (event) => {
29205
29422
  assertTrustedIpcSender(event);
29206
- const activeTab = windowState.tabManager.getActiveTab();
29423
+ const activeTab = windowState2.tabManager.getActiveTab();
29207
29424
  const wc = activeTab?.view.webContents;
29208
29425
  if (!wc) return null;
29209
29426
  return getLatestPageDiff(wc.getURL());
@@ -29214,7 +29431,7 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
29214
29431
  if (!isPremiumActiveState(getPremiumState())) {
29215
29432
  return { error: "Premium required" };
29216
29433
  }
29217
- const activeTab = windowState.tabManager.getActiveTab();
29434
+ const activeTab = windowState2.tabManager.getActiveTab();
29218
29435
  const wc = activeTab?.view.webContents;
29219
29436
  if (!wc) return [];
29220
29437
  return getPageDiffBursts(wc.getURL());
@@ -29225,7 +29442,7 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
29225
29442
  electron.ipcMain.on(Channels.PAGE_DIFF_ACTIVITY, (event) => {
29226
29443
  const wc = event.sender;
29227
29444
  if (!wc || wc.isDestroyed()) return;
29228
- if (!isManagedTabIpcSender(event, windowState.tabManager)) return;
29445
+ if (!isManagedTabIpcSender(event, windowState2.tabManager)) return;
29229
29446
  if (!allowPageEvent(wc.id)) return;
29230
29447
  invalidateExtractionCache(wc);
29231
29448
  notePageMutationActivity(wc, sendToRendererViews, {
@@ -29235,7 +29452,7 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
29235
29452
  electron.ipcMain.on(Channels.PAGE_DIFF_DIRTY, (event) => {
29236
29453
  const wc = event.sender;
29237
29454
  if (!wc || wc.isDestroyed()) return;
29238
- if (!isManagedTabIpcSender(event, windowState.tabManager)) return;
29455
+ if (!isManagedTabIpcSender(event, windowState2.tabManager)) return;
29239
29456
  if (!allowPageEvent(wc.id)) return;
29240
29457
  invalidateExtractionCache(wc);
29241
29458
  schedulePageSnapshotCapture(wc, sendToRendererViews, 0, {
@@ -29303,12 +29520,14 @@ function renderReportAsMarkdown(report, traces) {
29303
29520
  }
29304
29521
  const logger$4 = createLogger("ResearchIPC");
29305
29522
  function registerResearchHandlers(getOrchestrator) {
29306
- electron.ipcMain.handle(Channels.RESEARCH_STATE_GET, () => {
29523
+ electron.ipcMain.handle(Channels.RESEARCH_STATE_GET, (event) => {
29524
+ assertTrustedIpcSender(event);
29307
29525
  return getOrchestrator().getState();
29308
29526
  });
29309
29527
  electron.ipcMain.handle(
29310
29528
  Channels.RESEARCH_START_BRIEF,
29311
- async (_event, query) => {
29529
+ async (event, query) => {
29530
+ assertTrustedIpcSender(event);
29312
29531
  try {
29313
29532
  const trimmedQuery = query.trim();
29314
29533
  if (!trimmedQuery) {
@@ -29325,7 +29544,8 @@ function registerResearchHandlers(getOrchestrator) {
29325
29544
  }
29326
29545
  }
29327
29546
  );
29328
- electron.ipcMain.handle(Channels.RESEARCH_CONFIRM_BRIEF, () => {
29547
+ electron.ipcMain.handle(Channels.RESEARCH_CONFIRM_BRIEF, (event) => {
29548
+ assertTrustedIpcSender(event);
29329
29549
  try {
29330
29550
  if (isToolGated("research_confirm_brief")) {
29331
29551
  return { accepted: false, reason: "premium" };
@@ -29343,7 +29563,8 @@ function registerResearchHandlers(getOrchestrator) {
29343
29563
  });
29344
29564
  electron.ipcMain.handle(
29345
29565
  Channels.RESEARCH_APPROVE_OBJECTIVES,
29346
- (_event, options) => {
29566
+ (event, options) => {
29567
+ assertTrustedIpcSender(event);
29347
29568
  try {
29348
29569
  if (isToolGated("research_approve_objectives")) {
29349
29570
  return { accepted: false, reason: "premium" };
@@ -29369,23 +29590,28 @@ function registerResearchHandlers(getOrchestrator) {
29369
29590
  );
29370
29591
  electron.ipcMain.handle(
29371
29592
  Channels.RESEARCH_SET_MODE,
29372
- (_event, mode) => {
29593
+ (event, mode) => {
29594
+ assertTrustedIpcSender(event);
29373
29595
  getOrchestrator().setSupervisionMode(mode);
29374
29596
  }
29375
29597
  );
29376
29598
  electron.ipcMain.handle(
29377
29599
  Channels.RESEARCH_SET_TRACES,
29378
- (_event, include) => {
29600
+ (event, include) => {
29601
+ assertTrustedIpcSender(event);
29379
29602
  getOrchestrator().setIncludeTraces(include);
29380
29603
  }
29381
29604
  );
29382
- electron.ipcMain.handle(Channels.RESEARCH_CANCEL, () => {
29605
+ electron.ipcMain.handle(Channels.RESEARCH_CANCEL, (event) => {
29606
+ assertTrustedIpcSender(event);
29383
29607
  getOrchestrator().cancel();
29384
29608
  });
29385
- electron.ipcMain.handle(Channels.RESEARCH_STOP_AND_SYNTHESIZE, () => {
29609
+ electron.ipcMain.handle(Channels.RESEARCH_STOP_AND_SYNTHESIZE, (event) => {
29610
+ assertTrustedIpcSender(event);
29386
29611
  getOrchestrator().stopAndSynthesizeCurrentFindings();
29387
29612
  });
29388
- electron.ipcMain.handle(Channels.RESEARCH_EXPORT_REPORT, async () => {
29613
+ electron.ipcMain.handle(Channels.RESEARCH_EXPORT_REPORT, async (event) => {
29614
+ assertTrustedIpcSender(event);
29389
29615
  try {
29390
29616
  if (isToolGated("research_export_report")) {
29391
29617
  return { accepted: false, reason: "premium" };
@@ -29417,16 +29643,16 @@ function registerResearchHandlers(getOrchestrator) {
29417
29643
  }
29418
29644
  });
29419
29645
  }
29420
- function registerIpcHandlers(windowState, runtime2) {
29421
- const { tabManager, chromeView, sidebarView, devtoolsPanelView, mainWindow } = windowState;
29646
+ function registerIpcHandlers(windowState2, runtime2) {
29647
+ const { tabManager, chromeView, sidebarView, devtoolsPanelView, mainWindow } = windowState2;
29422
29648
  registerTrustedIpcSender(chromeView.webContents);
29423
29649
  registerTrustedIpcSender(sidebarView.webContents);
29424
29650
  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
- };
29651
+ const sendToRendererViews = createWindowStateMessenger(
29652
+ chromeView,
29653
+ sidebarView,
29654
+ devtoolsPanelView
29655
+ );
29430
29656
  let researchOrchestrator = null;
29431
29657
  const getResearchOrchestrator = () => {
29432
29658
  if (!researchOrchestrator) {
@@ -29440,10 +29666,10 @@ function registerIpcHandlers(windowState, runtime2) {
29440
29666
  return researchOrchestrator;
29441
29667
  };
29442
29668
  const getExistingResearchOrchestrator = () => researchOrchestrator;
29443
- registerTabHandlers(windowState);
29669
+ registerTabHandlers(windowState2);
29444
29670
  registerAIHandlers(tabManager, runtime2, sendToRendererViews, getResearchOrchestrator);
29445
- registerContentHandlers(windowState);
29446
- registerHighlightHandlers(windowState, sendToRendererViews);
29671
+ registerContentHandlers(windowState2);
29672
+ registerHighlightHandlers(windowState2, sendToRendererViews);
29447
29673
  registerAgentRuntimeHandlers(
29448
29674
  runtime2,
29449
29675
  chromeView.webContents,
@@ -29463,15 +29689,15 @@ function registerIpcHandlers(windowState, runtime2) {
29463
29689
  registerSecurityHandlers(tabManager);
29464
29690
  registerCodexHandlers();
29465
29691
  registerOpenRouterHandlers(applySettingChange);
29466
- registerSidebarHandlers(windowState, (event) => assertTrustedIpcSender(event));
29692
+ registerSidebarHandlers(windowState2, (event) => assertTrustedIpcSender(event));
29467
29693
  registerVaultHandlers();
29468
29694
  registerHumanVaultHandlers();
29469
29695
  registerWindowControlHandlers(mainWindow);
29470
- registerAutofillHandlers(windowState);
29471
- registerPageDiffHandlers(windowState, sendToRendererViews);
29696
+ registerAutofillHandlers(windowState2);
29697
+ registerPageDiffHandlers(windowState2, sendToRendererViews);
29472
29698
  registerResearchHandlers(() => getResearchOrchestrator());
29473
- registerScheduleHandlers(windowState, runtime2, sendToRendererViews);
29474
- registerSystemHandlers(windowState, sendToRendererViews);
29699
+ registerScheduleHandlers(windowState2, runtime2, sendToRendererViews);
29700
+ registerSystemHandlers(windowState2, sendToRendererViews);
29475
29701
  }
29476
29702
  const UNDOABLE_ACTIONS = /* @__PURE__ */ new Set([
29477
29703
  "accept_cookies",
@@ -29912,19 +30138,39 @@ class AgentRuntime {
29912
30138
  constructor(tabManager) {
29913
30139
  this.tabManager = tabManager;
29914
30140
  this.state = this.loadPersistedState();
29915
- onMcpStatusChange(() => this.emit());
30141
+ this.mcpUnsubscribe = onMcpStatusChange(() => this.emit());
29916
30142
  }
29917
30143
  tabManager;
29918
30144
  state;
29919
30145
  updateListener = null;
29920
30146
  pendingResolvers = /* @__PURE__ */ new Map();
29921
30147
  undoSnapshots = [];
30148
+ mcpUnsubscribe = null;
29922
30149
  setUpdateListener(listener) {
29923
30150
  this.updateListener = listener;
29924
30151
  if (listener) {
29925
30152
  listener(this.getState());
29926
30153
  }
29927
30154
  }
30155
+ /**
30156
+ * Release all resources, listeners, and pending promises.
30157
+ * Call when the window is closing to prevent memory leaks.
30158
+ */
30159
+ dispose() {
30160
+ this.mcpUnsubscribe?.();
30161
+ this.mcpUnsubscribe = null;
30162
+ for (const [id, resolve] of this.pendingResolvers) {
30163
+ resolve(false);
30164
+ this.pendingResolvers.delete(id);
30165
+ }
30166
+ this.undoSnapshots = [];
30167
+ this.state.actions = [];
30168
+ this.state.transcript = [];
30169
+ this.state.supervisor.pendingApprovals = [];
30170
+ this.state.flowState = null;
30171
+ this.state.taskTracker = null;
30172
+ this.updateListener = null;
30173
+ }
29928
30174
  getState() {
29929
30175
  const snapshot2 = clone(this.state);
29930
30176
  snapshot2.mcpStatus = getMcpStatus();
@@ -29978,6 +30224,7 @@ class AgentRuntime {
29978
30224
  -20
29979
30225
  );
29980
30226
  this.emit();
30227
+ void this.flushPersist();
29981
30228
  return clone(checkpoint);
29982
30229
  }
29983
30230
  restoreCheckpoint(checkpointId) {
@@ -30769,7 +31016,7 @@ Action: ${issue.action}` : "";
30769
31016
  return `${issue.title}
30770
31017
  ${issue.detail}${action}`;
30771
31018
  }
30772
- async function maybeShowStartupHealthDialog(windowState) {
31019
+ async function maybeShowStartupHealthDialog(windowState2) {
30773
31020
  const health = getRuntimeHealth();
30774
31021
  const hasIssues = health.startupIssues.length > 0 || health.mcp.status === "error";
30775
31022
  if (!hasIssues) return;
@@ -30781,7 +31028,7 @@ ${health.mcp.message}
30781
31028
  Action: Open Settings (Ctrl+,) to choose a different port, then save to restart the MCP server.`
30782
31029
  );
30783
31030
  }
30784
- await electron.dialog.showMessageBox(windowState.mainWindow, {
31031
+ await electron.dialog.showMessageBox(windowState2.mainWindow, {
30785
31032
  type: health.startupIssues.some((issue) => issue.severity === "error") || health.mcp.status === "error" ? "warning" : "info",
30786
31033
  title: "Vessel Startup Checks",
30787
31034
  message: "Vessel launched with runtime warnings.",
@@ -30817,10 +31064,10 @@ async function bootstrap() {
30817
31064
  sendSafe(state2.sidebarView.webContents, Channels.HIGHLIGHT_COUNT_UPDATE, count);
30818
31065
  sendSafe(state2.devtoolsPanelView.webContents, Channels.HIGHLIGHT_COUNT_UPDATE, count);
30819
31066
  };
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);
31067
+ const windowState2 = createMainWindow((tabs, activeId, meta) => {
31068
+ sendSafe(windowState2.chromeView.webContents, Channels.TAB_STATE_UPDATE, tabs, activeId);
31069
+ void syncActiveHighlightCount(windowState2);
31070
+ layoutViews(windowState2);
30824
31071
  if (meta.persistSession) {
30825
31072
  runtime?.onTabStateChanged();
30826
31073
  }
@@ -30829,7 +31076,7 @@ async function bootstrap() {
30829
31076
  const revealMainWindow = () => {
30830
31077
  if (didRevealMainWindow) return;
30831
31078
  didRevealMainWindow = true;
30832
- windowState.mainWindow.show();
31079
+ windowState2.mainWindow.show();
30833
31080
  closeSplash(splash, 0);
30834
31081
  };
30835
31082
  let didInitializeChromeRenderer = false;
@@ -30837,25 +31084,25 @@ async function bootstrap() {
30837
31084
  logger.warn("Renderer did not finish loading before splash timeout");
30838
31085
  revealMainWindow();
30839
31086
  }, 8e3);
30840
- const { chromeView, sidebarView, devtoolsPanelView, tabManager } = windowState;
31087
+ const { chromeView, sidebarView, devtoolsPanelView, tabManager } = windowState2;
30841
31088
  runtime = new AgentRuntime(tabManager);
30842
31089
  installAdBlocking(tabManager);
30843
31090
  setDevToolsPanelListener((state2) => {
30844
31091
  sendSafe(devtoolsPanelView.webContents, Channels.DEVTOOLS_PANEL_STATE, state2);
30845
31092
  });
30846
- registerIpcHandlers(windowState, runtime);
31093
+ registerIpcHandlers(windowState2, runtime);
30847
31094
  tabManager.onSecurityStateChange((tabId, state2) => {
30848
31095
  sendSafe(chromeView.webContents, Channels.SECURITY_STATE_UPDATE, { tabId, state: state2 });
30849
31096
  sendSafe(sidebarView.webContents, Channels.SECURITY_STATE_UPDATE, { tabId, state: state2 });
30850
31097
  });
30851
- registerHighlightShortcut(windowState.mainWindow, tabManager);
31098
+ registerHighlightShortcut(windowState2.mainWindow, tabManager);
30852
31099
  setupAppMenu({
30853
31100
  newWindow: () => {
30854
31101
  createSecondaryWindow();
30855
31102
  },
30856
31103
  reopenClosedTab: () => {
30857
31104
  const id = tabManager.reopenClosedTab();
30858
- if (id) layoutViews(windowState);
31105
+ if (id) layoutViews(windowState2);
30859
31106
  },
30860
31107
  zoomIn: () => {
30861
31108
  const id = tabManager.getActiveTabId();
@@ -30908,11 +31155,11 @@ async function bootstrap() {
30908
31155
  tabManager.createTab(settings2.defaultUrl);
30909
31156
  runtime.captureSession("Initial session");
30910
31157
  }
30911
- layoutViews(windowState);
30912
- setImmediate(() => layoutViews(windowState));
31158
+ layoutViews(windowState2);
31159
+ setImmediate(() => layoutViews(windowState2));
30913
31160
  clearTimeout(splashTimeout);
30914
31161
  revealMainWindow();
30915
- void maybeShowStartupHealthDialog(windowState);
31162
+ void maybeShowStartupHealthDialog(windowState2);
30916
31163
  };
30917
31164
  chromeView.webContents.once("dom-ready", () => {
30918
31165
  initializeChromeRenderer();
@@ -30957,6 +31204,8 @@ electron.app.on("window-all-closed", () => {
30957
31204
  electron.globalShortcut.unregisterAll();
30958
31205
  stopTelemetry();
30959
31206
  stopBackgroundRevalidation();
31207
+ runtime?.dispose();
31208
+ windowState?.tabManager.dispose();
30960
31209
  void Promise.all([
30961
31210
  runtime?.flushPersist() ?? Promise.resolve(),
30962
31211
  flushPersist$1(),