@quanta-intellect/vessel-browser 0.1.94 → 0.1.95

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
@@ -276,9 +276,10 @@ function persistNow() {
276
276
  return fs.promises.mkdir(path.dirname(getSettingsPath()), { recursive: true }).then(
277
277
  () => fs.promises.writeFile(
278
278
  getSettingsPath(),
279
- JSON.stringify(buildPersistedSettings(settings), null, 2)
279
+ JSON.stringify(buildPersistedSettings(settings), null, 2),
280
+ { encoding: "utf-8", mode: 384 }
280
281
  )
281
- ).catch((err) => logger$n.error("Failed to save settings:", err));
282
+ ).then(() => fs.promises.chmod(getSettingsPath(), 384).catch(() => void 0)).catch((err) => logger$n.error("Failed to save settings:", err));
282
283
  }
283
284
  function saveSettings() {
284
285
  saveDirty = true;
@@ -1133,7 +1134,7 @@ function createDebouncedJsonPersistence({
1133
1134
  data,
1134
1135
  typeof data === "string" ? { encoding: "utf-8", mode: 384 } : { mode: 384 }
1135
1136
  )
1136
- ).catch((err) => logger$l.error(`Failed to save ${logLabel}:`, err));
1137
+ ).then(() => fs.promises.chmod(filePath2, 384).catch(() => void 0)).catch((err) => logger$l.error(`Failed to save ${logLabel}:`, err));
1137
1138
  };
