@quanta-intellect/vessel-browser 0.1.153 → 0.1.155

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
@@ -8084,6 +8084,22 @@ function shouldBlockOffGoalDomainNavigation(goal, targetUrl) {
8084
8084
  function hasRecentDuplicateToolCall(recentToolSignatures, signature) {
8085
8085
  return recentToolSignatures.includes(signature);
8086
8086
  }
8087
+ const DUPLICATE_TOOL_CALL_RETRYABLE_TOOLS = /* @__PURE__ */ new Set([
8088
+ "read_page",
8089
+ "current_tab",
8090
+ "inspect_element",
8091
+ "screenshot",
8092
+ "go_back",
8093
+ "go_forward",
8094
+ "click"
8095
+ ]);
8096
+ const REPEATED_TOOL_CALL_NUDGE = `[System] You are stuck repeating the same action. Stop repeating navigate/search. Use a different supported tool that advances the task, such as click, read_page, or scroll.`;
8097
+ function shouldSuppressDuplicateToolCall(recentToolSignatures, toolName, signature) {
8098
+ return !DUPLICATE_TOOL_CALL_RETRYABLE_TOOLS.has(toolName) && hasRecentDuplicateToolCall(recentToolSignatures, signature);
8099
+ }
8100
+ function buildRepeatedToolCallError(toolName) {
8101
+ return `Error: Repeated the same tool call (${toolName}) with the same arguments twice in a row. Do not repeat it. Continue with the next logical step for the original task.`;
8102
+ }
8087
8103
  function isClickReadLoop(names) {
8088
8104
  if (names.length < 6) return false;
8089
8105
  const tail = names.slice(-6);
@@ -10089,17 +10105,9 @@ class OpenAICompatProvider {
10089
10105
  compactCorrectionCount += 1;
10090
10106
  continue;
10091
10107
  }
10092
- const neverSuppressDuplicate = [
10093
- "read_page",
10094
- "current_tab",
10095
- "inspect_element",
10096
- "screenshot",
10097
- "go_back",
10098
- "go_forward",
10099
- "click"
10100
- ].includes(tc.name);
10101
- if (this.agentToolProfile === "compact" && !neverSuppressDuplicate && hasRecentDuplicateToolCall(
10108
+ if (this.agentToolProfile === "compact" && shouldSuppressDuplicateToolCall(
10102
10109
  recentCompactToolSignatures,
10110
+ tc.name,
10103
10111
  toolSignature
10104
10112
  )) {
10105
10113
  onChunk(`
@@ -10108,13 +10116,13 @@ class OpenAICompatProvider {
10108
10116
  messages.push({
10109
10117
  role: "tool",
10110
10118
  tool_call_id: tc.id,
10111
- content: `Error: Repeated the same tool call (${tc.name}) with the same arguments twice in a row. Do not repeat it. Continue with the next logical step for the original task.`
10119
+ content: buildRepeatedToolCallError(tc.name)
10112
10120
  });
10113
10121
  compactCorrectionCount += 1;
10114
10122
  if (compactCorrectionCount >= 2) {
10115
10123
  messages.push({
10116
10124
  role: "user",
10117
- content: `[System] You are stuck repeating the same action. Stop repeating navigate/search. Use a different supported tool that advances the task, such as click, read_page, or scroll.`
10125
+ content: REPEATED_TOOL_CALL_NUDGE
10118
10126
  });
10119
10127
  }
10120
10128
  continue;
@@ -11228,21 +11236,17 @@ ${latestToolResultPreview || ""}`
11228
11236
  correctionCount += 1;
11229
11237
  continue;
11230
11238
  }
11231
- if (![
11232
- "read_page",
11233
- "current_tab",
11234
- "inspect_element",
11235
- "screenshot",
11236
- "go_back",
11237
- "go_forward",
11238
- "click"
11239
- ].includes(prepared.prepared.name) && hasRecentDuplicateToolCall(recentToolSignatures, toolSignature)) {
11239
+ if (shouldSuppressDuplicateToolCall(
11240
+ recentToolSignatures,
11241
+ prepared.prepared.name,
11242
+ toolSignature
11243
+ )) {
11240
11244
  onChunk(`
11241
11245
  <<tool:${prepared.prepared.name}:↻ duplicate suppressed>>
11242
11246
  `);
11243
11247
  const output2 = createCodexToolOutput(
11244
11248
  prepared.prepared.callId,
11245
- `Error: Repeated the same tool call (${prepared.prepared.name}) with the same arguments twice in a row. Do not repeat it. Continue with the next logical step for the original task.`
11249
+ buildRepeatedToolCallError(prepared.prepared.name)
11246
11250
  );
11247
11251
  currentInput.push(output2);
11248
11252
  latestToolResultPreview = previewToolResult(output2.output);
@@ -12977,6 +12981,166 @@ function getUrlPathSegments(value) {
12977
12981
  return value.split("?")[0].split("#")[0].split("/").filter(Boolean);
12978
12982
  }
12979
12983
  }
12984
+ const SITE_RESULT_FILTERS = [
12985
+ {
12986
+ hostname: "news.ycombinator.com",
12987
+ listingPaths: [
12988
+ "/",
12989
+ "/news",
12990
+ "/newest",
12991
+ "/front",
12992
+ "/ask",
12993
+ "/show",
12994
+ "/jobs",
12995
+ "/best",
12996
+ "/active",
12997
+ "/classic",
12998
+ "/noobstories"
12999
+ ],
13000
+ utilityPathnames: ["/hide", "/user"],
13001
+ utilityTextPatterns: [
13002
+ /^(hide|past|favorite|unfavorite|flag|unflag|discuss|reply|parent|more)$/,
13003
+ /^\d+\s+(?:comments?|points?)$/
13004
+ ]
13005
+ }
13006
+ ];
13007
+ function matchesSiteFilter(url, filter, baseHostname) {
13008
+ try {
13009
+ const parsed = new URL(url, baseHostname ? `https://${baseHostname}` : void 0);
13010
+ return parsed.hostname === filter.hostname;
13011
+ } catch {
13012
+ return false;
13013
+ }
13014
+ }
13015
+ function isSiteListingPage(url) {
13016
+ for (const filter of SITE_RESULT_FILTERS) {
13017
+ if (!matchesSiteFilter(url, filter, "")) continue;
13018
+ try {
13019
+ const pathname = new URL(url).pathname.replace(/\/+$/, "") || "/";
13020
+ if (filter.listingPaths?.includes(pathname)) return true;
13021
+ } catch {
13022
+ }
13023
+ }
13024
+ return false;
13025
+ }
13026
+ function isSiteUtilityLink(element) {
13027
+ if (!element.href) return false;
13028
+ for (const filter of SITE_RESULT_FILTERS) {
13029
+ if (!matchesSiteFilter(element.href, filter, "")) continue;
13030
+ const text = normalizeComparable(element.text || "");
13031
+ for (const pattern of filter.utilityTextPatterns ?? []) {
13032
+ if (pattern.test(text)) return true;
13033
+ }
13034
+ try {
13035
+ const pathname = new URL(element.href).pathname.replace(/\/+$/, "") || "/";
13036
+ if (filter.utilityPathnames?.includes(pathname)) return true;
13037
+ } catch {
13038
+ }
13039
+ }
13040
+ return false;
13041
+ }
13042
+ function isSearchOrListingPage(page) {
13043
+ if (isSiteListingPage(page.url)) return true;
13044
+ const haystack = normalizeComparable(
13045
+ [page.url, page.title, page.excerpt, page.headings.map((heading) => heading.text).join(" ")].filter(Boolean).join(" ")
13046
+ );
13047
+ return /\b(search|results|find|discover|browse|repositories|repository|issues|pull requests|prs|users|events|listings)\b/.test(
13048
+ haystack
13049
+ );
13050
+ }
13051
+ function collectJsonLdEntityItems(input, results = []) {
13052
+ if (!input) return results;
13053
+ if (Array.isArray(input)) {
13054
+ input.forEach((item2) => collectJsonLdEntityItems(item2, results));
13055
+ return results;
13056
+ }
13057
+ if (typeof input !== "object") return results;
13058
+ const item = input;
13059
+ const type = item["@type"];
13060
+ const types = Array.isArray(type) ? type : [type];
13061
+ const typeNames = types.filter((entry) => typeof entry === "string");
13062
+ if ((typeof item.name === "string" || typeof item.url === "string") && !typeNames.some(
13063
+ (entry) => ["BreadcrumbList", "Organization", "WebSite", "WebPage"].includes(entry)
13064
+ )) {
13065
+ results.push(item);
13066
+ }
13067
+ collectJsonLdEntityItems(item["@graph"], results);
13068
+ collectJsonLdEntityItems(item.mainEntity, results);
13069
+ collectJsonLdEntityItems(item.itemListElement, results);
13070
+ collectJsonLdEntityItems(item.item, results);
13071
+ return results;
13072
+ }
13073
+ function getResultCandidates(page) {
13074
+ const entityItems = collectJsonLdEntityItems(page.jsonLd ?? []);
13075
+ const entityNames = new Set(
13076
+ entityItems.map((item) => typeof item.name === "string" ? normalizeComparable(item.name) : "").filter(Boolean)
13077
+ );
13078
+ const entityUrls = new Set(
13079
+ entityItems.map((item) => typeof item.url === "string" ? normalizeUrlForMatch(item.url) : null).filter((value) => Boolean(value))
13080
+ );
13081
+ const pageHost = normalizeUrlForMatch(page.url);
13082
+ const searchOrListingPage = isSearchOrListingPage(page);
13083
+ const scored = page.interactiveElements.filter(
13084
+ (element) => element.type === "link" && element.text?.trim() && element.href && !isSiteUtilityLink(element)
13085
+ ).map((element) => {
13086
+ const text = element.text?.trim() || "";
13087
+ const comparableText = normalizeComparable(text);
13088
+ const href = normalizeUrlForMatch(element.href);
13089
+ const haystack = normalizeComparable(
13090
+ [element.text, element.description, element.selector, element.href].filter(Boolean).join(" ")
13091
+ );
13092
+ let score = 0;
13093
+ if (entityNames.has(comparableText)) score += 6;
13094
+ if (href && entityUrls.has(href)) score += 6;
13095
+ if (entityItems.some((item) => {
13096
+ const name = typeof item.name === "string" ? normalizeComparable(item.name) : "";
13097
+ return Boolean(name) && (name.includes(comparableText) || comparableText.includes(name));
13098
+ })) {
13099
+ score += 4;
13100
+ }
13101
+ if (element.context === "article") score += 3;
13102
+ else if (element.context === "main" || element.context === "content") score += 1;
13103
+ if (href && pageHost) {
13104
+ try {
13105
+ if (new URL(href).origin === new URL(pageHost).origin) score += 1;
13106
+ } catch {
13107
+ }
13108
+ }
13109
+ const hrefSegments = getUrlPathSegments(element.href);
13110
+ if (hrefSegments.length >= 2) score += 1;
13111
+ if (text.includes("/")) score += 1;
13112
+ if (searchOrListingPage && (element.context === "article" || element.context === "main" || element.context === "content")) {
13113
+ score += 2;
13114
+ }
13115
+ if (/\b(card|tile|result|rating|review)\b/.test(haystack)) score += 1;
13116
+ if (/\b(item|list|row|repo|repository|issue|pull request|event)\b/.test(haystack)) {
13117
+ score += 1;
13118
+ }
13119
+ if (text.length >= 12 && text.split(/\s+/).length >= 2) score += 1;
13120
+ if (element.context === "nav" || element.context === "header" || element.context === "footer" || element.context === "sidebar" || element.context === "dialog") {
13121
+ score -= 5;
13122
+ }
13123
+ if (/\b(home|menu|about|contact|privacy|terms|login|sign in|sign up|subscribe|newsletter|facebook|instagram|pinterest|share|print|next|previous|prev|sort|filter|star|sponsor)\b/.test(
13124
+ comparableText
13125
+ )) {
13126
+ score -= 4;
13127
+ }
13128
+ return { element, score };
13129
+ }).filter(({ score, element }) => {
13130
+ if (entityItems.length > 0) return score >= 4;
13131
+ if (searchOrListingPage) {
13132
+ return score >= 4 || score >= 3 && (element.context === "article" || element.context === "main" || element.context === "content");
13133
+ }
13134
+ return score >= 4 || score >= 3 && element.context === "article";
13135
+ }).sort((a, b) => b.score - a.score || (a.element.index ?? 0) - (b.element.index ?? 0));
13136
+ const seen = /* @__PURE__ */ new Set();
13137
+ return scored.map(({ element }) => element).filter((element) => {
13138
+ const key2 = `${normalizeComparable(element.text || "")}|${normalizeUrlForMatch(element.href) || ""}`;
13139
+ if (seen.has(key2)) return false;
13140
+ seen.add(key2);
13141
+ return true;
13142
+ });
13143
+ }
12980
13144
  const MAX_STRUCTURED_ITEMS = 100;
12981
13145
  const LARGE_PAGE_HINT_THRESHOLD = 12e3;
12982
13146
  function truncateContent(content) {
@@ -13729,166 +13893,6 @@ const PAGE_TYPE_READ_MODE = {
13729
13893
  ARTICLE: "summary",
13730
13894
  GENERAL: "visible_only"
13731
13895
  };
13732
- const SITE_RESULT_FILTERS = [
13733
- {
13734
- hostname: "news.ycombinator.com",
13735
- listingPaths: [
13736
- "/",
13737
- "/news",
13738
- "/newest",
13739
- "/front",
13740
- "/ask",
13741
- "/show",
13742
- "/jobs",
13743
- "/best",
13744
- "/active",
13745
- "/classic",
13746
- "/noobstories"
13747
- ],
13748
- utilityPathnames: ["/hide", "/user"],
13749
- utilityTextPatterns: [
13750
- /^(hide|past|favorite|unfavorite|flag|unflag|discuss|reply|parent|more)$/,
13751
- /^\d+\s+(?:comments?|points?)$/
13752
- ]
13753
- }
13754
- ];
13755
- function matchesSiteFilter(url, filter, baseHostname) {
13756
- try {
13757
- const parsed = new URL(url, baseHostname ? `https://${baseHostname}` : void 0);
13758
- return parsed.hostname === filter.hostname;
13759
- } catch {
13760
- return false;
13761
- }
13762
- }
13763
- function isSiteListingPage(url) {
13764
- for (const filter of SITE_RESULT_FILTERS) {
13765
- if (!matchesSiteFilter(url, filter, "")) continue;
13766
- try {
13767
- const pathname = new URL(url).pathname.replace(/\/+$/, "") || "/";
13768
- if (filter.listingPaths?.includes(pathname)) return true;
13769
- } catch {
13770
- }
13771
- }
13772
- return false;
13773
- }
13774
- function isSiteUtilityLink(element) {
13775
- if (!element.href) return false;
13776
- for (const filter of SITE_RESULT_FILTERS) {
13777
- if (!matchesSiteFilter(element.href, filter, "")) continue;
13778
- const text = normalizeComparable(element.text || "");
13779
- for (const pattern of filter.utilityTextPatterns ?? []) {
13780
- if (pattern.test(text)) return true;
13781
- }
13782
- try {
13783
- const pathname = new URL(element.href).pathname.replace(/\/+$/, "") || "/";
13784
- if (filter.utilityPathnames?.includes(pathname)) return true;
13785
- } catch {
13786
- }
13787
- }
13788
- return false;
13789
- }
13790
- function isSearchOrListingPage(page) {
13791
- if (isSiteListingPage(page.url)) return true;
13792
- const haystack = normalizeComparable(
13793
- [page.url, page.title, page.excerpt, page.headings.map((heading) => heading.text).join(" ")].filter(Boolean).join(" ")
13794
- );
13795
- return /\b(search|results|find|discover|browse|repositories|repository|issues|pull requests|prs|users|events|listings)\b/.test(
13796
- haystack
13797
- );
13798
- }
13799
- function collectJsonLdEntityItems(input, results = []) {
13800
- if (!input) return results;
13801
- if (Array.isArray(input)) {
13802
- input.forEach((item2) => collectJsonLdEntityItems(item2, results));
13803
- return results;
13804
- }
13805
- if (typeof input !== "object") return results;
13806
- const item = input;
13807
- const type = item["@type"];
13808
- const types = Array.isArray(type) ? type : [type];
13809
- const typeNames = types.filter((entry) => typeof entry === "string");
13810
- if ((typeof item.name === "string" || typeof item.url === "string") && !typeNames.some(
13811
- (entry) => ["BreadcrumbList", "Organization", "WebSite", "WebPage"].includes(entry)
13812
- )) {
13813
- results.push(item);
13814
- }
13815
- collectJsonLdEntityItems(item["@graph"], results);
13816
- collectJsonLdEntityItems(item.mainEntity, results);
13817
- collectJsonLdEntityItems(item.itemListElement, results);
13818
- collectJsonLdEntityItems(item.item, results);
13819
- return results;
13820
- }
13821
- function getResultCandidates(page) {
13822
- const entityItems = collectJsonLdEntityItems(page.jsonLd ?? []);
13823
- const entityNames = new Set(
13824
- entityItems.map((item) => typeof item.name === "string" ? normalizeComparable(item.name) : "").filter(Boolean)
13825
- );
13826
- const entityUrls = new Set(
13827
- entityItems.map((item) => typeof item.url === "string" ? normalizeUrlForMatch(item.url) : null).filter((value) => Boolean(value))
13828
- );
13829
- const pageHost = normalizeUrlForMatch(page.url);
13830
- const searchOrListingPage = isSearchOrListingPage(page);
13831
- const scored = page.interactiveElements.filter(
13832
- (element) => element.type === "link" && element.text?.trim() && element.href && !isSiteUtilityLink(element)
13833
- ).map((element) => {
13834
- const text = element.text?.trim() || "";
13835
- const comparableText = normalizeComparable(text);
13836
- const href = normalizeUrlForMatch(element.href);
13837
- const haystack = normalizeComparable(
13838
- [element.text, element.description, element.selector, element.href].filter(Boolean).join(" ")
13839
- );
13840
- let score = 0;
13841
- if (entityNames.has(comparableText)) score += 6;
13842
- if (href && entityUrls.has(href)) score += 6;
13843
- if (entityItems.some((item) => {
13844
- const name = typeof item.name === "string" ? normalizeComparable(item.name) : "";
13845
- return Boolean(name) && (name.includes(comparableText) || comparableText.includes(name));
13846
- })) {
13847
- score += 4;
13848
- }
13849
- if (element.context === "article") score += 3;
13850
- else if (element.context === "main" || element.context === "content") score += 1;
13851
- if (href && pageHost) {
13852
- try {
13853
- if (new URL(href).origin === new URL(pageHost).origin) score += 1;
13854
- } catch {
13855
- }
13856
- }
13857
- const hrefSegments = getUrlPathSegments(element.href);
13858
- if (hrefSegments.length >= 2) score += 1;
13859
- if (text.includes("/")) score += 1;
13860
- if (searchOrListingPage && (element.context === "article" || element.context === "main" || element.context === "content")) {
13861
- score += 2;
13862
- }
13863
- if (/\b(card|tile|result|rating|review)\b/.test(haystack)) score += 1;
13864
- if (/\b(item|list|row|repo|repository|issue|pull request|event)\b/.test(haystack)) {
13865
- score += 1;
13866
- }
13867
- if (text.length >= 12 && text.split(/\s+/).length >= 2) score += 1;
13868
- if (element.context === "nav" || element.context === "header" || element.context === "footer" || element.context === "sidebar" || element.context === "dialog") {
13869
- score -= 5;
13870
- }
13871
- if (/\b(home|menu|about|contact|privacy|terms|login|sign in|sign up|subscribe|newsletter|facebook|instagram|pinterest|share|print|next|previous|prev|sort|filter|star|sponsor)\b/.test(
13872
- comparableText
13873
- )) {
13874
- score -= 4;
13875
- }
13876
- return { element, score };
13877
- }).filter(({ score, element }) => {
13878
- if (entityItems.length > 0) return score >= 4;
13879
- if (searchOrListingPage) {
13880
- return score >= 4 || score >= 3 && (element.context === "article" || element.context === "main" || element.context === "content");
13881
- }
13882
- return score >= 4 || score >= 3 && element.context === "article";
13883
- }).sort((a, b) => b.score - a.score || (a.element.index ?? 0) - (b.element.index ?? 0));
13884
- const seen = /* @__PURE__ */ new Set();
13885
- return scored.map(({ element }) => element).filter((element) => {
13886
- const key2 = `${normalizeComparable(element.text || "")}|${normalizeUrlForMatch(element.href) || ""}`;
13887
- if (seen.has(key2)) return false;
13888
- seen.add(key2);
13889
- return true;
13890
- });
13891
- }
13892
13896
  function buildScopedContext(page, mode) {
13893
13897
  const render = SCOPED_CONTEXT_RENDERERS.get(mode) ?? buildStructuredContext;
13894
13898
  return render(page);
@@ -19074,16 +19078,216 @@ function handleCreateCheckpoint(ctx, args) {
19074
19078
  const checkpoint = ctx.runtime.createCheckpoint(args.name, args.note);
19075
19079
  return `Created checkpoint ${checkpoint.name} (${checkpoint.id})`;
19076
19080
  }
19077
- function handleRestoreCheckpoint(ctx, args) {
19078
- const checkpoint = findCheckpoint(ctx.runtime.getState().checkpoints, args);
19079
- if (!checkpoint) {
19080
- return "Error: No matching checkpoint found";
19081
+ function handleRestoreCheckpoint(ctx, args) {
19082
+ const checkpoint = findCheckpoint(ctx.runtime.getState().checkpoints, args);
19083
+ if (!checkpoint) {
19084
+ return "Error: No matching checkpoint found";
19085
+ }
19086
+ ctx.runtime.restoreCheckpoint(checkpoint.id);
19087
+ return `Restored checkpoint ${checkpoint.name}`;
19088
+ }
19089
+ function unixNow() {
19090
+ return Math.floor(Date.now() / 1e3);
19091
+ }
19092
+ const logger$q = createLogger("VaultShared");
19093
+ const ALGORITHM = "aes-256-gcm";
19094
+ const IV_LENGTH = 12;
19095
+ const AUTH_TAG_LENGTH = 16;
19096
+ const KEY_STORAGE_PREFIX = "base64:";
19097
+ function encodeEncryptionKeyForStorage(key2) {
19098
+ return `${KEY_STORAGE_PREFIX}${key2.toString("base64")}`;
19099
+ }
19100
+ function decodeEncryptionKeyFromStorage(value) {
19101
+ if (value.startsWith(KEY_STORAGE_PREFIX)) {
19102
+ return Buffer.from(value.slice(KEY_STORAGE_PREFIX.length), "base64");
19103
+ }
19104
+ return Buffer.from(value, "utf-8");
19105
+ }
19106
+ function assertSecretStorageAvailable(customMessage) {
19107
+ if (!electron.safeStorage.isEncryptionAvailable()) {
19108
+ throw new Error(
19109
+ "Vault requires OS-backed secret storage (Keychain, DPAPI, or libsecret)."
19110
+ );
19111
+ }
19112
+ }
19113
+ function getOrCreateEncryptionKey(keyFilename) {
19114
+ assertSecretStorageAvailable();
19115
+ const keyPath = path.join(electron.app.getPath("userData"), keyFilename);
19116
+ if (fs$1.existsSync(keyPath)) {
19117
+ const encryptedKey = fs$1.readFileSync(keyPath);
19118
+ const key22 = decodeEncryptionKeyFromStorage(
19119
+ electron.safeStorage.decryptString(encryptedKey)
19120
+ );
19121
+ if (key22.length !== 32) {
19122
+ throw new Error("Stored vault encryption key has an invalid length.");
19123
+ }
19124
+ return key22;
19125
+ }
19126
+ const key2 = crypto$1.randomBytes(32);
19127
+ fs$1.mkdirSync(path.dirname(keyPath), { recursive: true });
19128
+ const encrypted = electron.safeStorage.encryptString(encodeEncryptionKeyForStorage(key2));
19129
+ fs$1.writeFileSync(keyPath, encrypted, { mode: 384 });
19130
+ fs$1.chmodSync(keyPath, 384);
19131
+ return key2;
19132
+ }
19133
+ function createEncryptDecrypt(keyFilename) {
19134
+ let cachedKey = null;
19135
+ function getKey() {
19136
+ if (!cachedKey) cachedKey = getOrCreateEncryptionKey(keyFilename);
19137
+ return cachedKey;
19138
+ }
19139
+ function encrypt2(plaintext) {
19140
+ const key2 = getKey();
19141
+ const iv = crypto$1.randomBytes(IV_LENGTH);
19142
+ const cipher = crypto$1.createCipheriv(ALGORITHM, key2, iv, {
19143
+ authTagLength: AUTH_TAG_LENGTH
19144
+ });
19145
+ const encrypted = Buffer.concat([
19146
+ cipher.update(plaintext, "utf-8"),
19147
+ cipher.final()
19148
+ ]);
19149
+ const authTag = cipher.getAuthTag();
19150
+ return Buffer.concat([iv, authTag, encrypted]);
19151
+ }
19152
+ function decrypt2(data) {
19153
+ const key2 = getKey();
19154
+ const iv = data.subarray(0, IV_LENGTH);
19155
+ const authTag = data.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);
19156
+ const ciphertext = data.subarray(IV_LENGTH + AUTH_TAG_LENGTH);
19157
+ const decipher = crypto$1.createDecipheriv(ALGORITHM, key2, iv, {
19158
+ authTagLength: AUTH_TAG_LENGTH
19159
+ });
19160
+ decipher.setAuthTag(authTag);
19161
+ return decipher.update(ciphertext, void 0, "utf-8") + decipher.final("utf-8");
19162
+ }
19163
+ function resetKey() {
19164
+ cachedKey = null;
19165
+ }
19166
+ return { encrypt: encrypt2, decrypt: decrypt2, resetKey };
19167
+ }
19168
+ function createVaultIO(vaultFilename, encrypt2, decrypt2) {
19169
+ let cachedEntries = null;
19170
+ function getVaultPath() {
19171
+ return path.join(electron.app.getPath("userData"), vaultFilename);
19172
+ }
19173
+ function loadVault2() {
19174
+ if (cachedEntries) return cachedEntries;
19175
+ const vaultPath = getVaultPath();
19176
+ if (!fs$1.existsSync(vaultPath)) {
19177
+ cachedEntries = [];
19178
+ return cachedEntries;
19179
+ }
19180
+ try {
19181
+ const raw = fs$1.readFileSync(vaultPath);
19182
+ const json = decrypt2(raw);
19183
+ cachedEntries = JSON.parse(json);
19184
+ return cachedEntries;
19185
+ } catch (err) {
19186
+ logger$q.error("Failed to load vault:", err);
19187
+ throw new Error("Could not unlock the vault. Check OS secret storage availability.");
19188
+ }
19189
+ }
19190
+ function saveVault2(entries) {
19191
+ const json = JSON.stringify(entries, null, 2);
19192
+ const encrypted = encrypt2(json);
19193
+ const vaultPath = getVaultPath();
19194
+ fs$1.mkdirSync(path.dirname(vaultPath), { recursive: true });
19195
+ fs$1.writeFileSync(vaultPath, encrypted, { mode: 384 });
19196
+ fs$1.chmodSync(vaultPath, 384);
19197
+ cachedEntries = entries;
19198
+ }
19199
+ function resetCache() {
19200
+ cachedEntries = null;
19201
+ }
19202
+ return { loadVault: loadVault2, saveVault: saveVault2, resetCache };
19203
+ }
19204
+ function normalizeCredentialHost(value) {
19205
+ try {
19206
+ const parsed = new URL(value.includes("://") ? value : `https://${value}`);
19207
+ return parsed.hostname.toLowerCase().replace(/^www\./, "");
19208
+ } catch {
19209
+ const normalized = value.toLowerCase().trim().replace(/^(https?:\/\/)?(www\.)?/, "").replace(/\/.*$/, "");
19210
+ return normalized && !normalized.includes(" ") ? normalized : null;
19211
+ }
19212
+ }
19213
+ function domainMatches(pattern, hostname) {
19214
+ const isWildcard = pattern.trim().startsWith("*.");
19215
+ const p = normalizeCredentialHost(isWildcard ? pattern.slice(2) : pattern);
19216
+ const h = normalizeCredentialHost(hostname);
19217
+ if (!p || !h) return false;
19218
+ return isWildcard ? h.endsWith("." + p) : p === h;
19219
+ }
19220
+ function generateTotpCode(secret) {
19221
+ const epoch = unixNow();
19222
+ const counter = Math.floor(epoch / 30);
19223
+ const base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
19224
+ const cleanSecret = secret.replace(/[\s=-]/g, "").toUpperCase();
19225
+ let bits = "";
19226
+ for (const ch of cleanSecret) {
19227
+ const val = base32Chars.indexOf(ch);
19228
+ if (val === -1) continue;
19229
+ bits += val.toString(2).padStart(5, "0");
19230
+ }
19231
+ const keyBytes = Buffer.alloc(Math.floor(bits.length / 8));
19232
+ for (let i = 0; i < keyBytes.length; i++) {
19233
+ keyBytes[i] = parseInt(bits.slice(i * 8, i * 8 + 8), 2);
19234
+ }
19235
+ const counterBuf = Buffer.alloc(8);
19236
+ counterBuf.writeUInt32BE(Math.floor(counter / 4294967296), 0);
19237
+ counterBuf.writeUInt32BE(counter & 4294967295, 4);
19238
+ const hmac = crypto$1.createHmac("sha1", keyBytes).update(counterBuf).digest();
19239
+ const offset = hmac[hmac.length - 1] & 15;
19240
+ const code = (hmac[offset] & 127) << 24 | (hmac[offset + 1] & 255) << 16 | (hmac[offset + 2] & 255) << 8 | hmac[offset + 3] & 255;
19241
+ return (code % 1e6).toString().padStart(6, "0");
19242
+ }
19243
+ function createAuditLog(filename, maxEntries) {
19244
+ function getAuditPath2() {
19245
+ return path.join(electron.app.getPath("userData"), filename);
19081
19246
  }
19082
- ctx.runtime.restoreCheckpoint(checkpoint.id);
19083
- return `Restored checkpoint ${checkpoint.name}`;
19247
+ function appendAudit(entry) {
19248
+ try {
19249
+ const auditPath = getAuditPath2();
19250
+ fs$1.mkdirSync(path.dirname(auditPath), { recursive: true });
19251
+ fs$1.appendFileSync(auditPath, JSON.stringify(entry) + "\n", {
19252
+ encoding: "utf-8",
19253
+ mode: 384
19254
+ });
19255
+ fs$1.chmodSync(auditPath, 384);
19256
+ try {
19257
+ const lines = fs$1.readFileSync(auditPath, "utf-8").split("\n").filter((l) => l.trim());
19258
+ if (lines.length > maxEntries) {
19259
+ const trimmed = lines.slice(-maxEntries);
19260
+ fs$1.writeFileSync(auditPath, trimmed.join("\n") + "\n", {
19261
+ encoding: "utf-8",
19262
+ mode: 384
19263
+ });
19264
+ fs$1.chmodSync(auditPath, 384);
19265
+ }
19266
+ } catch (err) {
19267
+ logger$q.warn("Failed to trim audit log:", err);
19268
+ }
19269
+ } catch (err) {
19270
+ logger$q.error("Failed to write audit log:", err);
19271
+ }
19272
+ }
19273
+ function readAuditLog2(limit = 100) {
19274
+ try {
19275
+ const auditPath = getAuditPath2();
19276
+ if (!fs$1.existsSync(auditPath)) return [];
19277
+ const lines = fs$1.readFileSync(auditPath, "utf-8").split("\n").filter((l) => l.trim());
19278
+ return lines.slice(-Math.min(limit, maxEntries)).map((line) => JSON.parse(line)).reverse();
19279
+ } catch (err) {
19280
+ logger$q.error("Failed to read audit log:", err);
19281
+ return [];
19282
+ }
19283
+ }
19284
+ return { appendAudit, readAuditLog: readAuditLog2 };
19084
19285
  }
19085
- const logger$q = createLogger("Sessions");
19286
+ const logger$p = createLogger("Sessions");
19086
19287
  const SESSION_VERSION = 1;
19288
+ const ENCRYPTED_SESSION_FORMAT = "vessel:named-session:v2";
19289
+ const SESSION_KEY_FILENAME = "vessel-named-sessions.key";
19290
+ const sessionCrypto = createEncryptDecrypt(SESSION_KEY_FILENAME);
19087
19291
  function getSessionsDir() {
19088
19292
  return path.join(electron.app.getPath("userData"), "named-sessions");
19089
19293
  }
@@ -19109,17 +19313,40 @@ async function getSessionPath(name) {
19109
19313
  const dir = await ensureSessionsDir();
19110
19314
  return path.join(dir, sessionFileName(name));
19111
19315
  }
19316
+ function isEncryptedSessionFile(value) {
19317
+ return !!value && typeof value === "object" && value.format === ENCRYPTED_SESSION_FORMAT && typeof value.payload === "string";
19318
+ }
19319
+ function encodeSessionFile(data) {
19320
+ const plaintext = JSON.stringify({ version: SESSION_VERSION, ...data });
19321
+ const encrypted = sessionCrypto.encrypt(plaintext);
19322
+ return JSON.stringify(
19323
+ {
19324
+ format: ENCRYPTED_SESSION_FORMAT,
19325
+ payload: encrypted.toString("base64")
19326
+ },
19327
+ null,
19328
+ 2
19329
+ );
19330
+ }
19331
+ function decodeSessionFile(raw) {
19332
+ const parsed = JSON.parse(raw);
19333
+ if (isEncryptedSessionFile(parsed)) {
19334
+ const decrypted = sessionCrypto.decrypt(Buffer.from(parsed.payload, "base64"));
19335
+ return parseSessionData(JSON.parse(decrypted));
19336
+ }
19337
+ return parseSessionData(parsed);
19338
+ }
19112
19339
  async function writeSessionFile(filePath2, data) {
19113
- const payload = JSON.stringify({ version: SESSION_VERSION, ...data }, null, 2);
19340
+ const payload = encodeSessionFile(data);
19114
19341
  await writeFileAtomic(filePath2, payload, { mode: 384 });
19115
19342
  }
19116
19343
  async function readSessionFile(filePath2) {
19117
19344
  const raw = await readIfExists(filePath2, "utf-8");
19118
19345
  if (raw == null) return null;
19119
19346
  try {
19120
- return parseSessionData(JSON.parse(raw));
19347
+ return decodeSessionFile(raw);
19121
19348
  } catch (err) {
19122
- logger$q.warn(`Failed to read session file ${filePath2}:`, err);
19349
+ logger$p.warn(`Failed to read session file ${filePath2}:`, err);
19123
19350
  return null;
19124
19351
  }
19125
19352
  }
@@ -19231,7 +19458,7 @@ async function captureLocalStorageForOrigin(tabManager, origin) {
19231
19458
  );
19232
19459
  }
19233
19460
  } catch (err) {
19234
- logger$q.debug(`Failed to capture localStorage for origin ${origin}:`, err);
19461
+ logger$p.debug(`Failed to capture localStorage for origin ${origin}:`, err);
19235
19462
  return {};
19236
19463
  }
19237
19464
  return {};
@@ -19325,7 +19552,7 @@ async function loadNamedSession(tabManager, name) {
19325
19552
  try {
19326
19553
  await electron.session.defaultSession.cookies.set(cookieSetDetails(cookie));
19327
19554
  } catch (err) {
19328
- logger$q.debug(`Skipping cookie ${cookie.name} for ${cookie.domain}:`, err);
19555
+ logger$p.debug(`Skipping cookie ${cookie.name} for ${cookie.domain}:`, err);
19329
19556
  continue;
19330
19557
  }
19331
19558
  }
@@ -19394,7 +19621,7 @@ function handleFlowEnd(ctx) {
19394
19621
  ctx.runtime.clearFlow();
19395
19622
  return "Workflow ended.";
19396
19623
  }
19397
- const logger$p = createLogger("Screenshot");
19624
+ const logger$o = createLogger("Screenshot");
19398
19625
  const SCREENSHOT_RETRY_COUNT = 3;
19399
19626
  const SCREENSHOT_RETRY_BASE_DELAY_MS = 120;
19400
19627
  async function captureScreenshot(wc) {
@@ -19416,7 +19643,7 @@ async function captureScreenshot(wc) {
19416
19643
  }
19417
19644
  }
19418
19645
  } catch (err) {
19419
- logger$p.debug(
19646
+ logger$o.debug(
19420
19647
  `capturePage attempt ${attempt + 1} failed; retrying if attempts remain.`,
19421
19648
  getErrorMessage(err)
19422
19649
  );
@@ -20584,10 +20811,7 @@ function normalizeBookmarkMetadataUpdate(input) {
20584
20811
  }
20585
20812
  return normalized;
20586
20813
  }
20587
- function unixNow() {
20588
- return Math.floor(Date.now() / 1e3);
20589
- }
20590
- const logger$o = createLogger("BookmarkManager");
20814
+ const logger$n = createLogger("BookmarkManager");
20591
20815
  const UNSORTED_ID = "unsorted";
20592
20816
  const ARCHIVE_FOLDER_NAME = "Archive";
20593
20817
  const NETSCAPE_BOOKMARKS_DOCTYPE = "<!DOCTYPE NETSCAPE-Bookmark-file-1>";
@@ -21160,7 +21384,7 @@ function importBookmarksFromJson(content) {
21160
21384
  emit$2();
21161
21385
  }
21162
21386
  } catch (err) {
21163
- logger$o.warn("Failed to import bookmarks from JSON:", err);
21387
+ logger$n.warn("Failed to import bookmarks from JSON:", err);
21164
21388
  errors++;
21165
21389
  }
21166
21390
  return { imported, skipped, errors };
@@ -21819,7 +22043,7 @@ const DANGEROUS_ACTIONS = /* @__PURE__ */ new Set([
21819
22043
  function isDangerousAction(name) {
21820
22044
  return DANGEROUS_ACTIONS.has(name);
21821
22045
  }
21822
- const logger$n = createLogger("ResearchOrchestrator");
22046
+ const logger$m = createLogger("ResearchOrchestrator");
21823
22047
  const MAX_THREADS = 5;
21824
22048
  const MAX_TRACE_ARGS_CHARS = 1200;
21825
22049
  const MAX_TRACE_RESULT_CHARS = 2e3;
@@ -22008,7 +22232,7 @@ class ResearchOrchestrator {
22008
22232
  }
22009
22233
  stopAndSynthesizeCurrentFindings() {
22010
22234
  if (this.state.phase !== "executing") {
22011
- logger$n.warn("Not executing, ignoring stopAndSynthesizeCurrentFindings");
22235
+ logger$m.warn("Not executing, ignoring stopAndSynthesizeCurrentFindings");
22012
22236
  return;
22013
22237
  }
22014
22238
  this.stopRequested = true;
@@ -22042,23 +22266,23 @@ class ResearchOrchestrator {
22042
22266
  async startBrief(userQuery) {
22043
22267
  const query = userQuery.trim();
22044
22268
  if (!query) {
22045
- logger$n.warn("Ignoring empty Research Desk query");
22269
+ logger$m.warn("Ignoring empty Research Desk query");
22046
22270
  return;
22047
22271
  }
22048
22272
  if (this.state.phase !== "idle") {
22049
- logger$n.warn("Research already in progress, ignoring startBrief");
22273
+ logger$m.warn("Research already in progress, ignoring startBrief");
22050
22274
  return;
22051
22275
  }
22052
22276
  this.state = this.initialState();
22053
22277
  this.state.originalQuery = query;
22054
22278
  this.state.startedAt = (/* @__PURE__ */ new Date()).toISOString();
22055
22279
  this.setPhase("briefing");
22056
- logger$n.info(`Brief started for query: ${query.slice(0, 120)}`);
22280
+ logger$m.info(`Brief started for query: ${query.slice(0, 120)}`);
22057
22281
  }
22058
22282
  // ── phase: briefing → planning ─────────────────────────────────
22059
22283
  confirmBrief() {
22060
22284
  if (this.state.phase !== "briefing") {
22061
- logger$n.warn("Not in briefing phase, ignoring confirmBrief");
22285
+ logger$m.warn("Not in briefing phase, ignoring confirmBrief");
22062
22286
  return;
22063
22287
  }
22064
22288
  this.setPhase("planning");
@@ -22066,7 +22290,7 @@ class ResearchOrchestrator {
22066
22290
  // ── phase: planning → awaiting_approval ────────────────────────
22067
22291
  setObjectives(objectives) {
22068
22292
  if (this.state.phase !== "planning") {
22069
- logger$n.warn("Not in planning phase, ignoring setObjectives");
22293
+ logger$m.warn("Not in planning phase, ignoring setObjectives");
22070
22294
  return;
22071
22295
  }
22072
22296
  const threads = objectives.threads.slice(0, MAX_THREADS).map(mergeBlockedSourceDomains);
@@ -22095,11 +22319,11 @@ class ResearchOrchestrator {
22095
22319
  try {
22096
22320
  const parsed = JSON.parse(json);
22097
22321
  if (typeof parsed.researchQuestion !== "string" || !parsed.researchQuestion.trim()) {
22098
- logger$n.warn("Missing researchQuestion in objectives JSON");
22322
+ logger$m.warn("Missing researchQuestion in objectives JSON");
22099
22323
  return false;
22100
22324
  }
22101
22325
  if (!Array.isArray(parsed.threads) || parsed.threads.length === 0) {
22102
- logger$n.warn("Missing or empty threads array in objectives JSON");
22326
+ logger$m.warn("Missing or empty threads array in objectives JSON");
22103
22327
  return false;
22104
22328
  }
22105
22329
  const threads = parsed.threads.map((t, i) => {
@@ -22117,7 +22341,7 @@ class ResearchOrchestrator {
22117
22341
  };
22118
22342
  }).filter((thread) => thread.question && thread.searchQueries.length > 0).slice(0, MAX_THREADS);
22119
22343
  if (threads.length === 0) {
22120
- logger$n.warn("Objectives JSON did not contain any valid research threads");
22344
+ logger$m.warn("Objectives JSON did not contain any valid research threads");
22121
22345
  return false;
22122
22346
  }
22123
22347
  const objectives = {
@@ -22128,17 +22352,17 @@ class ResearchOrchestrator {
22128
22352
  totalSourceBudget: threads.reduce((sum, t) => sum + t.sourceBudget, 0)
22129
22353
  };
22130
22354
  this.setObjectives(objectives);
22131
- logger$n.info(`Parsed ${objectives.threads.length} threads from objectives`);
22355
+ logger$m.info(`Parsed ${objectives.threads.length} threads from objectives`);
22132
22356
  return true;
22133
22357
  } catch (err) {
22134
- logger$n.warn("Failed to parse objectives JSON", err);
22358
+ logger$m.warn("Failed to parse objectives JSON", err);
22135
22359
  return false;
22136
22360
  }
22137
22361
  }
22138
22362
  // ── phase: awaiting_approval → executing ───────────────────────
22139
22363
  approveObjectives(mode, includeTraces) {
22140
22364
  if (this.state.phase !== "awaiting_approval") {
22141
- logger$n.warn("Not awaiting approval, ignoring approveObjectives");
22365
+ logger$m.warn("Not awaiting approval, ignoring approveObjectives");
22142
22366
  return;
22143
22367
  }
22144
22368
  if (mode) this.state.supervisionMode = mode;
@@ -22173,7 +22397,7 @@ class ResearchOrchestrator {
22173
22397
  this.state.threads.map((thread) => {
22174
22398
  if (this.state.phase !== "executing") return null;
22175
22399
  return this.runSubAgent(thread, tabMutex).catch((err) => {
22176
- logger$n.error(`Sub-agent "${thread.label}" failed`, err);
22400
+ logger$m.error(`Sub-agent "${thread.label}" failed`, err);
22177
22401
  return {
22178
22402
  threadLabel: thread.label,
22179
22403
  threadQuestion: thread.question,
@@ -22202,7 +22426,7 @@ class ResearchOrchestrator {
22202
22426
  try {
22203
22427
  await this.synthesizeReport();
22204
22428
  } catch (err) {
22205
- logger$n.error("Auto-synthesis failed", err);
22429
+ logger$m.error("Auto-synthesis failed", err);
22206
22430
  this.state.error = `Synthesis failed: ${String(err)}`;
22207
22431
  this.setPhase("delivered");
22208
22432
  }
@@ -22313,7 +22537,7 @@ Start by searching for: ${thread.searchQueries.join(" or ")}`;
22313
22537
  try {
22314
22538
  this.tabManager.closeTab(tabId);
22315
22539
  } catch (err) {
22316
- logger$n.warn(`Failed to close sub-agent tab ${tabId}`, err);
22540
+ logger$m.warn(`Failed to close sub-agent tab ${tabId}`, err);
22317
22541
  }
22318
22542
  }
22319
22543
  }
@@ -22322,7 +22546,7 @@ Start by searching for: ${thread.searchQueries.join(" or ")}`;
22322
22546
  try {
22323
22547
  claims = await this.extractClaimsFromTranscript(thread, transcript);
22324
22548
  } catch (err) {
22325
- logger$n.warn(`Claim extraction failed for "${thread.label}"`, err);
22549
+ logger$m.warn(`Claim extraction failed for "${thread.label}"`, err);
22326
22550
  }
22327
22551
  }
22328
22552
  if (this.state.phase === "executing" && this.state.includeTraces) {
@@ -22412,7 +22636,7 @@ ${transcript.slice(0, 32e3)}`;
22412
22636
  (claim) => claim.claim && claim.sourceUrl && claim.extractedQuote
22413
22637
  );
22414
22638
  } catch {
22415
- logger$n.warn(`Failed to parse claims JSON for "${thread.label}"`);
22639
+ logger$m.warn(`Failed to parse claims JSON for "${thread.label}"`);
22416
22640
  return [];
22417
22641
  }
22418
22642
  }
@@ -22497,7 +22721,7 @@ ${transcript.slice(0, 32e3)}`;
22497
22721
  objectives
22498
22722
  };
22499
22723
  } catch (err) {
22500
- logger$n.warn("Failed to parse synthesis JSON, using sourced fallback report", err);
22724
+ logger$m.warn("Failed to parse synthesis JSON, using sourced fallback report", err);
22501
22725
  return buildFallbackReport(objectives, findings, String(err));
22502
22726
  }
22503
22727
  }
@@ -23072,7 +23296,7 @@ function loadRenderers(chromeView, sidebarView, devtoolsPanelView) {
23072
23296
  });
23073
23297
  }
23074
23298
  }
23075
- const logger$m = createLogger("PrivateWindow");
23299
+ const logger$l = createLogger("PrivateWindow");
23076
23300
  const privateWindows = /* @__PURE__ */ new Set();
23077
23301
  function layoutPrivateViews(state2) {
23078
23302
  const { window: win, chromeView, tabManager } = state2;
@@ -23302,7 +23526,7 @@ function createPrivateWindow() {
23302
23526
  privateSession.clearStorageData(),
23303
23527
  privateSession.clearCache()
23304
23528
  ]).catch((error) => {
23305
- logger$m.warn("Failed to clear private browsing session:", error);
23529
+ logger$l.warn("Failed to clear private browsing session:", error);
23306
23530
  });
23307
23531
  });
23308
23532
  privateWindows.add(state2);
@@ -23311,38 +23535,38 @@ function createPrivateWindow() {
23311
23535
  tabManager?.createTab("about:blank");
23312
23536
  if (state2) layoutPrivateViews(state2);
23313
23537
  } catch (error) {
23314
- logger$m.error("Failed to initialize private browsing tab:", error);
23538
+ logger$l.error("Failed to initialize private browsing tab:", error);
23315
23539
  try {
23316
23540
  win?.close();
23317
23541
  } catch (cleanupError) {
23318
- logger$m.warn("Failed to close private window after tab init failure:", cleanupError);
23542
+ logger$l.warn("Failed to close private window after tab init failure:", cleanupError);
23319
23543
  }
23320
23544
  }
23321
23545
  });
23322
23546
  loadPrivateRenderer(chromeView);
23323
23547
  win.show();
23324
- logger$m.info("Private browsing window opened");
23548
+ logger$l.info("Private browsing window opened");
23325
23549
  return state2;
23326
23550
  } catch (error) {
23327
- logger$m.error("Failed to create private browsing window:", error);
23551
+ logger$l.error("Failed to create private browsing window:", error);
23328
23552
  if (state2) {
23329
23553
  privateWindows.delete(state2);
23330
23554
  }
23331
23555
  try {
23332
23556
  tabManager?.destroyAllTabs();
23333
23557
  } catch (cleanupError) {
23334
- logger$m.warn("Failed to clean up private tabs after launch failure:", cleanupError);
23558
+ logger$l.warn("Failed to clean up private tabs after launch failure:", cleanupError);
23335
23559
  }
23336
23560
  try {
23337
23561
  win?.close();
23338
23562
  } catch (cleanupError) {
23339
- logger$m.warn("Failed to close private window after launch failure:", cleanupError);
23563
+ logger$l.warn("Failed to close private window after launch failure:", cleanupError);
23340
23564
  }
23341
23565
  void Promise.all([
23342
23566
  privateSession.clearStorageData(),
23343
23567
  privateSession.clearCache()
23344
23568
  ]).catch((cleanupError) => {
23345
- logger$m.warn("Failed to clear failed private browsing session:", cleanupError);
23569
+ logger$l.warn("Failed to clear failed private browsing session:", cleanupError);
23346
23570
  });
23347
23571
  throw error;
23348
23572
  }
@@ -23741,7 +23965,7 @@ function registerTabHandlers(windowState, _sendToRendererViews) {
23741
23965
  );
23742
23966
  }
23743
23967
  const require$1 = node_module.createRequire(require("url").pathToFileURL(__filename).href);
23744
- const logger$l = createLogger("DevTrace");
23968
+ const logger$k = createLogger("DevTrace");
23745
23969
  let cachedFactory;
23746
23970
  function createNoopTraceSession() {
23747
23971
  return {
@@ -23774,7 +23998,7 @@ function loadLocalFactory() {
23774
23998
  return cachedFactory;
23775
23999
  }
23776
24000
  } catch (err) {
23777
- logger$l.warn("Failed to load local trace logger:", err);
24001
+ logger$k.warn("Failed to load local trace logger:", err);
23778
24002
  }
23779
24003
  }
23780
24004
  return cachedFactory;
@@ -24682,7 +24906,7 @@ function registerContentHandlers(windowState) {
24682
24906
  return windowState.uiState.focusMode;
24683
24907
  });
24684
24908
  }
24685
- const logger$k = createLogger("HighlightIPC");
24909
+ const logger$j = createLogger("HighlightIPC");
24686
24910
  const HighlightIndexSchema = zod.z.number().int().min(0);
24687
24911
  function registerHighlightHandlers(windowState, sendToRendererViews) {
24688
24912
  const { tabManager, chromeView } = windowState;
@@ -24692,7 +24916,7 @@ function registerHighlightHandlers(windowState, sendToRendererViews) {
24692
24916
  try {
24693
24917
  return await getHighlightCount(info.wc) ?? 0;
24694
24918
  } catch (err) {
24695
- logger$k.warn("Failed to get active highlight count:", err);
24919
+ logger$j.warn("Failed to get active highlight count:", err);
24696
24920
  return 0;
24697
24921
  }
24698
24922
  };
@@ -24717,13 +24941,13 @@ function registerHighlightHandlers(windowState, sendToRendererViews) {
24717
24941
  const result = await captureSelectionHighlight(wc);
24718
24942
  if (result.success && result.text) {
24719
24943
  await highlightOnPage(wc, null, result.text, void 0, void 0, "yellow").catch(
24720
- (err) => logger$k.warn("Failed to highlight captured selection:", err)
24944
+ (err) => logger$j.warn("Failed to highlight captured selection:", err)
24721
24945
  );
24722
24946
  await emitHighlightCount();
24723
24947
  }
24724
24948
  return result;
24725
24949
  } catch (err) {
24726
- logger$k.warn("Failed to capture highlight from active tab:", err);
24950
+ logger$j.warn("Failed to capture highlight from active tab:", err);
24727
24951
  return { success: false, message: "Could not capture selection" };
24728
24952
  }
24729
24953
  });
@@ -24741,7 +24965,7 @@ function registerHighlightHandlers(windowState, sendToRendererViews) {
24741
24965
  }
24742
24966
  });
24743
24967
  } catch (err) {
24744
- logger$k.warn("Failed to persist auto-highlight selection:", err);
24968
+ logger$j.warn("Failed to persist auto-highlight selection:", err);
24745
24969
  }
24746
24970
  });
24747
24971
  electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_COUNT, (event) => {
@@ -24756,7 +24980,7 @@ function registerHighlightHandlers(windowState, sendToRendererViews) {
24756
24980
  try {
24757
24981
  return scrollToHighlight(info.wc, validatedIndex);
24758
24982
  } catch (err) {
24759
- logger$k.warn("Failed to scroll to highlight:", err);
24983
+ logger$j.warn("Failed to scroll to highlight:", err);
24760
24984
  return false;
24761
24985
  }
24762
24986
  });
@@ -24780,7 +25004,7 @@ function registerHighlightHandlers(windowState, sendToRendererViews) {
24780
25004
  }
24781
25005
  return removed;
24782
25006
  } catch (err) {
24783
- logger$k.warn("Failed to remove highlight at index:", err);
25007
+ logger$j.warn("Failed to remove highlight at index:", err);
24784
25008
  return false;
24785
25009
  }
24786
25010
  });
@@ -24797,7 +25021,7 @@ function registerHighlightHandlers(windowState, sendToRendererViews) {
24797
25021
  }
24798
25022
  return cleared;
24799
25023
  } catch (err) {
24800
- logger$k.warn("Failed to clear highlight elements:", err);
25024
+ logger$j.warn("Failed to clear highlight elements:", err);
24801
25025
  return false;
24802
25026
  }
24803
25027
  });
@@ -26160,7 +26384,7 @@ async function linkBookmarkToMemory({
26160
26384
  }
26161
26385
  });
26162
26386
  }
26163
- const logger$j = createLogger("MCP");
26387
+ const logger$i = createLogger("MCP");
26164
26388
  function asTextResponse(text) {
26165
26389
  return { content: [{ type: "text", text }] };
26166
26390
  }
@@ -26261,7 +26485,7 @@ async function getPostActionState(tabManager, name) {
26261
26485
  }
26262
26486
  }
26263
26487
  } catch (err) {
26264
- logger$j.warn("Failed to compute post-action state warning:", err);
26488
+ logger$i.warn("Failed to compute post-action state warning:", err);
26265
26489
  }
26266
26490
  return `${warning}
26267
26491
  [state: url=${wc.getURL()}, canGoBack=${tab.canGoBack()}, canGoForward=${tab.canGoForward()}, loading=${wc.isLoading()}]`;