1138
1139
  const schedule = () => {
1139
1140
  saveDirty2 = true;
@@ -4760,6 +4761,21 @@ const POSTHOG_HOST = process.env.POSTHOG_HOST || "https://us.i.posthog.com";
4760
4761
  const BATCH_INTERVAL_MS = 6e4;
4761
4762
  const MAX_BATCH_SIZE = 50;
4762
4763
  const SENSITIVE_PROPERTY_RE = /url|uri|query|prompt|content|text|token|secret|key|password|credential|email|domain/i;
4764
+ const SENSITIVE_STRING_VALUE_RE = /https?:\/\/|www\.|[^\s@]+@[^\s@]+\.[^\s@]+|bearer\s+\S+|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.|(?:sk|pk|rk|gh[pousr]|xox[baprs])-[-_A-Za-z0-9]{12,}/i;
4765
+ const EMPTY_PROPERTY_ALLOWLIST = /* @__PURE__ */ new Set();
4766
+ const TELEMETRY_PROPERTY_ALLOWLIST = {
4767
+ app_launched: /* @__PURE__ */ new Set(["electron_version", "chrome_version"]),
4768
+ app_session_ended: /* @__PURE__ */ new Set(["duration_minutes"]),
4769
+ tool_called: /* @__PURE__ */ new Set(["tool_name", "page_type"]),
4770
+ provider_configured: /* @__PURE__ */ new Set(["provider_id"]),
4771
+ page_type_detected: /* @__PURE__ */ new Set(["page_type"]),
4772
+ setting_changed: /* @__PURE__ */ new Set(["setting_key"]),
4773
+ approval_mode_changed: /* @__PURE__ */ new Set(["mode"]),
4774
+ bookmark_action: /* @__PURE__ */ new Set(["action"]),
4775
+ vault_action: /* @__PURE__ */ new Set(["action"]),
4776
+ extraction_failed: /* @__PURE__ */ new Set(["reason"]),
4777
+ premium_funnel: /* @__PURE__ */ new Set(["step", "status", "reason"])
4778
+ };
4763
4779
  function getDeviceIdPath() {
4764
4780
  return path.join(electron.app.getPath("userData"), ".vessel-device-id");
4765
4781
  }
@@ -4775,7 +4791,8 @@ function getDeviceId() {
4775
4791
  deviceId = crypto$1.randomUUID();
4776
4792
  try {
4777
4793
  fs.mkdirSync(path.dirname(idPath), { recursive: true });
4778
- fs.writeFileSync(idPath, deviceId, "utf-8");
4794
+ fs.writeFileSync(idPath, deviceId, { encoding: "utf-8", mode: 384 });
4795
+ fs.chmodSync(idPath, 384);
4779
4796
  } catch {
4780
4797
  }
4781
4798
  return deviceId;
@@ -4788,11 +4805,15 @@ function isEnabled() {
4788
4805
  if (process.env.VESSEL_DEV === "1") return false;
4789
4806
  return loadSettings().telemetryEnabled !== false;
4790
4807
  }
4791
- function sanitizeTelemetryProperties(properties) {
4808
+ function sanitizeTelemetryProperties(properties, allowedKeys) {
4792
4809
  const safe = {};
4793
4810
  for (const [key2, value] of Object.entries(properties)) {
4794
- if (SENSITIVE_PROPERTY_RE.test(key2)) continue;
4811
+ if (allowedKeys && !allowedKeys.has(key2)) continue;
4812
+ if (!allowedKeys?.has(key2) && SENSITIVE_PROPERTY_RE.test(key2)) continue;
4795
4813
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
4814
+ if (typeof value === "string" && SENSITIVE_STRING_VALUE_RE.test(value)) {
4815
+ continue;
4816
+ }
4796
4817
  safe[key2] = typeof value === "string" ? value.slice(0, 120) : value;
4797
4818
  }
4798
4819
  }
@@ -4800,10 +4821,11 @@ function sanitizeTelemetryProperties(properties) {
4800
4821
  }
4801
4822
  function trackEvent(event, properties = {}) {
4802
4823
  if (!isEnabled()) return;
4824
+ const allowedKeys = TELEMETRY_PROPERTY_ALLOWLIST[event] ?? EMPTY_PROPERTY_ALLOWLIST;
4803
4825
  eventQueue.push({
4804
4826
  event,
4805
4827
  properties: {
4806
- ...sanitizeTelemetryProperties(properties),
4828
+ ...sanitizeTelemetryProperties(properties, allowedKeys),
4807
4829
  premium_status: isPremium() ? "premium" : "free",
4808
4830
  app_version: electron.app.getVersion(),
4809
4831
  platform: process.platform,
@@ -6986,7 +7008,7 @@ function isClickReadLoop(names) {
6986
7008
  return clickReadPairs >= 2;
6987
7009
  }
6988
7010
  const ANTHROPIC_MAX_TOKENS = 4096;
6989
- function isRecord(value) {
7011
+ function isRecord$1(value) {
6990
7012
  return value !== null && typeof value === "object" && !Array.isArray(value);
6991
7013
  }
6992
7014
  function anthropicModelLikelySupportsThinking(model) {
@@ -7110,7 +7132,7 @@ class AnthropicProvider {
7110
7132
  } else if (event.type === "content_block_stop" && currentToolUse) {
7111
7133
  try {
7112
7134
  const input = JSON.parse(currentToolUse.inputJson || "{}");
7113
- if (!isRecord(input)) {
7135
+ if (!isRecord$1(input)) {
7114
7136
  throw new Error("Tool input must be a JSON object");
7115
7137
  }
7116
7138
  toolUseBlocks.push({
@@ -8523,6 +8545,9 @@ async function openExternalAllowlisted(url, rule) {
8523
8545
  if (!schemes.includes(parsed.protocol)) {
8524
8546
  throw new Error(`Blocked external URL scheme: ${parsed.protocol}`);
8525
8547
  }
8548
+ if (parsed.username || parsed.password) {
8549
+ throw new Error("Blocked external URL with embedded credentials");
8550
+ }
8526
8551
  if (rule.hosts && !rule.hosts.includes(parsed.hostname)) {
8527
8552
  throw new Error(`Blocked external URL host: ${parsed.hostname}`);
8528
8553
  }
@@ -8593,54 +8618,6 @@ function parseTokenExpiry(accessToken) {
8593
8618
  }
8594
8619
  return Date.now() + 36e5;
8595
8620
  }
8596
- async function exchangeIdTokenForApiKey(idToken) {
8597
- const body = new URLSearchParams({
8598
- grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
8599
- client_id: CLIENT_ID,
8600
- requested_token: "openai-api-key",
8601
- subject_token: idToken,
8602
- subject_token_type: "urn:ietf:params:oauth:token-type:id_token"
8603
- });
8604
- const response = await fetch(`${ISSUER}/oauth/token`, {
8605
- method: "POST",
8606
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
8607
- body: body.toString()
8608
- });
8609
- if (!response.ok) {
8610
- let errorMsg = `OpenAI API token exchange failed: ${response.status}`;
8611
- try {
8612
- const err = await response.json();
8613
- if (typeof err.error_description === "string") {
8614
- errorMsg = err.error_description;
8615
- } else if (typeof err.error === "string") {
8616
- errorMsg = err.error;
8617
- }
8618
- } catch {
8619
- }
8620
- throw new Error(errorMsg);
8621
- }
8622
- const data = await response.json();
8623
- if (!data.access_token) {
8624
- throw new Error("OpenAI API token exchange did not return an access token");
8625
- }
8626
- return data.access_token;
8627
- }
8628
- async function ensureCodexApiKey(tokens) {
8629
- if (tokens.apiKey) return tokens;
8630
- if (!tokens.idToken) return tokens;
8631
- try {
8632
- return {
8633
- ...tokens,
8634
- apiKey: await exchangeIdTokenForApiKey(tokens.idToken)
8635
- };
8636
- } catch (err) {
8637
- logger$g.warn(
8638
- "Codex API-key token exchange failed; continuing with ChatGPT OAuth tokens:",
8639
- err
8640
- );
8641
- return tokens;
8642
- }
8643
- }
8644
8621
  async function exchangeCodeForTokens(code, redirectUri, codeVerifier) {
8645
8622
  const body = new URLSearchParams({
8646
8623
  grant_type: "authorization_code",
@@ -8678,7 +8655,7 @@ async function exchangeCodeForTokens(code, redirectUri, codeVerifier) {
8678
8655
  accountId: claims?.accountId || "",
8679
8656
  accountEmail: claims?.email
8680
8657
  };
8681
- return ensureCodexApiKey(tokens);
8658
+ return tokens;
8682
8659
  }
8683
8660
  async function refreshAccessToken(tokens) {
8684
8661
  const body = new URLSearchParams({
@@ -8714,7 +8691,7 @@ async function refreshAccessToken(tokens) {
8714
8691
  accountId: claims?.accountId || tokens.accountId || "",
8715
8692
  accountEmail: claims?.email || tokens.accountEmail
8716
8693
  };
8717
- return ensureCodexApiKey(refreshedTokens);
8694
+ return refreshedTokens;
8718
8695
  }
8719
8696
  function startServer(port, pkce, expectedState, resolve, reject) {
8720
8697
  const server = http.createServer(async (req, res) => {
@@ -8885,12 +8862,57 @@ const logger$f = createLogger("CodexProvider");
8885
8862
  const REFRESH_WINDOW_MS = 5 * 60 * 1e3;
8886
8863
  const CODEX_BACKEND_BASE_URL = "https://chatgpt.com/backend-api/codex";
8887
8864
  const CODEX_CLIENT_VERSION = "0.129.0";
8865
+ function isRecord(value) {
8866
+ return value !== null && typeof value === "object" && !Array.isArray(value);
8867
+ }
8868
+ async function createCodexFunctionCallOutput(functionCall, availableToolNames, onChunk, onToolCall) {
8869
+ const callId = functionCall.call_id || functionCall.id || "";
8870
+ const name = functionCall.name || "";
8871
+ if (!callId) {
8872
+ return {
8873
+ type: "function_call_output",
8874
+ call_id: callId,
8875
+ output: "Error: Function call was missing a call_id. Please retry the tool call."
8876
+ };
8877
+ }
8878
+ if (!name || !availableToolNames.has(name)) {
8879
+ onChunk(`
8880
+ <<tool:${name || "unknown"}:⚠ unsupported>>
8881
+ `);
8882
+ return {
8883
+ type: "function_call_output",
8884
+ call_id: callId,
8885
+ output: `Error: Unsupported tool${name ? `: ${name}` : ""}. Use one of the provided tools.`
8886
+ };
8887
+ }
8888
+ let args;
8889
+ try {
8890
+ const parsed = JSON.parse(functionCall.arguments || "{}");
8891
+ if (!isRecord(parsed)) throw new Error("Tool arguments must be a JSON object");
8892
+ args = parsed;
8893
+ } catch {
8894
+ onChunk(`
8895
+ <<tool:${name}:⚠ invalid args>>
8896
+ `);
8897
+ return {
8898
+ type: "function_call_output",
8899
+ call_id: callId,
8900
+ output: "Error: Invalid JSON in tool arguments. Please retry with a valid JSON object."
8901
+ };
8902
+ }
8903
+ const output = await onToolCall(name, args);
8904
+ return {
8905
+ type: "function_call_output",
8906
+ call_id: callId,
8907
+ output
8908
+ };
8909
+ }
8888
8910
  class CodexProvider {
8889
8911
  agentToolProfile;
8890
8912
  tokens;
8891
8913
  model;
8892
8914
  abortController = null;
8893
- constructor(tokens, model, _baseUrl) {
8915
+ constructor(tokens, model) {
8894
8916
  this.tokens = tokens;
8895
8917
  this.model = model;
8896
8918
  this.agentToolProfile = "default";
@@ -8909,7 +8931,7 @@ class CodexProvider {
8909
8931
  );
8910
8932
  }
8911
8933
  }
8912
- backendHeaders() {
8934
+ backendHeaders(turnState) {
8913
8935
  const headers = {
8914
8936
  Authorization: `Bearer ${this.tokens.accessToken}`,
8915
8937
  "Content-Type": "application/json",
@@ -8920,6 +8942,9 @@ class CodexProvider {
8920
8942
  if (this.tokens.accountId) {
8921
8943
  headers["ChatGPT-Account-ID"] = this.tokens.accountId;
8922
8944
  }
8945
+ if (turnState) {
8946
+ headers["x-codex-turn-state"] = turnState;
8947
+ }
8923
8948
  return headers;
8924
8949
  }
8925
8950
  buildInput(userMessage, history) {
@@ -8928,7 +8953,7 @@ class CodexProvider {
8928
8953
  input.push({
8929
8954
  type: "message",
8930
8955
  role: msg.role,
8931
- content: [{ type: "input_text", text: msg.content }]
8956
+ content: [{ type: msg.role === "assistant" ? "output_text" : "input_text", text: msg.content }]
8932
8957
  });
8933
8958
  }
8934
8959
  input.push({
@@ -8938,7 +8963,7 @@ class CodexProvider {
8938
8963
  });
8939
8964
  return input;
8940
8965
  }
8941
- handleStreamEvent(raw, onChunk, emittedTextFromDelta) {
8966
+ handleStreamEvent(raw, onChunk, acc) {
8942
8967
  if (!raw.trim() || raw.trim() === "[DONE]") return;
8943
8968
  let event;
8944
8969
  try {
@@ -8947,13 +8972,28 @@ class CodexProvider {
8947
8972
  return;
8948
8973
  }
8949
8974
  if (event.type === "response.output_text.delta" && event.delta) {
8950
- emittedTextFromDelta.value = true;
8975
+ acc.emittedTextFromDelta = true;
8976
+ acc.text += event.delta;
8951
8977
  onChunk(event.delta);
8952
8978
  return;
8953
8979
  }
8954
- if (event.type === "response.output_item.done" && !emittedTextFromDelta.value) {
8955
- const text = event.item?.content?.filter((item) => item.type === "output_text" && item.text).map((item) => item.text).join("");
8956
- if (text) onChunk(text);
8980
+ if (event.type === "response.function_call_arguments.delta" && event.delta) {
8981
+ const key2 = event.call_id || event.item_id || "";
8982
+ if (key2) {
8983
+ acc.functionCallArgs.set(key2, (acc.functionCallArgs.get(key2) || "") + event.delta);
8984
+ }
8985
+ return;
8986
+ }
8987
+ if (event.type === "response.output_item.done" && event.item) {
8988
+ const item = event.item;
8989
+ if (item.type === "function_call") {
8990
+ const key2 = item.call_id || item.id || "";
8991
+ const args = acc.functionCallArgs.get(key2) || item.arguments || "";
8992
+ acc.functionCallArgs.delete(key2);
8993
+ acc.items.push({ ...item, arguments: args });
8994
+ } else if (item.type === "message") {
8995
+ acc.items.push(item);
8996
+ }
8957
8997
  return;
8958
8998
  }
8959
8999
  if (event.type === "response.failed") {
@@ -8962,18 +9002,12 @@ class CodexProvider {
8962
9002
  throw new Error(message);
8963
9003
  }
8964
9004
  }
8965
- async streamCodexResponse(systemPrompt, userMessage, onChunk, history) {
9005
+ async streamCodexResponse(requestBody, onChunk, turnState) {
8966
9006
  const response = await fetch(`${CODEX_BACKEND_BASE_URL}/responses`, {
8967
9007
  method: "POST",
8968
- headers: this.backendHeaders(),
9008
+ headers: this.backendHeaders(turnState),
8969
9009
  signal: this.abortController?.signal,
8970
- body: JSON.stringify({
8971
- model: this.model,
8972
- instructions: systemPrompt,
8973
- input: this.buildInput(userMessage, history),
8974
- stream: true,
8975
- store: false
8976
- })
9010
+ body: JSON.stringify(requestBody)
8977
9011
  });
8978
9012
  if (!response.ok) {
8979
9013
  const text = await response.text().catch(() => "");
@@ -8984,33 +9018,53 @@ class CodexProvider {
8984
9018
  if (!response.body) {
8985
9019
  throw new Error("Codex backend returned an empty response stream");
8986
9020
  }
9021
+ const newTurnState = response.headers.get("x-codex-turn-state") || null;
8987
9022
  const reader = response.body.getReader();
8988
- const decoder = new TextDecoder();
8989
- let buffer = "";
8990
- const emittedTextFromDelta = { value: false };
8991
- while (true) {
8992
- const { value, done } = await reader.read();
8993
- if (done) break;
8994
- buffer += decoder.decode(value, { stream: true });
8995
- let separatorIndex;
8996
- while ((separatorIndex = buffer.indexOf("\n\n")) !== -1) {
8997
- const block = buffer.slice(0, separatorIndex);
8998
- buffer = buffer.slice(separatorIndex + 2);
8999
- const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
9000
- this.handleStreamEvent(data, onChunk, emittedTextFromDelta);
9001
- }
9002
- }
9003
- const trailing = buffer.trim();
9004
- if (trailing) {
9005
- const data = trailing.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
9006
- this.handleStreamEvent(data, onChunk, emittedTextFromDelta);
9023
+ try {
9024
+ const decoder = new TextDecoder();
9025
+ let buffer = "";
9026
+ const acc = {
9027
+ text: "",
9028
+ items: [],
9029
+ emittedTextFromDelta: false,
9030
+ functionCallArgs: /* @__PURE__ */ new Map()
9031
+ };
9032
+ while (true) {
9033
+ const { value, done } = await reader.read();
9034
+ if (done) break;
9035
+ buffer += decoder.decode(value, { stream: true });
9036
+ let separatorIndex;
9037
+ while ((separatorIndex = buffer.indexOf("\n\n")) !== -1) {
9038
+ const block = buffer.slice(0, separatorIndex);
9039
+ buffer = buffer.slice(separatorIndex + 2);
9040
+ const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
9041
+ this.handleStreamEvent(data, onChunk, acc);
9042
+ }
9043
+ }
9044
+ const trailing = buffer.trim();
9045
+ if (trailing) {
9046
+ const data = trailing.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
9047
+ this.handleStreamEvent(data, onChunk, acc);
9048
+ }
9049
+ return { text: acc.text, items: acc.items, turnState: newTurnState };
9050
+ } finally {
9051
+ reader.releaseLock();
9007
9052
  }
9008
9053
  }
9009
9054
  async streamQuery(systemPrompt, userMessage, onChunk, onEnd, history) {
9010
9055
  await this.ensureFreshTokens();
9011
9056
  this.abortController = new AbortController();
9012
9057
  try {
9013
- await this.streamCodexResponse(systemPrompt, userMessage, onChunk, history);
9058
+ await this.streamCodexResponse(
9059
+ {
9060
+ model: this.model,
9061
+ instructions: systemPrompt,
9062
+ input: this.buildInput(userMessage, history),
9063
+ stream: true,
9064
+ store: false
9065
+ },
9066
+ onChunk
9067
+ );
9014
9068
  } catch (err) {
9015
9069
  if (err.name !== "AbortError") {
9016
9070
  const msg = err instanceof Error ? err.message : String(err);
@@ -9024,11 +9078,59 @@ class CodexProvider {
9024
9078
  onEnd();
9025
9079
  }
9026
9080
  }
9027
- async streamAgentQuery(systemPrompt, userMessage, _tools, onChunk, _onToolCall, onEnd, history) {
9081
+ async streamAgentQuery(systemPrompt, userMessage, tools, onChunk, onToolCall, onEnd, history) {
9028
9082
  await this.ensureFreshTokens();
9029
9083
  this.abortController = new AbortController();
9084
+ const maxIterations = getEffectiveMaxIterations();
9085
+ const availableToolNames = new Set(tools.map((tool) => tool.name));
9086
+ let iterationsUsed = 0;
9087
+ const convertedTools = tools.map((tool) => ({
9088
+ type: "function",
9089
+ name: tool.name,
9090
+ description: tool.description || "",
9091
+ parameters: tool.input_schema
9092
+ }));
9093
+ let currentInput = this.buildInput(userMessage, history);
9094
+ let turnState = null;
9030
9095
  try {
9031
- await this.streamCodexResponse(systemPrompt, userMessage, onChunk, history);
9096
+ for (let i = 0; i < maxIterations; i++) {
9097
+ iterationsUsed = i + 1;
9098
+ const result = await this.streamCodexResponse(
9099
+ {
9100
+ model: this.model,
9101
+ instructions: systemPrompt,
9102
+ input: currentInput,
9103
+ tools: convertedTools,
9104
+ stream: true,
9105
+ store: false
9106
+ },
9107
+ onChunk,
9108
+ turnState || void 0
9109
+ );
9110
+ turnState = result.turnState || turnState;
9111
+ const functionCalls = result.items.filter(
9112
+ (item) => item.type === "function_call"
9113
+ );
9114
+ if (functionCalls.length === 0) {
9115
+ break;
9116
+ }
9117
+ currentInput = [];
9118
+ for (const fc of functionCalls) {
9119
+ currentInput.push(
9120
+ await createCodexFunctionCallOutput(
9121
+ fc,
9122
+ availableToolNames,
9123
+ onChunk,
9124
+ onToolCall
9125
+ )
9126
+ );
9127
+ }
9128
+ }
9129
+ if (iterationsUsed >= maxIterations) {
9130
+ onChunk(`
9131
+
9132
+ [Reached maximum tool call limit (${maxIterations} steps). You can adjust this in Settings → Max Tool Iterations, or continue by sending another message.]`);
9133
+ }
9032
9134
  } catch (err) {
9033
9135
  if (err.name !== "AbortError") {
9034
9136
  const msg = err instanceof Error ? err.message : String(err);
@@ -9112,11 +9214,11 @@ function buildLlamaCppCtxWarning(ctxSize) {
9112
9214
  }
9113
9215
  async function fetchCodexBackendModels(tokens) {
9114
9216
  const url = new URL("https://chatgpt.com/backend-api/codex/models");
9115
- url.searchParams.set("client_version", "0.129.0");
9217
+ url.searchParams.set("client_version", CODEX_CLIENT_VERSION);
9116
9218
  const headers = {
9117
9219
  Authorization: `Bearer ${tokens.accessToken}`,
9118
9220
  originator: "codex_cli_rs",
9119
- "User-Agent": "codex_cli_rs/0.129.0 Vessel"
9221
+ "User-Agent": `codex_cli_rs/${CODEX_CLIENT_VERSION} Vessel`
9120
9222
  };
9121
9223
  if (tokens.accountId) {
9122
9224
  headers["ChatGPT-Account-ID"] = tokens.accountId;
@@ -9219,7 +9321,7 @@ function createProvider(config) {
9219
9321
  "OpenAI Codex requires authentication. Open settings to connect your ChatGPT account."
9220
9322
  );
9221
9323
  }
9222
- return new CodexProvider(tokens, normalized.model, normalized.baseUrl);
9324
+ return new CodexProvider(tokens, normalized.model);
9223
9325
  }
9224
9326
  return new OpenAICompatProvider(normalized);
9225
9327
  }
@@ -19524,6 +19626,16 @@ const logger$b = createLogger("VaultShared");
19524
19626
  const ALGORITHM = "aes-256-gcm";
19525
19627
  const IV_LENGTH = 12;
19526
19628
  const AUTH_TAG_LENGTH = 16;
19629
+ const KEY_STORAGE_PREFIX = "base64:";
19630
+ function encodeEncryptionKeyForStorage(key2) {
19631
+ return `${KEY_STORAGE_PREFIX}${key2.toString("base64")}`;
19632
+ }
19633
+ function decodeEncryptionKeyFromStorage(value) {
19634
+ if (value.startsWith(KEY_STORAGE_PREFIX)) {
19635
+ return Buffer.from(value.slice(KEY_STORAGE_PREFIX.length), "base64");
19636
+ }
19637
+ return Buffer.from(value, "utf-8");
19638
+ }
19527
19639
  function assertSecretStorageAvailable(customMessage) {
19528
19640
  if (!electron.safeStorage.isEncryptionAvailable()) {
19529
19641
  throw new Error(
@@ -19536,11 +19648,17 @@ function getOrCreateEncryptionKey(keyFilename) {
19536
19648
  const keyPath = path$1.join(electron.app.getPath("userData"), keyFilename);
19537
19649
  if (fs$1.existsSync(keyPath)) {
19538
19650
  const encryptedKey = fs$1.readFileSync(keyPath);
19539
- return Buffer.from(electron.safeStorage.decryptString(encryptedKey), "utf-8");
19651
+ const key22 = decodeEncryptionKeyFromStorage(
19652
+ electron.safeStorage.decryptString(encryptedKey)
19653
+ );
19654
+ if (key22.length !== 32) {
19655
+ throw new Error("Stored vault encryption key has an invalid length.");
19656
+ }
19657
+ return key22;
19540
19658
  }
19541
19659
  const key2 = crypto$2.randomBytes(32);
19542
19660
  fs$1.mkdirSync(path$1.dirname(keyPath), { recursive: true });
19543
- const encrypted = electron.safeStorage.encryptString(key2.toString("utf-8"));
19661
+ const encrypted = electron.safeStorage.encryptString(encodeEncryptionKeyForStorage(key2));
19544
19662
  fs$1.writeFileSync(keyPath, encrypted, { mode: 384 });
19545
19663
  fs$1.chmodSync(keyPath, 384);
19546
19664
  return key2;
@@ -19607,7 +19725,7 @@ function createVaultIO(vaultFilename, encrypt2, decrypt2) {
19607
19725
  const encrypted = encrypt2(json);
19608
19726
  const vaultPath = getVaultPath();
19609
19727
  fs$1.mkdirSync(path$1.dirname(vaultPath), { recursive: true });
19610
- fs$1.writeFileSync(vaultPath, encrypted);
19728
+ fs$1.writeFileSync(vaultPath, encrypted, { mode: 384 });
19611
19729
  fs$1.chmodSync(vaultPath, 384);
19612
19730
  cachedEntries = entries;
19613
19731
  }
@@ -19663,12 +19781,20 @@ function createAuditLog(filename, maxEntries) {
19663
19781
  try {
19664
19782
  const auditPath = getAuditPath2();
19665
19783
  fs$1.mkdirSync(path$1.dirname(auditPath), { recursive: true });
19666
- fs$1.appendFileSync(auditPath, JSON.stringify(entry) + "\n");
19784
+ fs$1.appendFileSync(auditPath, JSON.stringify(entry) + "\n", {
19785
+ encoding: "utf-8",
19786
+ mode: 384
19787
+ });
19788
+ fs$1.chmodSync(auditPath, 384);
19667
19789
  try {
19668
19790
  const lines = fs$1.readFileSync(auditPath, "utf-8").split("\n").filter((l) => l.trim());
19669
19791
  if (lines.length > maxEntries) {
19670
19792
  const trimmed = lines.slice(-maxEntries);
19671
- fs$1.writeFileSync(auditPath, trimmed.join("\n") + "\n");
19793
+ fs$1.writeFileSync(auditPath, trimmed.join("\n") + "\n", {
19794
+ encoding: "utf-8",
19795
+ mode: 384
19796
+ });
19797
+ fs$1.chmodSync(auditPath, 384);
19672
19798
  }
19673
19799
  } catch {
19674
19800
  }
@@ -19795,7 +19921,11 @@ function appendAuditEntry(entry) {
19795
19921
  try {
19796
19922
  const auditPath = getAuditPath();
19797
19923
  fs$1.mkdirSync(path$1.dirname(auditPath), { recursive: true });
19798
- fs$1.appendFileSync(auditPath, JSON.stringify(entry) + "\n");
19924
+ fs$1.appendFileSync(auditPath, JSON.stringify(entry) + "\n", {
19925
+ encoding: "utf-8",
19926
+ mode: 384
19927
+ });
19928
+ fs$1.chmodSync(auditPath, 384);
19799
19929
  } catch (err) {
19800
19930
  logger$a.error("Failed to write audit log:", err);
19801
19931
  }
@@ -20075,7 +20205,7 @@ function isDangerousMcpAction(name) {
20075
20205
  }
20076
20206
  function requiresExplicitMcpApproval(name, args) {
20077
20207
  if (name === "delete_session" || name === "close_tab" || name === "load_session") return true;
20078
- if (name === "remove_folder" && args.delete_contents === true) return true;
20208
+ if (name === "remove_bookmark_folder" && args.delete_contents === true) return true;
20079
20209
  return false;
20080
20210
  }
20081
20211
  function getActiveTabSummary(tabManager) {
@@ -23909,6 +24039,43 @@ function uninstallKit(id, scheduledKitIds) {
23909
24039
  return errorResult("Failed to remove the kit file.");
23910
24040
  }
23911
24041
  }
24042
+ const trustedIpcSenderIds = /* @__PURE__ */ new Set();
24043
+ function registerTrustedIpcSender(wc) {
24044
+ trustedIpcSenderIds.add(wc.id);
24045
+ wc.once("destroyed", () => trustedIpcSenderIds.delete(wc.id));
24046
+ }
24047
+ function assertTrustedIpcSender(event) {
24048
+ if (!trustedIpcSenderIds.has(event.sender.id)) {
24049
+ throw new Error("Blocked IPC from untrusted renderer");
24050
+ }
24051
+ }
24052
+ function isManagedTabIpcSender(event, tabManager) {
24053
+ return Boolean(tabManager.findTabByWebContentsId(event.sender.id));
24054
+ }
24055
+ function assertString(value, name) {
24056
+ if (typeof value !== "string") throw new Error(`${name} must be a string`);
24057
+ }
24058
+ function assertOptionalString(value, name) {
24059
+ if (value !== void 0 && typeof value !== "string") {
24060
+ throw new Error(`${name} must be a string`);
24061
+ }
24062
+ }
24063
+ function assertNumber(value, name) {
24064
+ if (typeof value !== "number" || Number.isNaN(value)) {
24065
+ throw new Error(`${name} must be a number`);
24066
+ }
24067
+ }
24068
+ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
24069
+ function isValidEmail(value) {
24070
+ return EMAIL_RE.test(value.trim());
24071
+ }
24072
+ function getActiveTabInfo(tabManager) {
24073
+ const tab = tabManager.getActiveTab();
24074
+ if (!tab) return null;
24075
+ const wc = tab.view.webContents;
24076
+ if (wc.isDestroyed()) return null;
24077
+ return { tab, wc };
24078
+ }
23912
24079
  const logger$7 = createLogger("Scheduler");
23913
24080
  let jobs = [];
23914
24081
  let removeIdleListener = null;
@@ -23932,7 +24099,13 @@ function loadJobs() {
23932
24099
  }
23933
24100
  function saveJobs() {
23934
24101
  try {
23935
- fs$1.writeFileSync(getJobsPath(), JSON.stringify(jobs, null, 2), "utf-8");
24102
+ const jobsPath = getJobsPath();
24103
+ fs$1.mkdirSync(path$1.dirname(jobsPath), { recursive: true });
24104
+ fs$1.writeFileSync(jobsPath, JSON.stringify(jobs, null, 2), {
24105
+ encoding: "utf-8",
24106
+ mode: 384
24107
+ });
24108
+ fs$1.chmodSync(jobsPath, 384);
23936
24109
  } catch (err) {
23937
24110
  logger$7.warn("Failed to save jobs:", err);
23938
24111
  }
@@ -24136,8 +24309,12 @@ function registerScheduleHandlers(windowState, runtime2, sendToAll) {
24136
24309
  tick(windowState, runtime2);
24137
24310
  setInterval(() => tick(windowState, runtime2), 6e4);
24138
24311
  }, msToNextMinute);
24139
- electron.ipcMain.handle(Channels.SCHEDULE_GET_ALL, () => jobs);
24140
- electron.ipcMain.handle(Channels.SCHEDULE_CREATE, (_, rawJob) => {
24312
+ electron.ipcMain.handle(Channels.SCHEDULE_GET_ALL, (event) => {
24313
+ assertTrustedIpcSender(event);
24314
+ return jobs;
24315
+ });
24316
+ electron.ipcMain.handle(Channels.SCHEDULE_CREATE, (event, rawJob) => {
24317
+ assertTrustedIpcSender(event);
24141
24318
  if (!isValidJobData(rawJob)) {
24142
24319
  throw new Error(
24143
24320
  "Invalid job data. Required: kitId, kitName, kitIcon, renderedPrompt, schedule, enabled."
@@ -24154,7 +24331,8 @@ function registerScheduleHandlers(windowState, runtime2, sendToAll) {
24154
24331
  sendToAll(Channels.SCHEDULE_JOBS_UPDATE, jobs);
24155
24332
  return newJob;
24156
24333
  });
24157
- electron.ipcMain.handle(Channels.SCHEDULE_UPDATE, (_, id, updates) => {
24334
+ electron.ipcMain.handle(Channels.SCHEDULE_UPDATE, (event, id, updates) => {
24335
+ assertTrustedIpcSender(event);
24158
24336
  if (typeof id !== "string") throw new Error("id must be a string");
24159
24337
  const job = jobs.find((j) => j.id === id);
24160
24338
  if (!job) return null;
@@ -24180,7 +24358,8 @@ function registerScheduleHandlers(windowState, runtime2, sendToAll) {
24180
24358
  sendToAll(Channels.SCHEDULE_JOBS_UPDATE, jobs);
24181
24359
  return job;
24182
24360
  });
24183
- electron.ipcMain.handle(Channels.SCHEDULE_DELETE, (_, id) => {
24361
+ electron.ipcMain.handle(Channels.SCHEDULE_DELETE, (event, id) => {
24362
+ assertTrustedIpcSender(event);
24184
24363
  if (typeof id !== "string") throw new Error("id must be a string");
24185
24364
  const before = jobs.length;
24186
24365
  jobs = jobs.filter((j) => j.id !== id);
@@ -24190,40 +24369,6 @@ function registerScheduleHandlers(windowState, runtime2, sendToAll) {
24190
24369
  return true;
24191
24370
  });
24192
24371
  }
24193
- const trustedIpcSenderIds = /* @__PURE__ */ new Set();
24194
- function registerTrustedIpcSender(wc) {
24195
- trustedIpcSenderIds.add(wc.id);
24196
- wc.once("destroyed", () => trustedIpcSenderIds.delete(wc.id));
24197
- }
24198
- function assertTrustedIpcSender(event) {
24199
- if (!trustedIpcSenderIds.has(event.sender.id)) {
24200
- throw new Error("Blocked IPC from untrusted renderer");
24201
- }
24202
- }
24203
- function assertString(value, name) {
24204
- if (typeof value !== "string") throw new Error(`${name} must be a string`);
24205
- }
24206
- function assertOptionalString(value, name) {
24207
- if (value !== void 0 && typeof value !== "string") {
24208
- throw new Error(`${name} must be a string`);
24209
- }
24210
- }
24211
- function assertNumber(value, name) {
24212
- if (typeof value !== "number" || Number.isNaN(value)) {
24213
- throw new Error(`${name} must be a number`);
24214
- }
24215
- }
24216
- const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
24217
- function isValidEmail(value) {
24218
- return EMAIL_RE.test(value.trim());
24219
- }
24220
- function getActiveTabInfo(tabManager) {
24221
- const tab = tabManager.getActiveTab();
24222
- if (!tab) return null;
24223
- const wc = tab.view.webContents;
24224
- if (wc.isDestroyed()) return null;
24225
- return { tab, wc };
24226
- }
24227
24372
  const SAVE_DEBOUNCE_MS = 250;
24228
24373
  const PROFILE_FIELDS = [
24229
24374
  "label",
@@ -24563,24 +24708,29 @@ function sanitizeAutofillUpdates(value) {
24563
24708
  return updates;
24564
24709
  }
24565
24710
  function registerAutofillHandlers(windowState) {
24566
- electron.ipcMain.handle(Channels.AUTOFILL_LIST, () => {
24711
+ electron.ipcMain.handle(Channels.AUTOFILL_LIST, (event) => {
24712
+ assertTrustedIpcSender(event);
24567
24713
  return listProfiles();
24568
24714
  });
24569
24715
  electron.ipcMain.handle(
24570
24716
  Channels.AUTOFILL_ADD,
24571
- (_, profile) => {
24717
+ (event, profile) => {
24718
+ assertTrustedIpcSender(event);
24572
24719
  return addProfile(sanitizeAutofillProfile(profile));
24573
24720
  }
24574
24721
  );
24575
- electron.ipcMain.handle(Channels.AUTOFILL_UPDATE, (_, id, updates) => {
24722
+ electron.ipcMain.handle(Channels.AUTOFILL_UPDATE, (event, id, updates) => {
24723
+ assertTrustedIpcSender(event);
24576
24724
  assertString(id, "id");
24577
24725
  return updateProfile(id, sanitizeAutofillUpdates(updates));
24578
24726
  });
24579
- electron.ipcMain.handle(Channels.AUTOFILL_DELETE, (_, id) => {
24727
+ electron.ipcMain.handle(Channels.AUTOFILL_DELETE, (event, id) => {
24728
+ assertTrustedIpcSender(event);
24580
24729
  assertString(id, "id");
24581
24730
  return deleteProfile(id);
24582
24731
  });
24583
- electron.ipcMain.handle(Channels.AUTOFILL_FILL, async (_, profileId) => {
24732
+ electron.ipcMain.handle(Channels.AUTOFILL_FILL, async (event, profileId) => {
24733
+ assertTrustedIpcSender(event);
24584
24734
  assertString(profileId, "profileId");
24585
24735
  const profile = getProfile(profileId);
24586
24736
  if (!profile) throw new Error("Profile not found");
@@ -24650,12 +24800,14 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
24650
24800
  electron.ipcMain.on(Channels.PAGE_DIFF_ACTIVITY, (event) => {
24651
24801
  const wc = event.sender;
24652
24802
  if (!wc || wc.isDestroyed()) return;
24803
+ if (!isManagedTabIpcSender(event, windowState.tabManager)) return;
24653
24804
  if (!allowPageEvent(wc.id)) return;
24654
24805
  notePageMutationActivity(wc, sendToRendererViews);
24655
24806
  });
24656
24807
  electron.ipcMain.on(Channels.PAGE_DIFF_DIRTY, (event) => {
24657
24808
  const wc = event.sender;
24658
24809
  if (!wc || wc.isDestroyed()) return;
24810
+ if (!isManagedTabIpcSender(event, windowState.tabManager)) return;
24659
24811
  if (!allowPageEvent(wc.id)) return;
24660
24812
  schedulePageSnapshotCapture(wc, sendToRendererViews);
24661
24813
  });
@@ -25802,19 +25954,22 @@ function getSafeBookmarkExportName(name) {
25802
25954
  return safeName || "folder";
25803
25955
  }
25804
25956
  function registerBookmarkHandlers() {
25805
- electron.ipcMain.handle(Channels.BOOKMARKS_GET, () => {
25957
+ electron.ipcMain.handle(Channels.BOOKMARKS_GET, (event) => {
25958
+ assertTrustedIpcSender(event);
25806
25959
  return getState();
25807
25960
  });
25808
25961
  electron.ipcMain.handle(
25809
25962
  Channels.FOLDER_CREATE,
25810
- (_, name, summary) => {
25963
+ (event, name, summary) => {
25964
+ assertTrustedIpcSender(event);
25811
25965
  trackBookmarkAction("folder_create");
25812
25966
  return createFolderWithSummary(name, summary);
25813
25967
  }
25814
25968
  );
25815
25969
  electron.ipcMain.handle(
25816
25970
  Channels.BOOKMARK_SAVE,
25817
- (_, url, title, folderId, note, intent, expectedContent, keyFields, agentHints) => {
25971
+ (event, url, title, folderId, note, intent, expectedContent, keyFields, agentHints) => {
25972
+ assertTrustedIpcSender(event);
25818
25973
  trackBookmarkAction("save");
25819
25974
  const result = saveBookmarkWithPolicy(url, title, folderId, note, {
25820
25975
  onDuplicate: "update",
@@ -25835,12 +25990,14 @@ function registerBookmarkHandlers() {
25835
25990
  );
25836
25991
  electron.ipcMain.handle(
25837
25992
  Channels.BOOKMARK_UPDATE,
25838
- (_, id, updates) => {
25993
+ (event, id, updates) => {
25994
+ assertTrustedIpcSender(event);
25839
25995
  trackBookmarkAction("save");
25840
25996
  return updateBookmark(id, updates);
25841
25997
  }
25842
25998
  );
25843
- electron.ipcMain.handle(Channels.BOOKMARK_REMOVE, (_, id) => {
25999
+ electron.ipcMain.handle(Channels.BOOKMARK_REMOVE, (event, id) => {
26000
+ assertTrustedIpcSender(event);
25844
26001
  trackBookmarkAction("remove");
25845
26002
  return removeBookmark(id);
25846
26003
  });
@@ -25933,22 +26090,26 @@ function registerBookmarkHandlers() {
25933
26090
  trackBookmarkAction("import");
25934
26091
  return importBookmarksFromJson(content);
25935
26092
  });
25936
- electron.ipcMain.handle(Channels.FOLDER_REMOVE, (_, id, deleteContents) => {
26093
+ electron.ipcMain.handle(Channels.FOLDER_REMOVE, (event, id, deleteContents) => {
26094
+ assertTrustedIpcSender(event);
25937
26095
  trackBookmarkAction("folder_remove");
25938
26096
  return removeFolder(id, deleteContents ?? false);
25939
26097
  });
25940
26098
  electron.ipcMain.handle(
25941
26099
  Channels.FOLDER_RENAME,
25942
- (_, id, newName, summary) => {
26100
+ (event, id, newName, summary) => {
26101
+ assertTrustedIpcSender(event);
25943
26102
  return renameFolder(id, newName, summary);
25944
26103
  }
25945
26104
  );
25946
26105
  }
25947
26106
  function registerHistoryHandlers() {
25948
- electron.ipcMain.handle(Channels.HISTORY_GET, () => {
26107
+ electron.ipcMain.handle(Channels.HISTORY_GET, (event) => {
26108
+ assertTrustedIpcSender(event);
25949
26109
  return getState$1();
25950
26110
  });
25951
- electron.ipcMain.handle(Channels.HISTORY_SEARCH, (_, query) => {
26111
+ electron.ipcMain.handle(Channels.HISTORY_SEARCH, (event, query) => {
26112
+ assertTrustedIpcSender(event);
25952
26113
  return search(query);
25953
26114
  });
25954
26115
  electron.ipcMain.handle(Channels.HISTORY_CLEAR, (event) => {
@@ -26073,10 +26234,12 @@ function registerPremiumHandlers(tabManager, sendToRendererViews) {
26073
26234
  void handleUrl(currentUrl);
26074
26235
  }
26075
26236
  };
26076
- electron.ipcMain.handle(Channels.PREMIUM_GET_STATE, () => {
26237
+ electron.ipcMain.handle(Channels.PREMIUM_GET_STATE, (event) => {
26238
+ assertTrustedIpcSender(event);
26077
26239
  return getPremiumState();
26078
26240
  });
26079
- electron.ipcMain.handle(Channels.PREMIUM_ACTIVATION_START, async (_, email) => {
26241
+ electron.ipcMain.handle(Channels.PREMIUM_ACTIVATION_START, async (event, email) => {
26242
+ assertTrustedIpcSender(event);
26080
26243
  assertString(email, "email");
26081
26244
  if (!isValidEmail(email)) {
26082
26245
  return errorResult("Invalid email format");
@@ -26090,7 +26253,8 @@ function registerPremiumHandlers(tabManager, sendToRendererViews) {
26090
26253
  });
26091
26254
  electron.ipcMain.handle(
26092
26255
  Channels.PREMIUM_ACTIVATION_VERIFY,
26093
- async (_, email, code, challengeToken) => {
26256
+ async (event, email, code, challengeToken) => {
26257
+ assertTrustedIpcSender(event);
26094
26258
  assertString(email, "email");
26095
26259
  assertString(code, "code");
26096
26260
  assertString(challengeToken, "challengeToken");
@@ -26112,7 +26276,8 @@ function registerPremiumHandlers(tabManager, sendToRendererViews) {
26112
26276
  return result;
26113
26277
  }
26114
26278
  );
26115
- electron.ipcMain.handle(Channels.PREMIUM_CHECKOUT, async (_, email) => {
26279
+ electron.ipcMain.handle(Channels.PREMIUM_CHECKOUT, async (event, email) => {
26280
+ assertTrustedIpcSender(event);
26116
26281
  trackPremiumFunnel("checkout_clicked");
26117
26282
  const result = await getCheckoutUrl(email);
26118
26283
  if (result.ok && result.url) {
@@ -26121,19 +26286,22 @@ function registerPremiumHandlers(tabManager, sendToRendererViews) {
26121
26286
  }
26122
26287
  return result;
26123
26288
  });
26124
- electron.ipcMain.handle(Channels.PREMIUM_RESET, () => {
26289
+ electron.ipcMain.handle(Channels.PREMIUM_RESET, (event) => {
26290
+ assertTrustedIpcSender(event);
26125
26291
  trackPremiumFunnel("reset");
26126
26292
  const state2 = resetPremium();
26127
26293
  sendToRendererViews(Channels.PREMIUM_UPDATE, state2);
26128
26294
  return state2;
26129
26295
  });
26130
- electron.ipcMain.handle(Channels.PREMIUM_TRACK_CONTEXT, (_, step) => {
26296
+ electron.ipcMain.handle(Channels.PREMIUM_TRACK_CONTEXT, (event, step) => {
26297
+ assertTrustedIpcSender(event);
26131
26298
  assertString(step, "step");
26132
26299
  if (PREMIUM_TRACKABLE_STEPS.includes(step)) {
26133
26300
  trackPremiumFunnel(step);
26134
26301
  }
26135
26302
  });
26136
- electron.ipcMain.handle(Channels.PREMIUM_PORTAL, async () => {
26303
+ electron.ipcMain.handle(Channels.PREMIUM_PORTAL, async (event) => {
26304
+ assertTrustedIpcSender(event);
26137
26305
  trackPremiumFunnel("portal_opened");
26138
26306
  const result = await getPortalUrl();
26139
26307
  if (result.ok && result.url) {
@@ -26143,18 +26311,22 @@ function registerPremiumHandlers(tabManager, sendToRendererViews) {
26143
26311
  });
26144
26312
  }
26145
26313
  function registerSessionHandlers(tabManager) {
26146
- electron.ipcMain.handle(Channels.SESSION_LIST, () => {
26314
+ electron.ipcMain.handle(Channels.SESSION_LIST, (event) => {
26315
+ assertTrustedIpcSender(event);
26147
26316
  return listNamedSessions();
26148
26317
  });
26149
- electron.ipcMain.handle(Channels.SESSION_SAVE, async (_, name) => {
26318
+ electron.ipcMain.handle(Channels.SESSION_SAVE, async (event, name) => {
26319
+ assertTrustedIpcSender(event);
26150
26320
  assertString(name, "name");
26151
26321
  return await saveNamedSession(tabManager, name);
26152
26322
  });
26153
- electron.ipcMain.handle(Channels.SESSION_LOAD, async (_, name) => {
26323
+ electron.ipcMain.handle(Channels.SESSION_LOAD, async (event, name) => {
26324
+ assertTrustedIpcSender(event);
26154
26325
  assertString(name, "name");
26155
26326
  return await loadNamedSession(tabManager, name);
26156
26327
  });
26157
- electron.ipcMain.handle(Channels.SESSION_DELETE, (_, name) => {
26328
+ electron.ipcMain.handle(Channels.SESSION_DELETE, (event, name) => {
26329
+ assertTrustedIpcSender(event);
26158
26330
  assertString(name, "name");
26159
26331
  return deleteNamedSession(name);
26160
26332
  });
@@ -26312,6 +26484,10 @@ function snapshot() {
26312
26484
  function emit() {
26313
26485
  broadcaster?.(Channels.PERMISSIONS_GET, snapshot());
26314
26486
  }
26487
+ function getDecision(origin, permission) {
26488
+ const existing = records.find((r) => r.origin === origin && r.permission === permission);
26489
+ return existing?.decision ?? sessionDecisions.get(key(origin, permission)) ?? null;
26490
+ }
26315
26491
  function save(origin, permission, decision) {
26316
26492
  const k = key(origin, permission);
26317
26493
  const existing = records.find((r) => key(r.origin, r.permission) === k);
@@ -26344,6 +26520,12 @@ function setPermissionBroadcaster(fn) {
26344
26520
  broadcaster = fn;
26345
26521
  }
26346
26522
  function installPermissionHandler() {
26523
+ electron.session.defaultSession.setPermissionCheckHandler((webContents, permission, requestingOrigin) => {
26524
+ if (!ALLOWED_PERMISSION_TYPES.has(permission)) return false;
26525
+ const origin = parseOrigin(requestingOrigin || webContents.getURL());
26526
+ if (!origin) return false;
26527
+ return getDecision(origin, permission) === "allow";
26528
+ });
26347
26529
  electron.session.defaultSession.setPermissionRequestHandler((webContents, permission, callback, details) => {
26348
26530
  if (!ALLOWED_PERMISSION_TYPES.has(permission)) {
26349
26531
  callback(false);
@@ -26355,19 +26537,21 @@ function installPermissionHandler() {
26355
26537
  return;
26356
26538
  }
26357
26539
  const k = key(origin, permission);
26358
- const existing = records.find((r) => r.origin === origin && r.permission === permission);
26359
- if (existing) {
26360
- callback(existing.decision === "allow");
26361
- return;
26362
- }
26363
- const sessionDecision = sessionDecisions.get(k);
26364
- if (sessionDecision) {
26365
- callback(sessionDecision === "allow");
26540
+ const decision = getDecision(origin, permission);
26541
+ if (decision) {
26542
+ callback(decision === "allow");
26366
26543
  return;
26367
26544
  }
26368
26545
  const result = electron.dialog.showMessageBoxSync({
26369
26546
  type: "question",
26370
- buttons: ["Deny", "Allow Once", "Allow Until Quit", "Always Allow"],
26547
+ buttons: [
26548
+ "Deny Once",
26549
+ "Deny Until Quit",
26550
+ "Always Deny",
26551
+ "Allow Once",
26552
+ "Allow Until Quit",
26553
+ "Always Allow"
26554
+ ],
26371
26555
  defaultId: 0,
26372
26556
  cancelId: 0,
26373
26557
  title: "Site permission request",
@@ -26375,20 +26559,29 @@ function installPermissionHandler() {
26375
26559
  detail: "Temporary choices are safer for camera, microphone, location, and clipboard access. Persistent choices can be cleared in Settings > Privacy."
26376
26560
  });
26377
26561
  if (result === 1) {
26378
- callback(true);
26562
+ sessionDecisions.set(k, "deny");
26563
+ callback(false);
26379
26564
  return;
26380
26565
  }
26381
26566
  if (result === 2) {
26567
+ save(origin, permission, "deny");
26568
+ callback(false);
26569
+ return;
26570
+ }
26571
+ if (result === 3) {
26572
+ callback(true);
26573
+ return;
26574
+ }
26575
+ if (result === 4) {
26382
26576
  sessionDecisions.set(k, "allow");
26383
26577
  callback(true);
26384
26578
  return;
26385
26579
  }
26386
- if (result === 3) {
26580
+ if (result === 5) {
26387
26581
  save(origin, permission, "allow");
26388
26582
  callback(true);
26389
26583
  return;
26390
26584
  }
26391
- save(origin, permission, "deny");
26392
26585
  callback(false);
26393
26586
  });
26394
26587
  }
@@ -26491,7 +26684,10 @@ function registerIpcHandlers(windowState, runtime2) {
26491
26684
  requireTrusted(event);
26492
26685
  createSecondaryWindow();
26493
26686
  });
26494
- electron.ipcMain.handle(Channels.IS_PRIVATE_MODE, () => false);
26687
+ electron.ipcMain.handle(Channels.IS_PRIVATE_MODE, (event) => {
26688
+ requireTrusted(event);
26689
+ return false;
26690
+ });
26495
26691
  let sidebarResizeRecoveryTimer = null;
26496
26692
  let sidebarResizeActive = false;
26497
26693
  let runtimeUpdateTimer = null;
@@ -26572,37 +26768,45 @@ function registerIpcHandlers(windowState, runtime2) {
26572
26768
  onAIStreamIdle(() => {
26573
26769
  sendToRendererViews(Channels.AI_STREAM_IDLE);
26574
26770
  });
26575
- electron.ipcMain.handle(Channels.TAB_CREATE, (_, url) => {
26771
+ electron.ipcMain.handle(Channels.TAB_CREATE, (event, url) => {
26772
+ requireTrusted(event);
26576
26773
  const id = tabManager.createTab(url || loadSettings().defaultUrl);
26577
26774
  layoutViews(windowState);
26578
26775
  return id;
26579
26776
  });
26580
- electron.ipcMain.handle(Channels.TAB_CLOSE, (_, id) => {
26777
+ electron.ipcMain.handle(Channels.TAB_CLOSE, (event, id) => {
26778
+ requireTrusted(event);
26581
26779
  tabManager.closeTab(id);
26582
26780
  layoutViews(windowState);
26583
26781
  });
26584
- electron.ipcMain.handle(Channels.TAB_SWITCH, (_, id) => {
26782
+ electron.ipcMain.handle(Channels.TAB_SWITCH, (event, id) => {
26783
+ requireTrusted(event);
26585
26784
  tabManager.switchTab(id);
26586
26785
  layoutViews(windowState);
26587
26786
  });
26588
26787
  electron.ipcMain.handle(
26589
26788
  Channels.TAB_NAVIGATE,
26590
- (_, id, url, postBody) => {
26789
+ (event, id, url, postBody) => {
26790
+ requireTrusted(event);
26591
26791
  assertString(id, "tabId");
26592
26792
  assertString(url, "url");
26593
26793
  return tabManager.navigateTab(id, url, postBody);
26594
26794
  }
26595
26795
  );
26596
- electron.ipcMain.handle(Channels.TAB_BACK, (_, id) => {
26796
+ electron.ipcMain.handle(Channels.TAB_BACK, (event, id) => {
26797
+ requireTrusted(event);
26597
26798
  tabManager.goBack(id);
26598
26799
  });
26599
- electron.ipcMain.handle(Channels.TAB_FORWARD, (_, id) => {
26800
+ electron.ipcMain.handle(Channels.TAB_FORWARD, (event, id) => {
26801
+ requireTrusted(event);
26600
26802
  tabManager.goForward(id);
26601
26803
  });
26602
- electron.ipcMain.handle(Channels.TAB_RELOAD, (_, id) => {
26804
+ electron.ipcMain.handle(Channels.TAB_RELOAD, (event, id) => {
26805
+ requireTrusted(event);
26603
26806
  tabManager.reloadTab(id);
26604
26807
  });
26605
- electron.ipcMain.handle(Channels.TAB_TOGGLE_AD_BLOCK, (_, id) => {
26808
+ electron.ipcMain.handle(Channels.TAB_TOGGLE_AD_BLOCK, (event, id) => {
26809
+ requireTrusted(event);
26606
26810
  assertString(id, "id");
26607
26811
  const tab = tabManager.getTab(id);
26608
26812
  if (!tab) return null;
@@ -26610,87 +26814,108 @@ function registerIpcHandlers(windowState, runtime2) {
26610
26814
  tab.setAdBlockingEnabled(newState);
26611
26815
  return newState;
26612
26816
  });
26613
- electron.ipcMain.handle(Channels.TAB_ZOOM_IN, (_, id) => {
26817
+ electron.ipcMain.handle(Channels.TAB_ZOOM_IN, (event, id) => {
26818
+ requireTrusted(event);
26614
26819
  assertString(id, "id");
26615
26820
  tabManager.zoomIn(id);
26616
26821
  });
26617
- electron.ipcMain.handle(Channels.TAB_ZOOM_OUT, (_, id) => {
26822
+ electron.ipcMain.handle(Channels.TAB_ZOOM_OUT, (event, id) => {
26823
+ requireTrusted(event);
26618
26824
  assertString(id, "id");
26619
26825
  tabManager.zoomOut(id);
26620
26826
  });
26621
- electron.ipcMain.handle(Channels.TAB_ZOOM_RESET, (_, id) => {
26827
+ electron.ipcMain.handle(Channels.TAB_ZOOM_RESET, (event, id) => {
26828
+ requireTrusted(event);
26622
26829
  assertString(id, "id");
26623
26830
  tabManager.zoomReset(id);
26624
26831
  });
26625
- electron.ipcMain.handle(Channels.TAB_REOPEN_CLOSED, () => {
26832
+ electron.ipcMain.handle(Channels.TAB_REOPEN_CLOSED, (event) => {
26833
+ requireTrusted(event);
26626
26834
  const id = tabManager.reopenClosedTab();
26627
26835
  if (id) layoutViews(windowState);
26628
26836
  return id;
26629
26837
  });
26630
- electron.ipcMain.handle(Channels.TAB_DUPLICATE, (_, id) => {
26838
+ electron.ipcMain.handle(Channels.TAB_DUPLICATE, (event, id) => {
26839
+ requireTrusted(event);
26631
26840
  assertString(id, "id");
26632
26841
  const newId = tabManager.duplicateTab(id);
26633
26842
  if (newId) layoutViews(windowState);
26634
26843
  return newId;
26635
26844
  });
26636
- electron.ipcMain.handle(Channels.TAB_PIN, (_, id) => {
26845
+ electron.ipcMain.handle(Channels.TAB_PIN, (event, id) => {
26846
+ requireTrusted(event);
26637
26847
  assertString(id, "id");
26638
26848
  tabManager.pinTab(id);
26639
26849
  });
26640
- electron.ipcMain.handle(Channels.TAB_UNPIN, (_, id) => {
26850
+ electron.ipcMain.handle(Channels.TAB_UNPIN, (event, id) => {
26851
+ requireTrusted(event);
26641
26852
  assertString(id, "id");
26642
26853
  tabManager.unpinTab(id);
26643
26854
  });
26644
- electron.ipcMain.handle(Channels.TAB_GROUP_CREATE, (_, id) => {
26855
+ electron.ipcMain.handle(Channels.TAB_GROUP_CREATE, (event, id) => {
26856
+ requireTrusted(event);
26645
26857
  assertString(id, "id");
26646
26858
  return tabManager.createGroupFromTab(id);
26647
26859
  });
26648
- electron.ipcMain.handle(Channels.TAB_GROUP_ADD_TAB, (_, id, groupId) => {
26860
+ electron.ipcMain.handle(Channels.TAB_GROUP_ADD_TAB, (event, id, groupId) => {
26861
+ requireTrusted(event);
26649
26862
  assertString(id, "id");
26650
26863
  assertString(groupId, "groupId");
26651
26864
  tabManager.assignTabToGroup(id, groupId);
26652
26865
  });
26653
- electron.ipcMain.handle(Channels.TAB_GROUP_REMOVE_TAB, (_, id) => {
26866
+ electron.ipcMain.handle(Channels.TAB_GROUP_REMOVE_TAB, (event, id) => {
26867
+ requireTrusted(event);
26654
26868
  assertString(id, "id");
26655
26869
  tabManager.removeTabFromGroup(id);
26656
26870
  });
26657
- electron.ipcMain.handle(Channels.TAB_GROUP_TOGGLE_COLLAPSED, (_, groupId) => {
26871
+ electron.ipcMain.handle(Channels.TAB_GROUP_TOGGLE_COLLAPSED, (event, groupId) => {
26872
+ requireTrusted(event);
26658
26873
  assertString(groupId, "groupId");
26659
26874
  return tabManager.toggleGroupCollapsed(groupId);
26660
26875
  });
26661
26876
  electron.ipcMain.handle(
26662
26877
  Channels.TAB_GROUP_SET_COLOR,
26663
- (_, groupId, color) => {
26878
+ (event, groupId, color) => {
26879
+ requireTrusted(event);
26664
26880
  assertString(groupId, "groupId");
26665
26881
  assertString(color, "color");
26666
26882
  tabManager.setGroupColor(groupId, color);
26667
26883
  }
26668
26884
  );
26669
- electron.ipcMain.handle(Channels.TAB_TOGGLE_MUTE, (_, id) => {
26885
+ electron.ipcMain.handle(Channels.TAB_TOGGLE_MUTE, (event, id) => {
26886
+ requireTrusted(event);
26670
26887
  assertString(id, "id");
26671
26888
  return tabManager.toggleMuted(id);
26672
26889
  });
26673
- electron.ipcMain.handle(Channels.TAB_PRINT, (_, id) => {
26890
+ electron.ipcMain.handle(Channels.TAB_PRINT, (event, id) => {
26891
+ requireTrusted(event);
26674
26892
  assertString(id, "id");
26675
26893
  tabManager.printTab(id);
26676
26894
  });
26677
- electron.ipcMain.handle(Channels.TAB_PRINT_TO_PDF, (_, id) => {
26895
+ electron.ipcMain.handle(Channels.TAB_PRINT_TO_PDF, (event, id) => {
26896
+ requireTrusted(event);
26678
26897
  assertString(id, "id");
26679
26898
  return tabManager.saveTabAsPdf(id);
26680
26899
  });
26681
- electron.ipcMain.on(Channels.TAB_CONTEXT_MENU, (_event, id) => {
26900
+ electron.ipcMain.on(Channels.TAB_CONTEXT_MENU, (event, id) => {
26901
+ requireTrusted(event);
26682
26902
  assertString(id, "id");
26683
26903
  showTabContextMenu(tabManager, id, mainWindow, () => layoutViews(windowState));
26684
26904
  });
26685
- electron.ipcMain.on(Channels.TAB_GROUP_CONTEXT_MENU, (_event, groupId) => {
26905
+ electron.ipcMain.on(Channels.TAB_GROUP_CONTEXT_MENU, (event, groupId) => {
26906
+ requireTrusted(event);
26686
26907
  assertString(groupId, "groupId");
26687
26908
  showGroupContextMenu(tabManager, groupId, mainWindow);
26688
26909
  });
26689
- electron.ipcMain.handle(Channels.TAB_STATE_GET, () => ({
26690
- tabs: tabManager.getAllStates(),
26691
- activeId: tabManager.getActiveTabId() || ""
26692
- }));
26693
- electron.ipcMain.handle(Channels.AI_QUERY, async (_, query, history) => {
26910
+ electron.ipcMain.handle(Channels.TAB_STATE_GET, (event) => {
26911
+ requireTrusted(event);
26912
+ return {
26913
+ tabs: tabManager.getAllStates(),
26914
+ activeId: tabManager.getActiveTabId() || ""
26915
+ };
26916
+ });
26917
+ electron.ipcMain.handle(Channels.AI_QUERY, async (event, query, history) => {
26918
+ requireTrusted(event);
26694
26919
  const settings2 = loadSettings();
26695
26920
  const chatConfig = settings2.chatProvider;
26696
26921
  if (!chatConfig) {
@@ -26733,10 +26958,12 @@ function registerIpcHandlers(windowState, runtime2) {
26733
26958
  })();
26734
26959
  return { accepted: true };
26735
26960
  });
26736
- electron.ipcMain.handle(Channels.AI_CANCEL, () => {
26961
+ electron.ipcMain.handle(Channels.AI_CANCEL, (event) => {
26962
+ requireTrusted(event);
26737
26963
  activeChatProvider?.cancel();
26738
26964
  });
26739
- electron.ipcMain.handle(Channels.AI_FETCH_MODELS, async (_, config) => {
26965
+ electron.ipcMain.handle(Channels.AI_FETCH_MODELS, async (event, config) => {
26966
+ requireTrusted(event);
26740
26967
  try {
26741
26968
  if (!config || typeof config !== "object" || !("id" in config)) {
26742
26969
  return errorResult("Invalid provider configuration", { models: [] });
@@ -26748,12 +26975,14 @@ function registerIpcHandlers(windowState, runtime2) {
26748
26975
  return errorResult(getErrorMessage(err), { models: [] });
26749
26976
  }
26750
26977
  });
26751
- electron.ipcMain.handle(Channels.CONTENT_EXTRACT, async () => {
26978
+ electron.ipcMain.handle(Channels.CONTENT_EXTRACT, async (event) => {
26979
+ requireTrusted(event);
26752
26980
  const activeTab = tabManager.getActiveTab();
26753
26981
  if (!activeTab) return null;
26754
26982
  return extractContent(activeTab.view.webContents);
26755
26983
  });
26756
- electron.ipcMain.handle(Channels.READER_MODE_TOGGLE, async () => {
26984
+ electron.ipcMain.handle(Channels.READER_MODE_TOGGLE, async (event) => {
26985
+ requireTrusted(event);
26757
26986
  const activeTab = tabManager.getActiveTab();
26758
26987
  if (!activeTab) return;
26759
26988
  if (activeTab.state.isReaderMode) {
@@ -26773,7 +27002,8 @@ function registerIpcHandlers(windowState, runtime2) {
26773
27002
  );
26774
27003
  }
26775
27004
  });
26776
- electron.ipcMain.handle(Channels.SIDEBAR_TOGGLE, () => {
27005
+ electron.ipcMain.handle(Channels.SIDEBAR_TOGGLE, (event) => {
27006
+ requireTrusted(event);
26777
27007
  windowState.uiState.sidebarOpen = !windowState.uiState.sidebarOpen;
26778
27008
  layoutViews(windowState);
26779
27009
  return {
@@ -26781,7 +27011,8 @@ function registerIpcHandlers(windowState, runtime2) {
26781
27011
  width: windowState.uiState.sidebarWidth
26782
27012
  };
26783
27013
  });
26784
- electron.ipcMain.handle(Channels.SIDEBAR_NAVIGATE, (_, tab) => {
27014
+ electron.ipcMain.handle(Channels.SIDEBAR_NAVIGATE, (event, tab) => {
27015
+ requireTrusted(event);
26785
27016
  assertString(tab, "tab");
26786
27017
  if (!windowState.uiState.sidebarOpen) {
26787
27018
  windowState.uiState.sidebarOpen = true;
@@ -26795,7 +27026,8 @@ function registerIpcHandlers(windowState, runtime2) {
26795
27026
  width: windowState.uiState.sidebarWidth
26796
27027
  };
26797
27028
  });
26798
- electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_START, () => {
27029
+ electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_START, (event) => {
27030
+ requireTrusted(event);
26799
27031
  sidebarResizeActive = true;
26800
27032
  clearSidebarResizeRecoveryTimer();
26801
27033
  const [width, height] = windowState.mainWindow.getContentSize();
@@ -26810,14 +27042,16 @@ function registerIpcHandlers(windowState, runtime2) {
26810
27042
  });
26811
27043
  scheduleSidebarResizeRecovery();
26812
27044
  });
26813
- electron.ipcMain.handle(Channels.SIDEBAR_RESIZE, (_, width) => {
27045
+ electron.ipcMain.handle(Channels.SIDEBAR_RESIZE, (event, width) => {
27046
+ requireTrusted(event);
26814
27047
  assertNumber(width, "width");
26815
27048
  const clamped = Math.max(240, Math.min(800, Math.round(width)));
26816
27049
  windowState.uiState.sidebarWidth = clamped;
26817
27050
  resizeSidebarViews(windowState);
26818
27051
  return clamped;
26819
27052
  });
26820
- electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_COMMIT, () => {
27053
+ electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_COMMIT, (event) => {
27054
+ requireTrusted(event);
26821
27055
  sidebarResizeActive = false;
26822
27056
  clearSidebarResizeRecoveryTimer();
26823
27057
  setSetting("sidebarWidth", windowState.uiState.sidebarWidth);
@@ -26825,7 +27059,8 @@ function registerIpcHandlers(windowState, runtime2) {
26825
27059
  });
26826
27060
  electron.ipcMain.on(
26827
27061
  Channels.RENDERER_VIEW_READY,
26828
- (_event, view) => {
27062
+ (event, view) => {
27063
+ requireTrusted(event);
26829
27064
  if (view !== "sidebar") return;
26830
27065
  if (!windowState.uiState.sidebarOpen) {
26831
27066
  windowState.uiState.sidebarOpen = true;
@@ -26833,12 +27068,14 @@ function registerIpcHandlers(windowState, runtime2) {
26833
27068
  }
26834
27069
  }
26835
27070
  );
26836
- electron.ipcMain.handle(Channels.FOCUS_MODE_TOGGLE, () => {
27071
+ electron.ipcMain.handle(Channels.FOCUS_MODE_TOGGLE, (event) => {
27072
+ requireTrusted(event);
26837
27073
  windowState.uiState.focusMode = !windowState.uiState.focusMode;
26838
27074
  layoutViews(windowState);
26839
27075
  return windowState.uiState.focusMode;
26840
27076
  });
26841
- electron.ipcMain.handle(Channels.SETTINGS_VISIBILITY, (_, open) => {
27077
+ electron.ipcMain.handle(Channels.SETTINGS_VISIBILITY, (event, open) => {
27078
+ requireTrusted(event);
26842
27079
  windowState.uiState.settingsOpen = open;
26843
27080
  if (open) {
26844
27081
  windowState.uiState.sidebarOpen = false;
@@ -26846,10 +27083,14 @@ function registerIpcHandlers(windowState, runtime2) {
26846
27083
  layoutViews(windowState);
26847
27084
  return windowState.uiState.settingsOpen;
26848
27085
  });
26849
- electron.ipcMain.handle(Channels.SETTINGS_GET, () => {
27086
+ electron.ipcMain.handle(Channels.SETTINGS_GET, (event) => {
27087
+ requireTrusted(event);
26850
27088
  return getRendererSettings();
26851
27089
  });
26852
- electron.ipcMain.handle(Channels.SETTINGS_HEALTH_GET, () => getRuntimeHealth());
27090
+ electron.ipcMain.handle(Channels.SETTINGS_HEALTH_GET, (event) => {
27091
+ requireTrusted(event);
27092
+ return getRuntimeHealth();
27093
+ });
26853
27094
  electron.ipcMain.handle(Channels.MCP_REGENERATE_TOKEN, (event) => {
26854
27095
  requireTrusted(event);
26855
27096
  return regenerateMcpAuthToken();
@@ -26874,7 +27115,10 @@ function registerIpcHandlers(windowState, runtime2) {
26874
27115
  sendToRendererViews(Channels.SETTINGS_UPDATE, rendererSettings);
26875
27116
  return rendererSettings;
26876
27117
  });
26877
- electron.ipcMain.handle(Channels.AGENT_RUNTIME_GET, () => runtime2.getState());
27118
+ electron.ipcMain.handle(Channels.AGENT_RUNTIME_GET, (event) => {
27119
+ requireTrusted(event);
27120
+ return runtime2.getState();
27121
+ });
26878
27122
  electron.ipcMain.handle(Channels.AGENT_PAUSE, (event) => {
26879
27123
  requireTrusted(event);
26880
27124
  return runtime2.pause();
@@ -26905,24 +27149,27 @@ function registerIpcHandlers(windowState, runtime2) {
26905
27149
  );
26906
27150
  electron.ipcMain.handle(
26907
27151
  Channels.AGENT_CHECKPOINT_CREATE,
26908
- (_, name, note) => runtime2.createCheckpoint(name, note)
26909
- );
26910
- electron.ipcMain.handle(
26911
- Channels.AGENT_CHECKPOINT_RESTORE,
26912
- (_, checkpointId) => runtime2.restoreCheckpoint(checkpointId)
26913
- );
26914
- electron.ipcMain.handle(
26915
- Channels.AGENT_CHECKPOINT_UPDATE_NOTE,
26916
- (_, checkpointId, note) => runtime2.updateCheckpointNote(checkpointId, note || "")
26917
- );
26918
- electron.ipcMain.handle(
26919
- Channels.AGENT_UNDO_LAST_ACTION,
26920
- () => runtime2.undoLastAction()
26921
- );
26922
- electron.ipcMain.handle(
26923
- Channels.AGENT_SESSION_CAPTURE,
26924
- (_, note) => runtime2.captureSession(note)
27152
+ (event, name, note) => {
27153
+ requireTrusted(event);
27154
+ return runtime2.createCheckpoint(name, note);
27155
+ }
26925
27156
  );
27157
+ electron.ipcMain.handle(Channels.AGENT_CHECKPOINT_RESTORE, (event, checkpointId) => {
27158
+ requireTrusted(event);
27159
+ return runtime2.restoreCheckpoint(checkpointId);
27160
+ });
27161
+ electron.ipcMain.handle(Channels.AGENT_CHECKPOINT_UPDATE_NOTE, (event, checkpointId, note) => {
27162
+ requireTrusted(event);
27163
+ return runtime2.updateCheckpointNote(checkpointId, note || "");
27164
+ });
27165
+ electron.ipcMain.handle(Channels.AGENT_UNDO_LAST_ACTION, (event) => {
27166
+ requireTrusted(event);
27167
+ return runtime2.undoLastAction();
27168
+ });
27169
+ electron.ipcMain.handle(Channels.AGENT_SESSION_CAPTURE, (event, note) => {
27170
+ requireTrusted(event);
27171
+ return runtime2.captureSession(note);
27172
+ });
26926
27173
  electron.ipcMain.handle(
26927
27174
  Channels.AGENT_SESSION_RESTORE,
26928
27175
  (event, snapshot2) => {
@@ -26931,7 +27178,8 @@ function registerIpcHandlers(windowState, runtime2) {
26931
27178
  }
26932
27179
  );
26933
27180
  registerBookmarkHandlers();
26934
- electron.ipcMain.handle(Channels.HIGHLIGHT_CAPTURE, async () => {
27181
+ electron.ipcMain.handle(Channels.HIGHLIGHT_CAPTURE, async (event) => {
27182
+ requireTrusted(event);
26935
27183
  try {
26936
27184
  const activeTab = tabManager.getActiveTab();
26937
27185
  if (!activeTab) {
@@ -26975,10 +27223,12 @@ function registerIpcHandlers(windowState, runtime2) {
26975
27223
  logger$4.warn("Failed to persist auto-highlight selection:", err);
26976
27224
  }
26977
27225
  });
26978
- electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_COUNT, () => {
27226
+ electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_COUNT, (event) => {
27227
+ requireTrusted(event);
26979
27228
  return getActiveHighlightCountSafe();
26980
27229
  });
26981
- electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_SCROLL, (_, index) => {
27230
+ electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_SCROLL, (event, index) => {
27231
+ requireTrusted(event);
26982
27232
  const info = getActiveTabInfo(tabManager);
26983
27233
  if (!info) return false;
26984
27234
  try {
@@ -26988,7 +27238,8 @@ function registerIpcHandlers(windowState, runtime2) {
26988
27238
  return false;
26989
27239
  }
26990
27240
  });
26991
- electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_REMOVE, async (_, index) => {
27241
+ electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_REMOVE, async (event, index) => {
27242
+ requireTrusted(event);
26992
27243
  const info = getActiveTabInfo(tabManager);
26993
27244
  if (!info) return false;
26994
27245
  try {
@@ -27002,7 +27253,8 @@ function registerIpcHandlers(windowState, runtime2) {
27002
27253
  return false;
27003
27254
  }
27004
27255
  });
27005
- electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_CLEAR, async () => {
27256
+ electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_CLEAR, async (event) => {
27257
+ requireTrusted(event);
27006
27258
  const info = getActiveTabInfo(tabManager);
27007
27259
  if (!info) return false;
27008
27260
  try {
@@ -27017,22 +27269,27 @@ function registerIpcHandlers(windowState, runtime2) {
27017
27269
  }
27018
27270
  });
27019
27271
  const findBridge = createFindInPageBridge(tabManager, chromeView);
27020
- electron.ipcMain.handle(Channels.FIND_IN_PAGE_START, (_, text, options) => {
27272
+ electron.ipcMain.handle(Channels.FIND_IN_PAGE_START, (event, text, options) => {
27273
+ requireTrusted(event);
27021
27274
  return findBridge.start(text, options);
27022
27275
  });
27023
- electron.ipcMain.handle(Channels.FIND_IN_PAGE_NEXT, (_, forward) => {
27276
+ electron.ipcMain.handle(Channels.FIND_IN_PAGE_NEXT, (event, forward) => {
27277
+ requireTrusted(event);
27024
27278
  return findBridge.next(forward);
27025
27279
  });
27026
- electron.ipcMain.handle(Channels.FIND_IN_PAGE_STOP, (_, action) => {
27280
+ electron.ipcMain.handle(Channels.FIND_IN_PAGE_STOP, (event, action) => {
27281
+ requireTrusted(event);
27027
27282
  findBridge.stop(action);
27028
27283
  });
27029
27284
  registerHistoryHandlers();
27030
- electron.ipcMain.handle(Channels.DEVTOOLS_PANEL_TOGGLE, () => {
27285
+ electron.ipcMain.handle(Channels.DEVTOOLS_PANEL_TOGGLE, (event) => {
27286
+ requireTrusted(event);
27031
27287
  windowState.uiState.devtoolsPanelOpen = !windowState.uiState.devtoolsPanelOpen;
27032
27288
  layoutViews(windowState);
27033
27289
  return { open: windowState.uiState.devtoolsPanelOpen };
27034
27290
  });
27035
- electron.ipcMain.handle(Channels.DEVTOOLS_PANEL_RESIZE, (_, height) => {
27291
+ electron.ipcMain.handle(Channels.DEVTOOLS_PANEL_RESIZE, (event, height) => {
27292
+ requireTrusted(event);
27036
27293
  const clamped = Math.max(MIN_DEVTOOLS_PANEL, Math.min(MAX_DEVTOOLS_PANEL, Math.round(height)));
27037
27294
  windowState.uiState.devtoolsPanelHeight = clamped;
27038
27295
  layoutViews(windowState);
@@ -27045,7 +27302,8 @@ function registerIpcHandlers(windowState, runtime2) {
27045
27302
  registerHumanVaultHandlers();
27046
27303
  registerWindowControlHandlers(mainWindow);
27047
27304
  registerCodexHandlers();
27048
- electron.ipcMain.handle(Channels.AUTOMATION_GET_INSTALLED, () => {
27305
+ electron.ipcMain.handle(Channels.AUTOMATION_GET_INSTALLED, (event) => {
27306
+ requireTrusted(event);
27049
27307
  return getInstalledKits();
27050
27308
  });
27051
27309
  electron.ipcMain.handle(Channels.AUTOMATION_INSTALL_FROM_FILE, async (event) => {
@@ -27078,7 +27336,10 @@ function registerIpcHandlers(windowState, runtime2) {
27078
27336
  });
27079
27337
  setDownloadBroadcaster(sendToRendererViews);
27080
27338
  setPermissionBroadcaster(sendToRendererViews);
27081
- electron.ipcMain.handle(Channels.DOWNLOADS_GET, () => listDownloads());
27339
+ electron.ipcMain.handle(Channels.DOWNLOADS_GET, (event) => {
27340
+ requireTrusted(event);
27341
+ return listDownloads();
27342
+ });
27082
27343
  electron.ipcMain.handle(Channels.DOWNLOADS_CLEAR, (event) => {
27083
27344
  requireTrusted(event);
27084
27345
  clearDownloads();
@@ -27092,7 +27353,10 @@ function registerIpcHandlers(windowState, runtime2) {
27092
27353
  requireTrusted(event);
27093
27354
  return showDownloadInFolder(id);
27094
27355
  });
27095
- electron.ipcMain.handle(Channels.PERMISSIONS_GET, () => listPermissions());
27356
+ electron.ipcMain.handle(Channels.PERMISSIONS_GET, (event) => {
27357
+ requireTrusted(event);
27358
+ return listPermissions();
27359
+ });
27096
27360
  electron.ipcMain.handle(Channels.PERMISSIONS_CLEAR, (event) => {
27097
27361
  requireTrusted(event);
27098
27362
  clearPermissions();
@@ -27103,12 +27367,16 @@ function registerIpcHandlers(windowState, runtime2) {
27103
27367
  clearPermissionsForOrigin(origin);
27104
27368
  return true;
27105
27369
  });
27106
- electron.ipcMain.handle(Channels.UPDATES_CHECK, () => checkForUpdates());
27370
+ electron.ipcMain.handle(Channels.UPDATES_CHECK, (event) => {
27371
+ requireTrusted(event);
27372
+ return checkForUpdates();
27373
+ });
27107
27374
  electron.ipcMain.handle(Channels.UPDATES_OPEN_DOWNLOAD, (event) => {
27108
27375
  requireTrusted(event);
27109
27376
  return openUpdateDownload();
27110
27377
  });
27111
- electron.ipcMain.handle(Channels.TAB_TOGGLE_PIP, async () => {
27378
+ electron.ipcMain.handle(Channels.TAB_TOGGLE_PIP, async (event) => {
27379
+ requireTrusted(event);
27112
27380
  return togglePictureInPicture(tabManager);
27113
27381
  });
27114
27382
  }