@@ -26366,7 +26590,7 @@ async function waitForConditionMcp(wc, text, selector, timeoutMs) {
26366
26590
  }
26367
26591
  })()
26368
26592
  `).catch((err) => {
26369
- logger$j.warn("Failed to gather wait_for timeout diagnostic:", err);
26593
+ logger$i.warn("Failed to gather wait_for timeout diagnostic:", err);
26370
26594
  return null;
26371
26595
  });
26372
26596
  if (typeof diagnostic === "string" && diagnostic.trim()) {
@@ -27234,7 +27458,7 @@ function registerSessionTools(server, tabManager, runtime2) {
27234
27458
  )
27235
27459
  );
27236
27460
  }
27237
- const logger$i = createLogger("MCPContentTools");
27461
+ const logger$h = createLogger("MCPContentTools");
27238
27462
  const EXTRACT_MODES = [
27239
27463
  "full",
27240
27464
  "summary",
@@ -27659,7 +27883,7 @@ To analyze visually, call vision_analyze with image_url="${screenshotPath}"`
27659
27883
  try {
27660
27884
  page = await extractContent(wc);
27661
27885
  } catch (err) {
27662
- logger$i.warn("Failed to extract page while generating suggestions:", err);
27886
+ logger$h.warn("Failed to extract page while generating suggestions:", err);
27663
27887
  return asTextResponse(
27664
27888
  "Could not read page. Try navigate to a working URL."
27665
27889
  );
@@ -28511,7 +28735,7 @@ function registerNavigationTools(server, tabManager, runtime2) {
28511
28735
  }
28512
28736
  );
28513
28737
  }
28514
- const logger$h = createLogger("MCPPrompts");
28738
+ const logger$g = createLogger("MCPPrompts");
28515
28739
  function registerPromptTools(server, tabManager, runtime2) {
28516
28740
  server.registerPrompt(
28517
28741
  "vessel-supervisor-brief",
@@ -28590,7 +28814,7 @@ function registerPromptTools(server, tabManager, runtime2) {
28590
28814
  const page = await extractContent(wc);
28591
28815
  pageType = detectPageType(page);
28592
28816
  } catch (err) {
28593
- logger$h.warn("Failed to detect page type for tool scoring, falling back to GENERAL:", err);
28817
+ logger$g.warn("Failed to detect page type for tool scoring, falling back to GENERAL:", err);
28594
28818
  }
28595
28819
  }
28596
28820
  const scored = TOOL_DEFINITIONS.map((def) => {
@@ -28751,7 +28975,7 @@ function registerGroupTools(server, tabManager, runtime2) {
28751
28975
  })
28752
28976
  );
28753
28977
  }
28754
- const logger$g = createLogger("MCPHighlightTools");
28978
+ const logger$f = createLogger("MCPHighlightTools");
28755
28979
  function registerHighlightTools(server, tabManager, runtime2) {
28756
28980
  server.registerTool(
28757
28981
  "highlight",
@@ -28968,7 +29192,7 @@ ${JSON.stringify(otherHighlights, null, 2)}`
28968
29192
  void 0,
28969
29193
  h.color
28970
29194
  ).catch(
28971
- (err) => logger$g.warn("Failed to restore highlight after removal:", err)
29195
+ (err) => logger$f.warn("Failed to restore highlight after removal:", err)
28972
29196
  );
28973
29197
  }
28974
29198
  }
@@ -29487,7 +29711,7 @@ ${steps.join("\n")}`;
29487
29711
  }
29488
29712
  const AUDIT_FILENAME = "vessel-vault-audit.jsonl";
29489
29713
  const MAX_ENTRIES = 1e3;
29490
- const logger$f = createLogger("VaultAudit");
29714
+ const logger$e = createLogger("VaultAudit");
29491
29715
  function getAuditPath() {
29492
29716
  return path.join(electron.app.getPath("userData"), AUDIT_FILENAME);
29493
29717
  }
@@ -29501,7 +29725,7 @@ function appendAuditEntry(entry) {
29501
29725
  });
29502
29726
  fs$1.chmodSync(auditPath, 384);
29503
29727
  } catch (err) {
29504
- logger$f.error("Failed to write audit log:", err);
29728
+ logger$e.error("Failed to write audit log:", err);
29505
29729
  }
29506
29730
  }
29507
29731
  function readAuditLog$1(limit = 100) {
@@ -29511,7 +29735,7 @@ function readAuditLog$1(limit = 100) {
29511
29735
  const lines = fs$1.readFileSync(auditPath, "utf-8").split("\n").filter((l) => l.trim());
29512
29736
  return lines.slice(-Math.min(limit, MAX_ENTRIES)).map((line) => JSON.parse(line)).reverse();
29513
29737
  } catch (err) {
29514
- logger$f.error("Failed to read audit log:", err);
29738
+ logger$e.error("Failed to read audit log:", err);
29515
29739
  return [];
29516
29740
  }
29517
29741
  }
@@ -29580,200 +29804,6 @@ async function requestHumanVaultConsent(request) {
29580
29804
  });
29581
29805
  return { approved: response === 1 };
29582
29806
  }
29583
- const logger$e = createLogger("VaultShared");
29584
- const ALGORITHM = "aes-256-gcm";
29585
- const IV_LENGTH = 12;
29586
- const AUTH_TAG_LENGTH = 16;
29587
- const KEY_STORAGE_PREFIX = "base64:";
29588
- function encodeEncryptionKeyForStorage(key2) {
29589
- return `${KEY_STORAGE_PREFIX}${key2.toString("base64")}`;
29590
- }
29591
- function decodeEncryptionKeyFromStorage(value) {
29592
- if (value.startsWith(KEY_STORAGE_PREFIX)) {
29593
- return Buffer.from(value.slice(KEY_STORAGE_PREFIX.length), "base64");
29594
- }
29595
- return Buffer.from(value, "utf-8");
29596
- }
29597
- function assertSecretStorageAvailable(customMessage) {
29598
- if (!electron.safeStorage.isEncryptionAvailable()) {
29599
- throw new Error(
29600
- "Vault requires OS-backed secret storage (Keychain, DPAPI, or libsecret)."
29601
- );
29602
- }
29603
- }
29604
- function getOrCreateEncryptionKey(keyFilename) {
29605
- assertSecretStorageAvailable();
29606
- const keyPath = path.join(electron.app.getPath("userData"), keyFilename);
29607
- if (fs$1.existsSync(keyPath)) {
29608
- const encryptedKey = fs$1.readFileSync(keyPath);
29609
- const key22 = decodeEncryptionKeyFromStorage(
29610
- electron.safeStorage.decryptString(encryptedKey)
29611
- );
29612
- if (key22.length !== 32) {
29613
- throw new Error("Stored vault encryption key has an invalid length.");
29614
- }
29615
- return key22;
29616
- }
29617
- const key2 = crypto$1.randomBytes(32);
29618
- fs$1.mkdirSync(path.dirname(keyPath), { recursive: true });
29619
- const encrypted = electron.safeStorage.encryptString(encodeEncryptionKeyForStorage(key2));
29620
- fs$1.writeFileSync(keyPath, encrypted, { mode: 384 });
29621
- fs$1.chmodSync(keyPath, 384);
29622
- return key2;
29623
- }
29624
- function createEncryptDecrypt(keyFilename) {
29625
- let cachedKey = null;
29626
- function getKey() {
29627
- if (!cachedKey) cachedKey = getOrCreateEncryptionKey(keyFilename);
29628
- return cachedKey;
29629
- }
29630
- function encrypt2(plaintext) {
29631
- const key2 = getKey();
29632
- const iv = crypto$1.randomBytes(IV_LENGTH);
29633
- const cipher = crypto$1.createCipheriv(ALGORITHM, key2, iv, {
29634
- authTagLength: AUTH_TAG_LENGTH
29635
- });
29636
- const encrypted = Buffer.concat([
29637
- cipher.update(plaintext, "utf-8"),
29638
- cipher.final()
29639
- ]);
29640
- const authTag = cipher.getAuthTag();
29641
- return Buffer.concat([iv, authTag, encrypted]);
29642
- }
29643
- function decrypt2(data) {
29644
- const key2 = getKey();
29645
- const iv = data.subarray(0, IV_LENGTH);
29646
- const authTag = data.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);
29647
- const ciphertext = data.subarray(IV_LENGTH + AUTH_TAG_LENGTH);
29648
- const decipher = crypto$1.createDecipheriv(ALGORITHM, key2, iv, {
29649
- authTagLength: AUTH_TAG_LENGTH
29650
- });
29651
- decipher.setAuthTag(authTag);
29652
- return decipher.update(ciphertext, void 0, "utf-8") + decipher.final("utf-8");
29653
- }
29654
- function resetKey() {
29655
- cachedKey = null;
29656
- }
29657
- return { encrypt: encrypt2, decrypt: decrypt2, resetKey };
29658
- }
29659
- function createVaultIO(vaultFilename, encrypt2, decrypt2) {
29660
- let cachedEntries = null;
29661
- function getVaultPath() {
29662
- return path.join(electron.app.getPath("userData"), vaultFilename);
29663
- }
29664
- function loadVault2() {
29665
- if (cachedEntries) return cachedEntries;
29666
- const vaultPath = getVaultPath();
29667
- if (!fs$1.existsSync(vaultPath)) {
29668
- cachedEntries = [];
29669
- return cachedEntries;
29670
- }
29671
- try {
29672
- const raw = fs$1.readFileSync(vaultPath);
29673
- const json = decrypt2(raw);
29674
- cachedEntries = JSON.parse(json);
29675
- return cachedEntries;
29676
- } catch (err) {
29677
- logger$e.error("Failed to load vault:", err);
29678
- throw new Error("Could not unlock the vault. Check OS secret storage availability.");
29679
- }
29680
- }
29681
- function saveVault2(entries) {
29682
- const json = JSON.stringify(entries, null, 2);
29683
- const encrypted = encrypt2(json);
29684
- const vaultPath = getVaultPath();
29685
- fs$1.mkdirSync(path.dirname(vaultPath), { recursive: true });
29686
- fs$1.writeFileSync(vaultPath, encrypted, { mode: 384 });
29687
- fs$1.chmodSync(vaultPath, 384);
29688
- cachedEntries = entries;
29689
- }
29690
- function resetCache() {
29691
- cachedEntries = null;
29692
- }
29693
- return { loadVault: loadVault2, saveVault: saveVault2, resetCache };
29694
- }
29695
- function normalizeCredentialHost(value) {
29696
- try {
29697
- const parsed = new URL(value.includes("://") ? value : `https://${value}`);
29698
- return parsed.hostname.toLowerCase().replace(/^www\./, "");
29699
- } catch {
29700
- const normalized = value.toLowerCase().trim().replace(/^(https?:\/\/)?(www\.)?/, "").replace(/\/.*$/, "");
29701
- return normalized && !normalized.includes(" ") ? normalized : null;
29702
- }
29703
- }
29704
- function domainMatches(pattern, hostname) {
29705
- const isWildcard = pattern.trim().startsWith("*.");
29706
- const p = normalizeCredentialHost(isWildcard ? pattern.slice(2) : pattern);
29707
- const h = normalizeCredentialHost(hostname);
29708
- if (!p || !h) return false;
29709
- return isWildcard ? h.endsWith("." + p) : p === h;
29710
- }
29711
- function generateTotpCode(secret) {
29712
- const epoch = unixNow();
29713
- const counter = Math.floor(epoch / 30);
29714
- const base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
29715
- const cleanSecret = secret.replace(/[\s=-]/g, "").toUpperCase();
29716
- let bits = "";
29717
- for (const ch of cleanSecret) {
29718
- const val = base32Chars.indexOf(ch);
29719
- if (val === -1) continue;
29720
- bits += val.toString(2).padStart(5, "0");
29721
- }
29722
- const keyBytes = Buffer.alloc(Math.floor(bits.length / 8));
29723
- for (let i = 0; i < keyBytes.length; i++) {
29724
- keyBytes[i] = parseInt(bits.slice(i * 8, i * 8 + 8), 2);
29725
- }
29726
- const counterBuf = Buffer.alloc(8);
29727
- counterBuf.writeUInt32BE(Math.floor(counter / 4294967296), 0);
29728
- counterBuf.writeUInt32BE(counter & 4294967295, 4);
29729
- const hmac = crypto$1.createHmac("sha1", keyBytes).update(counterBuf).digest();
29730
- const offset = hmac[hmac.length - 1] & 15;
29731
- const code = (hmac[offset] & 127) << 24 | (hmac[offset + 1] & 255) << 16 | (hmac[offset + 2] & 255) << 8 | hmac[offset + 3] & 255;
29732
- return (code % 1e6).toString().padStart(6, "0");
29733
- }
29734
- function createAuditLog(filename, maxEntries) {
29735
- function getAuditPath2() {
29736
- return path.join(electron.app.getPath("userData"), filename);
29737
- }
29738
- function appendAudit(entry) {
29739
- try {
29740
- const auditPath = getAuditPath2();
29741
- fs$1.mkdirSync(path.dirname(auditPath), { recursive: true });
29742
- fs$1.appendFileSync(auditPath, JSON.stringify(entry) + "\n", {
29743
- encoding: "utf-8",
29744
- mode: 384
29745
- });
29746
- fs$1.chmodSync(auditPath, 384);
29747
- try {
29748
- const lines = fs$1.readFileSync(auditPath, "utf-8").split("\n").filter((l) => l.trim());
29749
- if (lines.length > maxEntries) {
29750
- const trimmed = lines.slice(-maxEntries);
29751
- fs$1.writeFileSync(auditPath, trimmed.join("\n") + "\n", {
29752
- encoding: "utf-8",
29753
- mode: 384
29754
- });
29755
- fs$1.chmodSync(auditPath, 384);
29756
- }
29757
- } catch (err) {
29758
- logger$e.warn("Failed to trim audit log:", err);
29759
- }
29760
- } catch (err) {
29761
- logger$e.error("Failed to write audit log:", err);
29762
- }
29763
- }
29764
- function readAuditLog2(limit = 100) {
29765
- try {
29766
- const auditPath = getAuditPath2();
29767
- if (!fs$1.existsSync(auditPath)) return [];
29768
- const lines = fs$1.readFileSync(auditPath, "utf-8").split("\n").filter((l) => l.trim());
29769
- return lines.slice(-Math.min(limit, maxEntries)).map((line) => JSON.parse(line)).reverse();
29770
- } catch (err) {
29771
- logger$e.error("Failed to read audit log:", err);
29772
- return [];
29773
- }
29774
- }
29775
- return { appendAudit, readAuditLog: readAuditLog2 };
29776
- }
29777
29807
  const VAULT_FILENAME$1 = "vessel-human-vault.enc";
29778
29808
  const KEY_FILENAME$1 = "vessel-human-vault.key";
29779
29809
  const AUDIT_MAX_ENTRIES = 2e3;