browsentic 0.7.4 → 0.7.5

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.
@@ -3478,10 +3478,10 @@ var require_websocket_server = __commonJS({
3478
3478
  process.nextTick(emitClose, this);
3479
3479
  }
3480
3480
  } else {
3481
- const server = this._server;
3481
+ const server2 = this._server;
3482
3482
  this._removeListeners();
3483
3483
  this._removeListeners = this._server = null;
3484
- server.close(() => {
3484
+ server2.close(() => {
3485
3485
  emitClose(this);
3486
3486
  });
3487
3487
  }
@@ -3666,17 +3666,17 @@ var require_websocket_server = __commonJS({
3666
3666
  }
3667
3667
  };
3668
3668
  module.exports = WebSocketServer2;
3669
- function addListeners(server, map2) {
3670
- for (const event of Object.keys(map2)) server.on(event, map2[event]);
3669
+ function addListeners(server2, map2) {
3670
+ for (const event of Object.keys(map2)) server2.on(event, map2[event]);
3671
3671
  return function removeListeners() {
3672
3672
  for (const event of Object.keys(map2)) {
3673
- server.removeListener(event, map2[event]);
3673
+ server2.removeListener(event, map2[event]);
3674
3674
  }
3675
3675
  };
3676
3676
  }
3677
- function emitClose(server) {
3678
- server._state = CLOSED;
3679
- server.emit("close");
3677
+ function emitClose(server2) {
3678
+ server2._state = CLOSED;
3679
+ server2.emit("close");
3680
3680
  }
3681
3681
  function socketOnError() {
3682
3682
  this.destroy();
@@ -3695,11 +3695,11 @@ var require_websocket_server = __commonJS({
3695
3695
  ` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
3696
3696
  );
3697
3697
  }
3698
- function abortHandshakeOrEmitwsClientError(server, req, socket, code, message, headers) {
3699
- if (server.listenerCount("wsClientError")) {
3698
+ function abortHandshakeOrEmitwsClientError(server2, req, socket, code, message, headers) {
3699
+ if (server2.listenerCount("wsClientError")) {
3700
3700
  const err = new Error(message);
3701
3701
  Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);
3702
- server.emit("wsClientError", err, socket, req);
3702
+ server2.emit("wsClientError", err, socket, req);
3703
3703
  } else {
3704
3704
  abortHandshake(socket, code, message, headers);
3705
3705
  }
@@ -3708,7 +3708,7 @@ var require_websocket_server = __commonJS({
3708
3708
  });
3709
3709
 
3710
3710
  // daemon.ts
3711
- import { randomBytes as randomBytes5, randomUUID as randomUUID14, timingSafeEqual } from "crypto";
3711
+ import { randomBytes as randomBytes5, randomUUID as randomUUID15, timingSafeEqual } from "crypto";
3712
3712
  import { createServer } from "http";
3713
3713
 
3714
3714
  // node_modules/ws/wrapper.mjs
@@ -4970,8 +4970,8 @@ function prefixIssues(path, issues) {
4970
4970
  function unwrapMessage(message) {
4971
4971
  return typeof message === "string" ? message : message?.message;
4972
4972
  }
4973
- function finalizeIssue(iss, ctx, config4) {
4974
- const message = iss.message ? iss.message : unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config4.customError?.(iss)) ?? unwrapMessage(config4.localeError?.(iss)) ?? "Invalid input";
4973
+ function finalizeIssue(iss, ctx, config5) {
4974
+ const message = iss.message ? iss.message : unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config5.customError?.(iss)) ?? unwrapMessage(config5.localeError?.(iss)) ?? "Invalid input";
4975
4975
  const { inst: _inst, continue: _continue, input: _input, ...rest } = iss;
4976
4976
  rest.path ?? (rest.path = []);
4977
4977
  rest.message = message;
@@ -21713,7 +21713,7 @@ var FOCUS_SHOT_ACTION = `${RESERVED_PREFIX}focusShot`;
21713
21713
  var READ_FILE_ACTION = `${RESERVED_PREFIX}readFile`;
21714
21714
 
21715
21715
  // ../lib/agents/catalog.ts
21716
- var AGENT_KINDS = ["claude", "codex", "antigravity", "vibe", "grok", "cursor", "qwen"];
21716
+ var AGENT_KINDS = ["claude", "codex", "antigravity", "vibe", "grok", "cursor", "qwen", "opencode"];
21717
21717
  var DEFAULT_AGENT = "claude";
21718
21718
  var AGENTS = {
21719
21719
  claude: {
@@ -21785,6 +21785,18 @@ var AGENTS = {
21785
21785
  // subcommand lists them, so these are curated.
21786
21786
  models: ["qwen3-coder-plus", "qwen3.7-plus", "qwen3.6-plus", "qwen3-max-2026-01-23"],
21787
21787
  beta: true
21788
+ },
21789
+ opencode: {
21790
+ kind: "opencode",
21791
+ label: "OpenCode",
21792
+ vendor: "Anomaly",
21793
+ bin: "opencode",
21794
+ install: "npm i -g opencode-ai",
21795
+ docs: "https://opencode.ai/docs/cli/",
21796
+ // Spelled provider/model, and reachable only through a provider OpenCode is signed in to: Zen's
21797
+ // free models refuse a run whose tools are narrowed to the browser.
21798
+ models: ["anthropic/claude-opus-5-5", "anthropic/claude-sonnet-5", "openai/gpt-5.6", "google/gemini-3.1-pro-preview"],
21799
+ beta: true
21788
21800
  }
21789
21801
  };
21790
21802
  var AGENT_LIST = AGENT_KINDS.map((kind) => AGENTS[kind]);
@@ -21847,16 +21859,16 @@ function clearLockfile() {
21847
21859
 
21848
21860
  // agent/config.ts
21849
21861
  var CONCURRENT_RUNS = { fallback: 3, max: 8 };
21850
- function maxConcurrentRuns(config4) {
21851
- return clamp2(config4.maxConcurrentRuns, CONCURRENT_RUNS);
21862
+ function maxConcurrentRuns(config5) {
21863
+ return clamp2(config5.maxConcurrentRuns, CONCURRENT_RUNS);
21852
21864
  }
21853
21865
  var SITE_MAP_LIMITS = {
21854
21866
  pages: { fallback: 15, max: 40 },
21855
21867
  screenshots: { fallback: 10, max: 24 },
21856
21868
  timeoutMs: { fallback: 10 * 6e4, max: 30 * 6e4 }
21857
21869
  };
21858
- function siteMapSettings(config4) {
21859
- const stored = config4.siteMap ?? {};
21870
+ function siteMapSettings(config5) {
21871
+ const stored = config5.siteMap ?? {};
21860
21872
  return {
21861
21873
  research: stored.research !== false,
21862
21874
  allowClicks: stored.allowClicks === true,
@@ -21903,8 +21915,8 @@ function settingsFor(stored, kind) {
21903
21915
  };
21904
21916
  }
21905
21917
  var text = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
21906
- function activeAgent(config4) {
21907
- return { kind: config4.agent, ...config4.agents[config4.agent] };
21918
+ function activeAgent(config5) {
21919
+ return { kind: config5.agent, ...config5.agents[config5.agent] };
21908
21920
  }
21909
21921
  function writeActiveAgent(kind) {
21910
21922
  const stored = readStored();
@@ -21946,9 +21958,9 @@ function writeGuardrailSetting(setting, value) {
21946
21958
  else delete next.guardrails;
21947
21959
  write(next);
21948
21960
  }
21949
- function write(config4) {
21961
+ function write(config5) {
21950
21962
  mkdirSync2(stateDir, { recursive: true, mode: 448 });
21951
- writeFileSync2(configPath, `${JSON.stringify(config4, null, 2)}
21963
+ writeFileSync2(configPath, `${JSON.stringify(config5, null, 2)}
21952
21964
  `, { mode: 384 });
21953
21965
  }
21954
21966
 
@@ -23062,8 +23074,8 @@ var DEFAULT_FENCE = {
23062
23074
  // image-specific renderer instead.
23063
23075
  except: ["page.closeTab", "page.stopMonitor", "page.screenshot"]
23064
23076
  };
23065
- function policyFrom(config4 = {}, requireApproval = [SUBMIT_ACTION]) {
23066
- const overrides = config4.rules ?? {};
23077
+ function policyFrom(config5 = {}, requireApproval = [SUBMIT_ACTION]) {
23078
+ const overrides = config5.rules ?? {};
23067
23079
  const rules = DEFAULT_RULES.map((rule) => {
23068
23080
  const legacy = rule.id === "form-submission" && !requireApproval.includes(SUBMIT_ACTION) ? "allow" : rule.effect;
23069
23081
  return { ...rule, effect: overrides[rule.id] ?? legacy };
@@ -23071,9 +23083,9 @@ function policyFrom(config4 = {}, requireApproval = [SUBMIT_ACTION]) {
23071
23083
  return {
23072
23084
  rules,
23073
23085
  requireApproval,
23074
- unattended: config4.unattended === "allow" ? "allow" : "deny",
23075
- urlPayloadBytes: typeof config4.urlPayloadBytes === "number" && config4.urlPayloadBytes >= 0 ? config4.urlPayloadBytes : DEFAULT_URL_PAYLOAD_BYTES,
23076
- fence: config4.fence === false ? { ...DEFAULT_FENCE, enabled: false } : DEFAULT_FENCE
23086
+ unattended: config5.unattended === "allow" ? "allow" : "deny",
23087
+ urlPayloadBytes: typeof config5.urlPayloadBytes === "number" && config5.urlPayloadBytes >= 0 ? config5.urlPayloadBytes : DEFAULT_URL_PAYLOAD_BYTES,
23088
+ fence: config5.fence === false ? { ...DEFAULT_FENCE, enabled: false } : DEFAULT_FENCE
23077
23089
  };
23078
23090
  }
23079
23091
  var POLICY = policyFrom();
@@ -23141,9 +23153,9 @@ function sealingStream() {
23141
23153
  // guardrails/settings.ts
23142
23154
  var LOCKED = /* @__PURE__ */ new Set(["reserved-action", "non-http-navigation", "unreadable-navigation", "secret-in-url"]);
23143
23155
  var RULE_IDS = new Set(DEFAULT_RULES.map((rule) => rule.id));
23144
- function guardrailSettings(config4, requireApproval, configPath2) {
23145
- const overrides = config4.rules ?? {};
23146
- const baseline = policyFrom({ ...config4, rules: {} }, requireApproval);
23156
+ function guardrailSettings(config5, requireApproval, configPath2) {
23157
+ const overrides = config5.rules ?? {};
23158
+ const baseline = policyFrom({ ...config5, rules: {} }, requireApproval);
23147
23159
  const rules = baseline.rules.map((rule) => {
23148
23160
  const override = overrides[rule.id];
23149
23161
  return {
@@ -23157,12 +23169,12 @@ function guardrailSettings(config4, requireApproval, configPath2) {
23157
23169
  });
23158
23170
  return {
23159
23171
  rules,
23160
- fence: { enabled: config4.fence !== false, overridden: config4.fence !== void 0 },
23172
+ fence: { enabled: config5.fence !== false, overridden: config5.fence !== void 0 },
23161
23173
  unattended: {
23162
- effect: config4.unattended === "allow" ? "allow" : "deny",
23163
- overridden: config4.unattended !== void 0
23174
+ effect: config5.unattended === "allow" ? "allow" : "deny",
23175
+ overridden: config5.unattended !== void 0
23164
23176
  },
23165
- hosts: config4.hosts ?? [],
23177
+ hosts: config5.hosts ?? [],
23166
23178
  configPath: configPath2
23167
23179
  };
23168
23180
  }
@@ -23194,6 +23206,8 @@ var FORBIDDEN = [
23194
23206
  var NEVER2 = ["Bash", "Edit", "Write", "NotebookEdit", "Glob", "Grep", "Task"];
23195
23207
  var NEVER_QWEN = ["Bash", "exec", "Edit", "agent", "skill", "monitor"];
23196
23208
  var GROK_SEALED = { GROK_MEMORY: "0", GROK_CLAUDE_MCPS_ENABLED: "false", GROK_CURSOR_MCPS_ENABLED: "false" };
23209
+ var OPENCODE_SEALED = { OPENCODE_DISABLE_PROJECT_CONFIG: "1", OPENCODE_DISABLE_CLAUDE_CODE: "1", OPENCODE_DISABLE_SHARE: "1" };
23210
+ var OPENCODE_CONFIG = ['"*":"deny"', '"share":"disabled"'];
23197
23211
  var CONTAINMENT = {
23198
23212
  claude: {
23199
23213
  localTools: "allowlist",
@@ -23363,6 +23377,37 @@ var CONTAINMENT = {
23363
23377
  denies: { flag: "--exclude-tools", tools: NEVER_QWEN },
23364
23378
  files: []
23365
23379
  }
23380
+ },
23381
+ opencode: {
23382
+ localTools: "allowlist",
23383
+ // A key in ANTHROPIC_* or OPENAI_* would be one of a dozen providers OpenCode reads; `opencode auth login` keeps them in a file.
23384
+ keepsEnv: ["OPENCODE_"],
23385
+ // `--auto` approves whatever is not denied, `--attach` runs the turn in a server started with someone
23386
+ // else's config, `--dir` moves it out of the workspace, and `--share` publishes it.
23387
+ forbidden: [/^--auto$/i, /^--attach/i, /^--dir/i, /^--share$/i],
23388
+ note: 'a per-run permission ruleset opening on "*": "deny", which OpenCode applies after the user\u2019s own rules, so the model is offered no tool that was not named \u2014 built-in, custom, or another MCP server\u2019s; external plugins and project config stay off, but MCP servers the user gave OpenCode itself still start, with their tools hidden',
23389
+ run: {
23390
+ // Plugins run inside OpenCode and can add tools or answer its permission prompts.
23391
+ required: ["--pure"],
23392
+ pairs: [
23393
+ ["--agent", "browsentic-contained"],
23394
+ ["--format", "json"]
23395
+ ],
23396
+ env: OPENCODE_SEALED,
23397
+ envContains: { OPENCODE_CONFIG_CONTENT: OPENCODE_CONFIG },
23398
+ files: ["instructions.md"]
23399
+ },
23400
+ task: {
23401
+ required: ["--pure"],
23402
+ pairs: [
23403
+ ["--agent", "browsentic-contained"],
23404
+ ["--format", "json"]
23405
+ ],
23406
+ env: OPENCODE_SEALED,
23407
+ // An empty map adds no server of ours, so a one-shot has no way to the browser.
23408
+ envContains: { OPENCODE_CONFIG_CONTENT: [...OPENCODE_CONFIG, '"mcp":{}'] },
23409
+ files: []
23410
+ }
23366
23411
  }
23367
23412
  };
23368
23413
  function vetPlan(kind, mode, plan, home) {
@@ -23391,6 +23436,10 @@ function vetPlan(kind, mode, plan, home) {
23391
23436
  for (const [name, value] of Object.entries(rules.env ?? {})) {
23392
23437
  if (plan.env?.[name] !== value) problems.push(`${label2} is spawned without ${name}=${value}.`);
23393
23438
  }
23439
+ for (const [name, needles] of Object.entries(rules.envContains ?? {})) {
23440
+ const missing = needles.filter((needle) => !plan.env?.[name]?.includes(needle));
23441
+ if (missing.length) problems.push(`${label2} is spawned with a ${name} missing ${missing.join(", ")}.`);
23442
+ }
23394
23443
  for (const path of rules.files) {
23395
23444
  if (!plan.files?.some((file2) => file2.path === path)) {
23396
23445
  problems.push(`${label2} is spawned without ${path} in its workspace.`);
@@ -23460,6 +23509,7 @@ var SECRET_PREFIX = [
23460
23509
  "QWEN_",
23461
23510
  "DASHSCOPE_",
23462
23511
  "BAILIAN_",
23512
+ "OPENCODE_",
23463
23513
  "VERCEL_",
23464
23514
  "NETLIFY_",
23465
23515
  "CLOUDFLARE_",
@@ -23870,7 +23920,7 @@ function revokeSessions(predicate) {
23870
23920
  }
23871
23921
 
23872
23922
  // agent/service.ts
23873
- import { randomUUID as randomUUID9 } from "crypto";
23923
+ import { randomUUID as randomUUID10 } from "crypto";
23874
23924
 
23875
23925
  // ../lib/actions/tool-names.ts
23876
23926
  function toolNameFor(actionName) {
@@ -24027,12 +24077,12 @@ function isMappableHost(host) {
24027
24077
 
24028
24078
  // agent/agent-skills.ts
24029
24079
  import { createHash } from "crypto";
24030
- import { readFileSync as readFileSync9, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
24031
- import { join as join18, sep as sep2 } from "path";
24080
+ import { readFileSync as readFileSync10, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
24081
+ import { join as join19, sep as sep2 } from "path";
24032
24082
 
24033
24083
  // agent/runners/index.ts
24034
24084
  import { spawn as spawn2 } from "child_process";
24035
- import { dirname as dirname4, join as join17 } from "path";
24085
+ import { dirname as dirname5, join as join18 } from "path";
24036
24086
  import { fileURLToPath as fileURLToPath3 } from "url";
24037
24087
 
24038
24088
  // agent/runners/antigravity.ts
@@ -24426,8 +24476,8 @@ var antigravityRunner = {
24426
24476
  }
24427
24477
  }
24428
24478
  };
24429
- function mcpConfig(server) {
24430
- const servers = server ? { [MCP_SERVER_NAME]: server } : {};
24479
+ function mcpConfig(server2) {
24480
+ const servers = server2 ? { [MCP_SERVER_NAME]: server2 } : {};
24431
24481
  return `${JSON.stringify({ mcpServers: servers }, null, 2)}
24432
24482
  `;
24433
24483
  }
@@ -24484,7 +24534,7 @@ var codexRunner = {
24484
24534
  skillDirs: () => [join12(homedir7(), ".codex", "skills"), join12(homedir7(), ".codex", "prompts")],
24485
24535
  stream(context) {
24486
24536
  const { settings, mcp, research } = context;
24487
- const server = `mcp_servers.${MCP_SERVER_NAME}`;
24537
+ const server2 = `mcp_servers.${MCP_SERVER_NAME}`;
24488
24538
  const effort = effortOf(settings, this.efforts);
24489
24539
  return {
24490
24540
  cwd: this.workspace("run"),
@@ -24496,16 +24546,16 @@ var codexRunner = {
24496
24546
  ...SANDBOX,
24497
24547
  ...QUIETED,
24498
24548
  "-c",
24499
- `${server}.command=${tomlString(mcp.command)}`,
24549
+ `${server2}.command=${tomlString(mcp.command)}`,
24500
24550
  "-c",
24501
- `${server}.args=${tomlArray(mcp.args)}`,
24551
+ `${server2}.args=${tomlArray(mcp.args)}`,
24502
24552
  "-c",
24503
- `${server}.env=${tomlTable(mcp.env)}`,
24553
+ `${server2}.env=${tomlTable(mcp.env)}`,
24504
24554
  "-c",
24505
- `${server}.required=true`,
24555
+ `${server2}.required=true`,
24506
24556
  // Headless Codex refuses an MCP call it would have asked about; the daemon gates these tools itself.
24507
24557
  "-c",
24508
- `${server}.default_tools_approval_mode="approve"`,
24558
+ `${server2}.default_tools_approval_mode="approve"`,
24509
24559
  "-c",
24510
24560
  `developer_instructions=${tomlString(`${context.systemPrompt.trim()}
24511
24561
 
@@ -24863,11 +24913,11 @@ function toolOf(event) {
24863
24913
  const [key, value] = Object.entries(event.tool_call ?? {}).find(([name]) => name.endsWith("ToolCall")) ?? [];
24864
24914
  if (!key) return void 0;
24865
24915
  const args = value?.args;
24866
- const server = args?.["server"];
24867
- if (typeof server === "string") {
24868
- if (server === MCP_SERVER_NAME) return void 0;
24916
+ const server2 = args?.["server"];
24917
+ if (typeof server2 === "string") {
24918
+ if (server2 === MCP_SERVER_NAME) return void 0;
24869
24919
  const tool = args?.["name"];
24870
- return `${server}:${typeof tool === "string" ? tool : key}`;
24920
+ return `${server2}:${typeof tool === "string" ? tool : key}`;
24871
24921
  }
24872
24922
  return key.replace(/ToolCall$/, "");
24873
24923
  }
@@ -25072,13 +25122,13 @@ function explain2(message) {
25072
25122
  }
25073
25123
  var oneLine = (message) => message.replace(/\s+/g, " ").slice(0, 240);
25074
25124
  var sentence = (message) => /[.!?]$/.test(message.trim()) ? message.trim() : `${message.trim()}.`;
25075
- function config2(server) {
25125
+ function config2(server2) {
25076
25126
  const quote = (value) => JSON.stringify(value);
25077
- const env = Object.entries(server.env).map(([name, value]) => `${name} = ${quote(value)}`);
25127
+ const env = Object.entries(server2.env).map(([name, value]) => `${name} = ${quote(value)}`);
25078
25128
  return [
25079
25129
  `[mcp_servers.${MCP_SERVER_NAME}]`,
25080
- `command = ${quote(server.command)}`,
25081
- `args = [${server.args.map(quote).join(", ")}]`,
25130
+ `command = ${quote(server2.command)}`,
25131
+ `args = [${server2.args.map(quote).join(", ")}]`,
25082
25132
  `env = { ${env.join(", ")} }`,
25083
25133
  ""
25084
25134
  ].join("\n");
@@ -25091,11 +25141,215 @@ function lastLine(stdout) {
25091
25141
  return null;
25092
25142
  }
25093
25143
 
25094
- // agent/runners/qwen.ts
25144
+ // agent/runners/opencode.ts
25095
25145
  import { randomUUID as randomUUID6 } from "crypto";
25096
25146
  import { readFileSync as readFileSync8 } from "fs";
25097
25147
  import { homedir as homedir10 } from "os";
25098
- import { join as join15 } from "path";
25148
+ import { dirname as dirname4, join as join15, relative } from "path";
25149
+ var AGENT = "browsentic-contained";
25150
+ var INSTRUCTIONS2 = "instructions.md";
25151
+ var WEB_TOOLS3 = ["webfetch", "websearch"];
25152
+ var RESULT_BYTES = 1e5;
25153
+ var TOOL_TIMEOUT_MS = 30 * 6e4;
25154
+ var TRUNCATION = `A tool result over ${RESULT_BYTES / 1e3} KB comes back cut, and the saved copy OpenCode points to cannot be opened in this run, so ask for less at a time \u2014 \`page_getPageInfo\` with a small \`maxPerKind\`, \`page_extractText\` with the cursor it hands back.`;
25155
+ var SEALED2 = { OPENCODE_DISABLE_PROJECT_CONFIG: "1", OPENCODE_DISABLE_CLAUDE_CODE: "1", OPENCODE_DISABLE_SHARE: "1" };
25156
+ var sessionsDb = () => join15(stateDir, "agents", "opencode", "sessions.db");
25157
+ var configHome = () => join15(process.env.XDG_CONFIG_HOME || join15(homedir10(), ".config"), "opencode");
25158
+ var INSTALL = "npm i -g opencode-ai";
25159
+ var opencodeRunner = {
25160
+ kind: "opencode",
25161
+ versionArgs: ["--version"],
25162
+ // Passed as --variant, whose names each model defines; a model ignores one it lacks.
25163
+ efforts: ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
25164
+ endsOnExit: true,
25165
+ workspace: (mode) => join15(stateDir, "agents", "opencode", mode),
25166
+ skillDirs: () => [
25167
+ join15(configHome(), "skills"),
25168
+ join15(configHome(), "skill"),
25169
+ join15(homedir10(), ".opencode", "skills"),
25170
+ join15(homedir10(), ".agents", "skills"),
25171
+ join15(homedir10(), ".claude", "skills")
25172
+ ],
25173
+ stream(context) {
25174
+ const { settings, research } = context;
25175
+ const base = this.workspace("run");
25176
+ sweepRunDirs(base);
25177
+ const cwd = conversationDir(base, context.conversation ?? context.runId);
25178
+ const allowed2 = [...context.mcpTools.map((tool) => `${MCP_SERVER_NAME}_${tool}`), ...research ? WEB_TOOLS3 : []];
25179
+ const effort = effortOf(settings, this.efforts);
25180
+ return {
25181
+ cwd,
25182
+ env: {
25183
+ ...SEALED2,
25184
+ OPENCODE_DB: sessionsDb(),
25185
+ OPENCODE_CONFIG_CONTENT: config3({
25186
+ mcp: { [MCP_SERVER_NAME]: server(context.mcp) },
25187
+ instructions: [join15(cwd, INSTRUCTIONS2)],
25188
+ permission: { "*": "deny", ...Object.fromEntries(allowed2.map((tool) => [tool, "allow"])) }
25189
+ }),
25190
+ BROWSENTIC_AGENT_RUN: context.runId
25191
+ },
25192
+ files: [{ path: INSTRUCTIONS2, content: `${context.systemPrompt.trim()}
25193
+
25194
+ ${TRUNCATION}
25195
+ ` }],
25196
+ args: [
25197
+ ...invocation(),
25198
+ ...context.sessionId ? ["--session", context.sessionId] : [],
25199
+ ...settings.model ? ["--model", settings.model] : [],
25200
+ ...effort ? ["--variant", effort] : [],
25201
+ "--",
25202
+ context.instruction
25203
+ ]
25204
+ };
25205
+ },
25206
+ reader() {
25207
+ let spoke = false;
25208
+ let generated = 0;
25209
+ const report = (tokens, sink) => {
25210
+ const made = (tokens.output ?? 0) + (tokens.reasoning ?? 0);
25211
+ const prompt = (tokens.input ?? 0) + (tokens.cache?.read ?? 0) + (tokens.cache?.write ?? 0);
25212
+ generated += made;
25213
+ sink.usage({ contextTokens: prompt + made, outputTokens: generated });
25214
+ };
25215
+ return (line2, sink) => {
25216
+ const event = parseJsonLine(line2);
25217
+ if (!event) return;
25218
+ if (event.sessionID) sink.session(event.sessionID);
25219
+ const part = event.part;
25220
+ switch (event.type) {
25221
+ case "text":
25222
+ if (!part?.text) return;
25223
+ sink.text(spoke ? `
25224
+
25225
+ ${part.text}` : part.text);
25226
+ spoke = true;
25227
+ return;
25228
+ case "tool_use": {
25229
+ const name = part?.tool;
25230
+ if (!name || ownTool3(name)) return;
25231
+ if (WEB_TOOLS3.includes(name)) return sink.tool(part.callID ?? randomUUID6(), name);
25232
+ if (part.state?.status !== "completed") return;
25233
+ return sink.fail(
25234
+ "AGENT_UNSAFE",
25235
+ `OpenCode ran its own ${name} tool in this run, which Browsentic denied, so the run was stopped. Update OpenCode and Browsentic; if it persists, please report it.`
25236
+ );
25237
+ }
25238
+ case "step_finish":
25239
+ if (part?.tokens) report(part.tokens, sink);
25240
+ return;
25241
+ case "error":
25242
+ return sink.fail("AGENT_FAILED", explain3(event.error));
25243
+ }
25244
+ };
25245
+ },
25246
+ json(context) {
25247
+ const { settings, reads } = context;
25248
+ const cwd = this.workspace("task");
25249
+ const permission = { "*": "deny", ...reads ? { read: scratchReads(cwd) } : {} };
25250
+ return {
25251
+ cwd,
25252
+ env: {
25253
+ ...SEALED2,
25254
+ OPENCODE_DB: sessionsDb(),
25255
+ // Merged over the user's servers rather than replacing them: it adds none, and the ruleset hides theirs.
25256
+ OPENCODE_CONFIG_CONTENT: config3({ mcp: {}, permission })
25257
+ },
25258
+ args: [...invocation(), ...settings.model ? ["--model", settings.model] : [], "--", context.prompt]
25259
+ };
25260
+ },
25261
+ answer(stdout) {
25262
+ let said = [];
25263
+ for (const line2 of stdout.split("\n")) {
25264
+ const event = parseJsonLine(line2);
25265
+ if (event?.type === "error") return { error: explain3(event.error) };
25266
+ if (event?.type === "step_start") said = [];
25267
+ if (event?.type === "text" && event.part?.text) said.push(event.part.text);
25268
+ }
25269
+ return { text: said.length ? said.join("\n\n") : void 0 };
25270
+ },
25271
+ hint(stderrTail) {
25272
+ if (/Unknown arguments?|Not enough arguments|Invalid values/i.test(stderrTail)) {
25273
+ return `Your OpenCode does not understand the flags Browsentic uses. Update it (${INSTALL}), then try again. (${stderrTail.trim()})`;
25274
+ }
25275
+ return null;
25276
+ },
25277
+ async check() {
25278
+ if (process.env.OPENCODE_API_KEY || process.env.OPENCODE_AUTH_CONTENT || signedIn2() || declaresProvider()) return null;
25279
+ return {
25280
+ code: "AGENT_NEEDS_PERMISSION",
25281
+ message: "OpenCode is signed in to no model provider, and its free OpenCode Zen models refuse a run whose tools Browsentic has narrowed to the browser.",
25282
+ fix: "opencode auth login"
25283
+ };
25284
+ }
25285
+ };
25286
+ var invocation = () => ["run", "--format", "json", "--pure", "--agent", AGENT];
25287
+ var ownTool3 = (name) => name.startsWith(`${MCP_SERVER_NAME}_`);
25288
+ var server = (mcp) => ({
25289
+ type: "local",
25290
+ command: [mcp.command, ...mcp.args],
25291
+ environment: mcp.env,
25292
+ enabled: true,
25293
+ timeout: TOOL_TIMEOUT_MS
25294
+ });
25295
+ function config3({ mcp, instructions, permission }) {
25296
+ return JSON.stringify({
25297
+ share: "disabled",
25298
+ autoupdate: false,
25299
+ snapshot: false,
25300
+ tool_output: { max_bytes: RESULT_BYTES, max_lines: RESULT_BYTES },
25301
+ mcp,
25302
+ ...instructions ? { instructions } : {},
25303
+ agent: { title: { disable: true }, [AGENT]: { mode: "primary", permission } }
25304
+ });
25305
+ }
25306
+ function scratchReads(workspace) {
25307
+ const scratch = join15(workspace, "tmp");
25308
+ const rules = { "*": "deny" };
25309
+ for (let root = dirname4(workspace); ; root = dirname4(root)) {
25310
+ rules[`${relative(root, scratch)}/*`] = "allow";
25311
+ if (root === dirname4(root)) return rules;
25312
+ }
25313
+ }
25314
+ function explain3(failure2) {
25315
+ const message = oneLine2(failure2?.data?.message ?? failure2?.name ?? "OpenCode reported an error");
25316
+ const status = failure2?.data?.statusCode;
25317
+ if (failure2?.data?.responseBody?.includes("FreeTierError")) {
25318
+ return `OpenCode Zen's free models refuse a run whose tools Browsentic has narrowed to the browser. Run "opencode auth login" to sign in to a provider, then pick one of its models in the Browsentic popup. (${message})`;
25319
+ }
25320
+ if (failure2?.name === "ProviderAuthError" || status === 401 || status === 403) {
25321
+ return `${message} If that is a login problem, run "opencode auth login" \u2014 a key kept only in an environment variable is not passed to a Browsentic run.`;
25322
+ }
25323
+ if (status === 429) return `The provider behind OpenCode is rate-limiting this account. Wait and try again. (${message})`;
25324
+ if (failure2?.name === "UnknownError") {
25325
+ return `OpenCode could not start this turn, most often because it does not know the model. Pick one in the Browsentic popup as "opencode models" lists it \u2014 provider/model \u2014 then try again. (${message})`;
25326
+ }
25327
+ return message;
25328
+ }
25329
+ var oneLine2 = (message) => message.replace(/\s+/g, " ").trim().slice(0, 240);
25330
+ var dataHome = () => join15(process.env.XDG_DATA_HOME || join15(homedir10(), ".local", "share"), "opencode");
25331
+ function signedIn2() {
25332
+ try {
25333
+ return Object.keys(JSON.parse(readFileSync8(join15(dataHome(), "auth.json"), "utf8"))).length > 0;
25334
+ } catch (error51) {
25335
+ return error51.code !== "ENOENT";
25336
+ }
25337
+ }
25338
+ function declaresProvider() {
25339
+ return ["opencode.json", "opencode.jsonc", "config.json"].some((name) => {
25340
+ try {
25341
+ return /"provider"\s*:/.test(readFileSync8(join15(configHome(), name), "utf8"));
25342
+ } catch {
25343
+ return false;
25344
+ }
25345
+ });
25346
+ }
25347
+
25348
+ // agent/runners/qwen.ts
25349
+ import { randomUUID as randomUUID7 } from "crypto";
25350
+ import { readFileSync as readFileSync9 } from "fs";
25351
+ import { homedir as homedir11 } from "os";
25352
+ import { join as join16 } from "path";
25099
25353
  var MACHINE = ["Bash", "exec", "Edit", "Read", "zoom_image", "monitor", "lsp", "save_memory"];
25100
25354
  var ESCAPES = [
25101
25355
  "skill",
@@ -25116,13 +25370,13 @@ var ESCAPES = [
25116
25370
  "image_gen",
25117
25371
  "read_mcp_resource"
25118
25372
  ];
25119
- var WEB_TOOLS3 = ["web_search", "web_fetch"];
25373
+ var WEB_TOOLS4 = ["web_search", "web_fetch"];
25120
25374
  var READ_TOOL2 = "read_file";
25121
25375
  var OTHER_READS = ["grep_search", "glob", "list_directory"];
25122
25376
  var APPROVAL = "default";
25123
25377
  var DANGEROUS = /^(run_shell_command|exec|edit|write_file|notebook_edit|read_file|grep_search|glob|list_directory|agent|skill|monitor|save_memory|lsp|zoom_image|image_gen|workflow|send_message|create_sub_session|propose_goal|cron_|team_|computer_use__|omni_)/;
25124
- var qwenHome = () => process.env.QWEN_HOME || join15(homedir10(), ".qwen");
25125
- var INSTALL = "npm i -g @qwen-code/qwen-code";
25378
+ var qwenHome = () => process.env.QWEN_HOME || join16(homedir11(), ".qwen");
25379
+ var INSTALL2 = "npm i -g @qwen-code/qwen-code";
25126
25380
  var AUTH_KEYS = ["QWEN_API_KEY", "OPENAI_API_KEY", "DASHSCOPE_API_KEY"];
25127
25381
  var AUTH_PREFIX = /^(QWEN_|DASHSCOPE_|BAILIAN_|OPENAI_).*(API_KEY|TOKEN)$/;
25128
25382
  var qwenRunner = {
@@ -25132,8 +25386,8 @@ var qwenRunner = {
25132
25386
  efforts: [],
25133
25387
  // Sessions are filed under ~/.qwen/projects/<sanitized-cwd>, so a resume only finds the
25134
25388
  // conversation it began in when this stays put.
25135
- workspace: (mode) => join15(stateDir, "agents", "qwen", mode),
25136
- skillDirs: () => [join15(qwenHome(), "skills"), join15(homedir10(), ".agents", "skills")],
25389
+ workspace: (mode) => join16(stateDir, "agents", "qwen", mode),
25390
+ skillDirs: () => [join16(qwenHome(), "skills"), join16(homedir11(), ".agents", "skills")],
25137
25391
  stream(context) {
25138
25392
  const { settings, research } = context;
25139
25393
  return {
@@ -25159,14 +25413,14 @@ var qwenRunner = {
25159
25413
  // to be auto-approved by name. One entry covers every tool the server offers.
25160
25414
  "--allowed-tools",
25161
25415
  `mcp__${MCP_SERVER_NAME}`,
25162
- ...research ? WEB_TOOLS3 : [],
25416
+ ...research ? WEB_TOOLS4 : [],
25163
25417
  "--exclude-tools",
25164
25418
  ...MACHINE,
25165
25419
  ...ESCAPES,
25166
- ...research ? [] : WEB_TOOLS3,
25420
+ ...research ? [] : WEB_TOOLS4,
25167
25421
  "--append-system-prompt",
25168
25422
  context.systemPrompt,
25169
- ...context.sessionId ? ["--resume", context.sessionId] : ["--session-id", randomUUID6()],
25423
+ ...context.sessionId ? ["--resume", context.sessionId] : ["--session-id", randomUUID7()],
25170
25424
  ...settings.model ? ["--model", settings.model] : []
25171
25425
  ]
25172
25426
  };
@@ -25202,7 +25456,7 @@ var qwenRunner = {
25202
25456
  }
25203
25457
  if (event?.type === "content_block_start" && event.content_block?.type === "tool_use") {
25204
25458
  const name = event.content_block.name ?? "tool";
25205
- if (WEB_TOOLS3.includes(name)) sink.tool(event.content_block.id ?? randomUUID6(), name);
25459
+ if (WEB_TOOLS4.includes(name)) sink.tool(event.content_block.id ?? randomUUID7(), name);
25206
25460
  }
25207
25461
  return;
25208
25462
  }
@@ -25216,7 +25470,7 @@ var qwenRunner = {
25216
25470
  if (message.is_error) {
25217
25471
  return sink.fail(
25218
25472
  "AGENT_FAILED",
25219
- explain3(message.error?.message) ?? (message.result?.trim() || "Qwen Code reported an error")
25473
+ explain4(message.error?.message) ?? (message.result?.trim() || "Qwen Code reported an error")
25220
25474
  );
25221
25475
  }
25222
25476
  return sink.done(message.subtype === "success" ? "end_turn" : message.subtype || "end_turn");
@@ -25244,7 +25498,7 @@ var qwenRunner = {
25244
25498
  "--exclude-tools",
25245
25499
  ...denied,
25246
25500
  ...ESCAPES,
25247
- ...WEB_TOOLS3,
25501
+ ...WEB_TOOLS4,
25248
25502
  ...settings.model ? ["--model", settings.model] : []
25249
25503
  ]
25250
25504
  };
@@ -25253,7 +25507,7 @@ var qwenRunner = {
25253
25507
  const answer = lastResult2(stdout);
25254
25508
  if (!answer) return {};
25255
25509
  if (answer.is_error) {
25256
- return { error: explain3(answer.error?.message) ?? (answer.result?.trim() || "Qwen Code reported an error") };
25510
+ return { error: explain4(answer.error?.message) ?? (answer.result?.trim() || "Qwen Code reported an error") };
25257
25511
  }
25258
25512
  return { text: typeof answer.result === "string" ? answer.result : void 0 };
25259
25513
  },
@@ -25266,7 +25520,7 @@ var qwenRunner = {
25266
25520
  return `Qwen Code refused the session id Browsentic minted. This is a bug in Browsentic \u2014 please report it. (${tail2})`;
25267
25521
  }
25268
25522
  if (/unknown argument|unknown option|invalid values|not a valid choice/i.test(tail2)) {
25269
- return `Your Qwen Code does not understand the flags Browsentic uses. Update it (${INSTALL}), then try again. (${tail2})`;
25523
+ return `Your Qwen Code does not understand the flags Browsentic uses. Update it (${INSTALL2}), then try again. (${tail2})`;
25270
25524
  }
25271
25525
  return null;
25272
25526
  },
@@ -25289,7 +25543,7 @@ function escaped(init) {
25289
25543
  if (live.length) {
25290
25544
  return `Qwen Code registered ${live.join(", ")} for this run, which Browsentic denied, so the run was stopped before the model saw them. Update Qwen Code and Browsentic; if it persists, please report it.`;
25291
25545
  }
25292
- const others = (init.mcp_servers ?? []).map((server) => server.name).filter((name) => name && name !== MCP_SERVER_NAME);
25546
+ const others = (init.mcp_servers ?? []).map((server2) => server2.name).filter((name) => name && name !== MCP_SERVER_NAME);
25293
25547
  if (others.length) {
25294
25548
  return `Qwen Code loaded the MCP server${others.length > 1 ? "s" : ""} ${others.join(", ")} beside Browsentic's own, which would reach the browser outside this run's gate, so the run was stopped. Update Qwen Code and Browsentic; if it persists, please report it.`;
25295
25549
  }
@@ -25298,27 +25552,27 @@ function escaped(init) {
25298
25552
  }
25299
25553
  return void 0;
25300
25554
  }
25301
- function explain3(message) {
25555
+ function explain4(message) {
25302
25556
  if (!message) return void 0;
25303
25557
  if (/No auth type is selected|API key not found/i.test(message)) {
25304
- return `Qwen Code has no model provider configured. Run "qwen" and use /auth, or export OPENAI_API_KEY with OPENAI_BASE_URL, then try again. (${oneLine2(message)})`;
25558
+ return `Qwen Code has no model provider configured. Run "qwen" and use /auth, or export OPENAI_API_KEY with OPENAI_BASE_URL, then try again. (${oneLine3(message)})`;
25305
25559
  }
25306
25560
  if (/qwen-oauth|Qwen OAuth/i.test(message)) {
25307
- return `Qwen OAuth's free tier was discontinued, so its requests are rejected. Configure another provider with /auth. (${oneLine2(message)})`;
25561
+ return `Qwen OAuth's free tier was discontinued, so its requests are rejected. Configure another provider with /auth. (${oneLine3(message)})`;
25308
25562
  }
25309
25563
  if (/rate limit|429|quota/i.test(message)) {
25310
- return `The provider behind Qwen Code is rate-limiting this account. Wait and try again. (${oneLine2(message)})`;
25564
+ return `The provider behind Qwen Code is rate-limiting this account. Wait and try again. (${oneLine3(message)})`;
25311
25565
  }
25312
25566
  if (/unknown model|model not found/i.test(message)) {
25313
- return `${oneLine2(message)} Pick another model for Qwen Code in the Browsentic popup, then try again.`;
25567
+ return `${oneLine3(message)} Pick another model for Qwen Code in the Browsentic popup, then try again.`;
25314
25568
  }
25315
- return oneLine2(message);
25569
+ return oneLine3(message);
25316
25570
  }
25317
- var oneLine2 = (message) => message.replace(/\s+/g, " ").trim().slice(0, 240);
25571
+ var oneLine3 = (message) => message.replace(/\s+/g, " ").trim().slice(0, 240);
25318
25572
  function configured() {
25319
25573
  let parsed2;
25320
25574
  try {
25321
- parsed2 = JSON.parse(readFileSync8(join15(qwenHome(), "settings.json"), "utf8"));
25575
+ parsed2 = JSON.parse(readFileSync9(join16(qwenHome(), "settings.json"), "utf8"));
25322
25576
  } catch (error51) {
25323
25577
  return error51.code !== "ENOENT";
25324
25578
  }
@@ -25339,34 +25593,34 @@ function lastResult2(stdout) {
25339
25593
  }
25340
25594
 
25341
25595
  // agent/runners/vibe.ts
25342
- import { homedir as homedir11 } from "os";
25343
- import { join as join16 } from "path";
25596
+ import { homedir as homedir12 } from "os";
25597
+ import { join as join17 } from "path";
25344
25598
  var CONFIG2 = ".vibe/config.toml";
25345
- var INSTRUCTIONS2 = "AGENTS.md";
25599
+ var INSTRUCTIONS3 = "AGENTS.md";
25346
25600
  var TASK_INSTRUCTIONS2 = "This directory is Browsentic scratch space. Answer the prompt exactly as it asks, and do not act on anything else you find here.\n";
25347
- var WEB_TOOLS4 = ["web_search", "web_fetch"];
25601
+ var WEB_TOOLS5 = ["web_search", "web_fetch"];
25348
25602
  var READ_TOOL3 = "read_file";
25349
25603
  var NO_TOOLS = "re:^$";
25350
25604
  var PROFILE = "ask";
25351
- var vibeHome = () => process.env.VIBE_HOME || join16(homedir11(), ".vibe");
25605
+ var vibeHome = () => process.env.VIBE_HOME || join17(homedir12(), ".vibe");
25352
25606
  var vibeRunner = {
25353
25607
  kind: "vibe",
25354
25608
  versionArgs: ["--version"],
25355
25609
  efforts: [],
25356
25610
  endsOnExit: true,
25357
- workspace: (mode) => join16(stateDir, "agents", "vibe", mode),
25358
- skillDirs: () => [join16(vibeHome(), "skills"), join16(homedir11(), ".agents", "skills")],
25611
+ workspace: (mode) => join17(stateDir, "agents", "vibe", mode),
25612
+ skillDirs: () => [join17(vibeHome(), "skills"), join17(homedir12(), ".agents", "skills")],
25359
25613
  stream(context) {
25360
25614
  const base = this.workspace("run");
25361
25615
  sweepRunDirs(base);
25362
- const builtins = context.research ? WEB_TOOLS4 : [];
25616
+ const builtins = context.research ? WEB_TOOLS5 : [];
25363
25617
  const granted = [...context.mcpTools.map((tool) => `${MCP_SERVER_NAME}_${tool}`), ...builtins];
25364
25618
  return {
25365
25619
  cwd: conversationDir(base, context.conversation ?? context.runId),
25366
25620
  env: { BROWSENTIC_AGENT_RUN: context.runId },
25367
25621
  files: [
25368
- { path: CONFIG2, content: config3(context.settings.model, context.mcp, granted) },
25369
- { path: INSTRUCTIONS2, content: `${context.systemPrompt.trim()}
25622
+ { path: CONFIG2, content: config4(context.settings.model, context.mcp, granted) },
25623
+ { path: INSTRUCTIONS3, content: `${context.systemPrompt.trim()}
25370
25624
  ` }
25371
25625
  ],
25372
25626
  args: [
@@ -25400,7 +25654,7 @@ ${text2}` : text2);
25400
25654
  return;
25401
25655
  }
25402
25656
  const tool = entry.type === "effect" ? entry.detail?.toolName : void 0;
25403
- if (tool && entry.id && !ownTool3(tool)) sink.tool(entry.id, tool);
25657
+ if (tool && entry.id && !ownTool4(tool)) sink.tool(entry.id, tool);
25404
25658
  };
25405
25659
  },
25406
25660
  json(context) {
@@ -25408,8 +25662,8 @@ ${text2}` : text2);
25408
25662
  return {
25409
25663
  cwd: this.workspace("task"),
25410
25664
  files: [
25411
- { path: CONFIG2, content: config3(context.settings.model, null, allowed2) },
25412
- { path: INSTRUCTIONS2, content: TASK_INSTRUCTIONS2 }
25665
+ { path: CONFIG2, content: config4(context.settings.model, null, allowed2) },
25666
+ { path: INSTRUCTIONS3, content: TASK_INSTRUCTIONS2 }
25413
25667
  ],
25414
25668
  args: [
25415
25669
  "--prompt",
@@ -25431,7 +25685,7 @@ ${text2}` : text2);
25431
25685
  },
25432
25686
  hint(stderrTail) {
25433
25687
  if (/Missing \w+ environment variable/i.test(stderrTail)) {
25434
- return `Mistral Vibe is installed but has no API key. Run "vibe --setup", or put MISTRAL_API_KEY in ${join16(vibeHome(), ".env")}, then try again. (${stderrTail.trim()})`;
25688
+ return `Mistral Vibe is installed but has no API key. Run "vibe --setup", or put MISTRAL_API_KEY in ${join17(vibeHome(), ".env")}, then try again. (${stderrTail.trim()})`;
25435
25689
  }
25436
25690
  if (/unrecognized arguments|invalid choice/i.test(stderrTail)) {
25437
25691
  return `Your Mistral Vibe does not understand the flags Browsentic uses. Update it, then try again. (${stderrTail.trim()})`;
@@ -25441,20 +25695,20 @@ ${text2}` : text2);
25441
25695
  };
25442
25696
  var enabling = (patterns) => patterns.flatMap((pattern) => ["--enabled-tools", pattern]);
25443
25697
  var textOf2 = (entry) => (entry.content ?? []).filter((block) => block.type === "text" && block.text).map((block) => block.text).join("\n\n");
25444
- var ownTool3 = (name) => name.startsWith(`${MCP_SERVER_NAME}_`);
25445
- function config3(model, server, granted) {
25698
+ var ownTool4 = (name) => name.startsWith(`${MCP_SERVER_NAME}_`);
25699
+ function config4(model, server2, granted) {
25446
25700
  const quote = (value) => JSON.stringify(value);
25447
25701
  const lines = model ? [`active_model = ${quote(model)}`, ""] : [];
25448
- if (server) {
25702
+ if (server2) {
25449
25703
  lines.push(
25450
25704
  "[[mcp_servers]]",
25451
25705
  `name = ${quote(MCP_SERVER_NAME)}`,
25452
25706
  'transport = "stdio"',
25453
- `command = [${quote(server.command)}]`,
25454
- `args = [${server.args.map(quote).join(", ")}]`,
25707
+ `command = [${quote(server2.command)}]`,
25708
+ `args = [${server2.args.map(quote).join(", ")}]`,
25455
25709
  "",
25456
25710
  "[mcp_servers.env]",
25457
- ...Object.entries(server.env).map(([name, value]) => `${name} = ${quote(value)}`),
25711
+ ...Object.entries(server2.env).map(([name, value]) => `${name} = ${quote(value)}`),
25458
25712
  ""
25459
25713
  );
25460
25714
  }
@@ -25470,26 +25724,27 @@ var RUNNERS = {
25470
25724
  vibe: vibeRunner,
25471
25725
  grok: grokRunner,
25472
25726
  cursor: cursorRunner,
25473
- qwen: qwenRunner
25727
+ qwen: qwenRunner,
25728
+ opencode: opencodeRunner
25474
25729
  };
25475
- var cliPath = join17(dirname4(fileURLToPath3(import.meta.url)), "cli.js");
25730
+ var cliPath = join18(dirname5(fileURLToPath3(import.meta.url)), "cli.js");
25476
25731
  function mcpServerFor(runId) {
25477
25732
  return { command: process.execPath, args: [cliPath, "mcp"], env: { BROWSENTIC_AGENT_RUN: runId } };
25478
25733
  }
25479
- function runnerFor(config4) {
25480
- const active = activeAgent(config4);
25734
+ function runnerFor(config5) {
25735
+ const active = activeAgent(config5);
25481
25736
  return { runner: RUNNERS[active.kind], settings: active };
25482
25737
  }
25483
25738
  var PROBE_TIMEOUT_MS = 8e3;
25484
25739
  var PROBE_TTL_MS = 3e4;
25485
25740
  var cached2 = null;
25486
- async function agentState(config4, { refresh = false } = {}) {
25487
- const signature = JSON.stringify(config4.agents);
25741
+ async function agentState(config5, { refresh = false } = {}) {
25742
+ const signature = JSON.stringify(config5.agents);
25488
25743
  if (!refresh && cached2 && cached2.signature === signature && Date.now() - cached2.at < PROBE_TTL_MS) {
25489
- return { ...cached2.state, active: config4.agent };
25744
+ return { ...cached2.state, active: config5.agent };
25490
25745
  }
25491
- const runners = await Promise.all(AGENT_KINDS.map((kind) => probe2(RUNNERS[kind], config4.agents[kind])));
25492
- const state = { active: config4.agent, runners };
25746
+ const runners = await Promise.all(AGENT_KINDS.map((kind) => probe2(RUNNERS[kind], config5.agents[kind])));
25747
+ const state = { active: config5.agent, runners };
25493
25748
  cached2 = { at: Date.now(), signature, state };
25494
25749
  return state;
25495
25750
  }
@@ -25582,8 +25837,8 @@ var TTL_MS = 3e4;
25582
25837
  var ID_RE = /^[0-9a-f]{16}$/;
25583
25838
  var cached3 = null;
25584
25839
  var known = /* @__PURE__ */ new Map();
25585
- function agentSkills(config4, { refresh = false } = {}) {
25586
- const agent = config4.agent;
25840
+ function agentSkills(config5, { refresh = false } = {}) {
25841
+ const agent = config5.agent;
25587
25842
  const dirs = RUNNERS[agent].skillDirs?.() ?? [];
25588
25843
  const signature = dirs.join("\n");
25589
25844
  if (!refresh && cached3 && cached3.agent === agent && cached3.dirs === signature && Date.now() - cached3.at < TTL_MS) {
@@ -25599,12 +25854,12 @@ function agentSkills(config4, { refresh = false } = {}) {
25599
25854
  cached3 = { at: Date.now(), agent, dirs: signature, skills };
25600
25855
  return skills.map(meta3);
25601
25856
  }
25602
- function resolveAgentSkill(id, config4) {
25857
+ function resolveAgentSkill(id, config5) {
25603
25858
  if (!ID_RE.test(id)) return unknown2();
25604
- if (!known.has(id)) agentSkills(config4, { refresh: true });
25859
+ if (!known.has(id)) agentSkills(config5, { refresh: true });
25605
25860
  const entry = known.get(id);
25606
- if (!entry || entry.agent !== config4.agent) return unknown2();
25607
- const dirs = RUNNERS[config4.agent].skillDirs?.() ?? [];
25861
+ if (!entry || entry.agent !== config5.agent) return unknown2();
25862
+ const dirs = RUNNERS[config5.agent].skillDirs?.() ?? [];
25608
25863
  if (!dirs.some((dir) => entry.path.startsWith(dir + sep2))) return unknown2();
25609
25864
  try {
25610
25865
  const stats = statSync4(entry.path);
@@ -25617,7 +25872,7 @@ function resolveAgentSkill(id, config4) {
25617
25872
  }
25618
25873
  };
25619
25874
  }
25620
- const body = splitFrontMatter(readFileSync9(entry.path, "utf8")).body.trim();
25875
+ const body = splitFrontMatter(readFileSync10(entry.path, "utf8")).body.trim();
25621
25876
  if (!body) return unknown2();
25622
25877
  return { skill: { name: entry.name, body } };
25623
25878
  } catch {
@@ -25643,11 +25898,11 @@ function scan(dir, agent, out) {
25643
25898
  return;
25644
25899
  }
25645
25900
  for (const entry of entries) {
25646
- const path = entry.name.endsWith(".md") ? join18(dir, entry.name) : join18(dir, entry.name, SKILL_FILE);
25901
+ const path = entry.name.endsWith(".md") ? join19(dir, entry.name) : join19(dir, entry.name, SKILL_FILE);
25647
25902
  try {
25648
25903
  const stats = statSync4(path);
25649
25904
  if (!stats.isFile() || stats.size > MAX_SKILL_BYTES) continue;
25650
- const { fields, body } = splitFrontMatter(readFileSync9(path, "utf8"));
25905
+ const { fields, body } = splitFrontMatter(readFileSync10(path, "utf8"));
25651
25906
  if (!body.trim()) continue;
25652
25907
  const name = clean(unquote(fields.name) || entry.name.replace(/\.md$/, ""), MAX_NAME);
25653
25908
  if (!name) continue;
@@ -25668,13 +25923,13 @@ function idOf(path) {
25668
25923
  }
25669
25924
 
25670
25925
  // agent/approvals.ts
25671
- import { chmodSync as chmodSync5, mkdirSync as mkdirSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "fs";
25672
- import { join as join19 } from "path";
25673
- var approvalsPath = join19(stateDir, "approvals.json");
25926
+ import { chmodSync as chmodSync5, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
25927
+ import { join as join20 } from "path";
25928
+ var approvalsPath = join20(stateDir, "approvals.json");
25674
25929
  var MAX_GRANTS = 200;
25675
25930
  function read2() {
25676
25931
  try {
25677
- const parsed2 = JSON.parse(readFileSync10(approvalsPath, "utf8"));
25932
+ const parsed2 = JSON.parse(readFileSync11(approvalsPath, "utf8"));
25678
25933
  if (!Array.isArray(parsed2.grants)) return [];
25679
25934
  return parsed2.grants.filter(
25680
25935
  (grant) => !!grant && typeof grant.action === "string" && typeof grant.host === "string" && typeof grant.at === "string"
@@ -25822,7 +26077,7 @@ function landedUrl(action, data) {
25822
26077
  }
25823
26078
 
25824
26079
  // agent/attachments.ts
25825
- import { randomUUID as randomUUID7 } from "crypto";
26080
+ import { randomUUID as randomUUID8 } from "crypto";
25826
26081
 
25827
26082
  // ../lib/files/report.ts
25828
26083
  var FILE_KINDS = ["text", "pdf", "image"];
@@ -25931,7 +26186,7 @@ async function reportOn(file2, deps) {
25931
26186
  const checked = validateFileReport(file2.report);
25932
26187
  return checked.ok ? checked.report : refusal("failed", deps.agent, "AGENT_FAILED", "The report on this file was damaged; re-attach it.");
25933
26188
  }
25934
- const toolId = randomUUID7();
26189
+ const toolId = randomUUID8();
25935
26190
  deps.emit({ kind: "tool", toolId, action: READ_FILE_ACTION, input: { name: file2.name } });
25936
26191
  const report = await deps.wait(file2.id, deps.signal) ?? refusal("failed", deps.agent, "AGENT_FAILED", "Browsentic lost track of this file\u2019s analysis \u2014 remove it and attach it again.");
25937
26192
  deps.emit({
@@ -26124,12 +26379,12 @@ ${overlay.body.trim()}`;
26124
26379
  }
26125
26380
 
26126
26381
  // agent/runner.ts
26127
- import { join as join21 } from "path";
26382
+ import { join as join22 } from "path";
26128
26383
 
26129
26384
  // agent/runners/drive.ts
26130
26385
  import { spawn as spawn3 } from "child_process";
26131
26386
  import { mkdirSync as mkdirSync9, utimesSync, writeFileSync as writeFileSync8 } from "fs";
26132
- import { dirname as dirname5, join as join20 } from "path";
26387
+ import { dirname as dirname6, join as join21 } from "path";
26133
26388
  import { createInterface } from "readline";
26134
26389
 
26135
26390
  // agent/runners/types.ts
@@ -26156,8 +26411,8 @@ function launch(kind, mode, settings, plan, signal) {
26156
26411
  log(describeContainment(kind));
26157
26412
  mkdirSync9(plan.cwd, { recursive: true, mode: 448 });
26158
26413
  for (const file2 of plan.files ?? []) {
26159
- const path = join20(plan.cwd, file2.path);
26160
- mkdirSync9(dirname5(path), { recursive: true, mode: 448 });
26414
+ const path = join21(plan.cwd, file2.path);
26415
+ mkdirSync9(dirname6(path), { recursive: true, mode: 448 });
26161
26416
  writeFileSync8(path, file2.content, { mode: 384 });
26162
26417
  }
26163
26418
  const now = /* @__PURE__ */ new Date();
@@ -26327,18 +26582,18 @@ function runInstruction(request) {
26327
26582
  request.emit
26328
26583
  );
26329
26584
  }
26330
- function runAgentJson(prompt, config4, signal, { reads = false, ...options }) {
26331
- const { runner, settings } = runnerFor(config4);
26585
+ function runAgentJson(prompt, config5, signal, { reads = false, ...options }) {
26586
+ const { runner, settings } = runnerFor(config5);
26332
26587
  return runJson(runner, { prompt, settings, reads, workspace: runner.workspace("task") }, signal, options);
26333
26588
  }
26334
- function taskDir(config4) {
26335
- return join21(runnerFor(config4).runner.workspace("task"), "tmp");
26589
+ function taskDir(config5) {
26590
+ return join22(runnerFor(config5).runner.workspace("task"), "tmp");
26336
26591
  }
26337
26592
 
26338
26593
  // agent/site-map-store.ts
26339
- import { randomUUID as randomUUID8 } from "crypto";
26340
- import { existsSync as existsSync5, mkdirSync as mkdirSync10, readFileSync as readFileSync11, readdirSync as readdirSync4, renameSync as renameSync3, rmSync as rmSync5, writeFileSync as writeFileSync9 } from "fs";
26341
- import { dirname as dirname6, join as join22, resolve as resolve2, sep as sep3 } from "path";
26594
+ import { randomUUID as randomUUID9 } from "crypto";
26595
+ import { existsSync as existsSync5, mkdirSync as mkdirSync10, readFileSync as readFileSync12, readdirSync as readdirSync4, renameSync as renameSync3, rmSync as rmSync5, writeFileSync as writeFileSync9 } from "fs";
26596
+ import { dirname as dirname7, join as join23, resolve as resolve2, sep as sep3 } from "path";
26342
26597
  var STAGING = ".staging";
26343
26598
  function mapTargetFor(url2) {
26344
26599
  let parsed2;
@@ -26380,21 +26635,21 @@ function uniqueName(domain2, host) {
26380
26635
  }
26381
26636
  function mappedHostOf(name) {
26382
26637
  try {
26383
- const meta4 = JSON.parse(readFileSync11(join22(uploadedSkillsDir(), name, "meta.json"), "utf8"));
26638
+ const meta4 = JSON.parse(readFileSync12(join23(uploadedSkillsDir(), name, "meta.json"), "utf8"));
26384
26639
  return typeof meta4.host === "string" ? meta4.host : "";
26385
26640
  } catch {
26386
- return existsSync5(join22(uploadedSkillsDir(), name, SKILL_FILE)) ? "" : null;
26641
+ return existsSync5(join23(uploadedSkillsDir(), name, SKILL_FILE)) ? "" : null;
26387
26642
  }
26388
26643
  }
26389
26644
  function prepareStaging() {
26390
- const id = randomUUID8();
26391
- const dir = join22(uploadedSkillsDir(), STAGING, id);
26645
+ const id = randomUUID9();
26646
+ const dir = join23(uploadedSkillsDir(), STAGING, id);
26392
26647
  const staging = {
26393
26648
  id,
26394
26649
  dir,
26395
- screenshots: join22(dir, "screenshots"),
26396
- evidence: join22(dir, "evidence"),
26397
- pages: join22(dir, "pages")
26650
+ screenshots: join23(dir, "screenshots"),
26651
+ evidence: join23(dir, "evidence"),
26652
+ pages: join23(dir, "pages")
26398
26653
  };
26399
26654
  for (const path of [dir, staging.screenshots, staging.evidence, staging.pages]) {
26400
26655
  mkdirSync10(path, { recursive: true, mode: 448 });
@@ -26410,7 +26665,7 @@ function stagedScreenshots(staging) {
26410
26665
  }
26411
26666
  function writeEvidence(staging, name, body) {
26412
26667
  if (!body.trim()) return;
26413
- writeFileSync9(join22(staging.evidence, name), body, { mode: 384 });
26668
+ writeFileSync9(join23(staging.evidence, name), body, { mode: 384 });
26414
26669
  }
26415
26670
  function stageSiteMap(args) {
26416
26671
  const { staging, target, report, index, background, warnings, runId } = args;
@@ -26429,17 +26684,17 @@ generatedAt: ${generatedAt}
26429
26684
  ---
26430
26685
 
26431
26686
  `);
26432
- writeFileSync9(join22(staging.dir, SKILL_FILE), markdown, { mode: 384 });
26433
- writeFileSync9(join22(staging.dir, "map.json"), JSON.stringify(report, null, 2), { mode: 384 });
26687
+ writeFileSync9(join23(staging.dir, SKILL_FILE), markdown, { mode: 384 });
26688
+ writeFileSync9(join23(staging.dir, "map.json"), JSON.stringify(report, null, 2), { mode: 384 });
26434
26689
  writeFileSync9(
26435
- join22(staging.dir, "meta.json"),
26690
+ join23(staging.dir, "meta.json"),
26436
26691
  JSON.stringify({ name: target.name, host: target.host, domain: target.domain, generatedAt, runId }, null, 2),
26437
26692
  { mode: 384 }
26438
26693
  );
26439
26694
  for (const [index_, page] of report.pages.entries()) {
26440
26695
  if (!page.notes) continue;
26441
26696
  const file2 = `${String(index_ + 1).padStart(2, "0")}-${skillNameForHost(page.path) || "page"}.md`;
26442
- writeFileSync9(join22(staging.pages, file2), `# ${page.title}
26697
+ writeFileSync9(join23(staging.pages, file2), `# ${page.title}
26443
26698
 
26444
26699
  ${page.path}
26445
26700
 
@@ -26451,7 +26706,7 @@ ${page.notes}
26451
26706
  name: target.name,
26452
26707
  host: target.host,
26453
26708
  domain: target.domain,
26454
- directory: join22(uploadedSkillsDir(), target.name),
26709
+ directory: join23(uploadedSkillsDir(), target.name),
26455
26710
  markdown,
26456
26711
  pages: report.pages.length,
26457
26712
  screenshots: stagedScreenshots(staging).length,
@@ -26502,7 +26757,7 @@ function renderSiteMapBody(args) {
26502
26757
  }
26503
26758
  const shots = report.pages.filter((page) => page.screenshot).length;
26504
26759
  if (shots) {
26505
- out.push("", "## Screenshots", "", `Full-size captures: ${join22(uploadedSkillsDir(), target.name, "screenshots")}`);
26760
+ out.push("", "## Screenshots", "", `Full-size captures: ${join23(uploadedSkillsDir(), target.name, "screenshots")}`);
26506
26761
  }
26507
26762
  const body = out.join("\n");
26508
26763
  return body.length > MAX_MAP_BODY_BYTES ? `${body.slice(0, MAX_MAP_BODY_BYTES - 40)}
@@ -26514,7 +26769,7 @@ function commitStaging(stagingId, exactHost = false) {
26514
26769
  if (!staging) return failure("NOT_FOUND", "That mapping run is no longer staged.");
26515
26770
  let meta4;
26516
26771
  try {
26517
- meta4 = JSON.parse(readFileSync11(join22(staging, "meta.json"), "utf8"));
26772
+ meta4 = JSON.parse(readFileSync12(join23(staging, "meta.json"), "utf8"));
26518
26773
  } catch {
26519
26774
  return failure("NOT_FOUND", "That staged map is incomplete.");
26520
26775
  }
@@ -26526,15 +26781,15 @@ function commitStaging(stagingId, exactHost = false) {
26526
26781
  return failure("NAME_TAKEN", `"${name}" is now a skill you wrote by hand. Remove it first, or discard this map.`);
26527
26782
  }
26528
26783
  if (exactHost && host !== domain2) {
26529
- const path = join22(staging, SKILL_FILE);
26530
- writeFileSync9(path, readFileSync11(path, "utf8").replace(`domains: [${domain2}]`, `domains: [${host}]`), {
26784
+ const path = join23(staging, SKILL_FILE);
26785
+ writeFileSync9(path, readFileSync12(path, "utf8").replace(`domains: [${domain2}]`, `domains: [${host}]`), {
26531
26786
  mode: 384
26532
26787
  });
26533
26788
  }
26534
- const destination = join22(uploadedSkillsDir(), name);
26789
+ const destination = join23(uploadedSkillsDir(), name);
26535
26790
  if (!contained(destination)) return failure("INVALID_INPUT", "Refusing to write outside the skills directory.");
26536
26791
  if (existsSync5(destination)) {
26537
- renameSync3(destination, join22(uploadedSkillsDir(), STAGING, `${name}-replaced-${randomUUID8().slice(0, 8)}`));
26792
+ renameSync3(destination, join23(uploadedSkillsDir(), STAGING, `${name}-replaced-${randomUUID9().slice(0, 8)}`));
26538
26793
  }
26539
26794
  renameSync3(staging, destination);
26540
26795
  log(`activated site map ${name} (${host})`);
@@ -26549,9 +26804,9 @@ function discardStaging(stagingId) {
26549
26804
  }
26550
26805
  function stagingDirFor(stagingId) {
26551
26806
  if (!/^[0-9a-f-]{36}$/i.test(stagingId)) return null;
26552
- const dir = resolve2(join22(uploadedSkillsDir(), STAGING, stagingId));
26553
- if (dirname6(dir) !== resolve2(join22(uploadedSkillsDir(), STAGING))) return null;
26554
- return existsSync5(join22(dir, SKILL_FILE)) ? dir : null;
26807
+ const dir = resolve2(join23(uploadedSkillsDir(), STAGING, stagingId));
26808
+ if (dirname7(dir) !== resolve2(join23(uploadedSkillsDir(), STAGING))) return null;
26809
+ return existsSync5(join23(dir, SKILL_FILE)) ? dir : null;
26555
26810
  }
26556
26811
  function contained(path) {
26557
26812
  const root = resolve2(uploadedSkillsDir());
@@ -26559,7 +26814,7 @@ function contained(path) {
26559
26814
  return target === root || target.startsWith(root + sep3);
26560
26815
  }
26561
26816
  function sweepStaging(maxAgeMs = 24 * 60 * 60 * 1e3, now = Date.now()) {
26562
- const root = join22(uploadedSkillsDir(), STAGING);
26817
+ const root = join23(uploadedSkillsDir(), STAGING);
26563
26818
  let entries;
26564
26819
  try {
26565
26820
  entries = readdirSync4(root);
@@ -26568,12 +26823,12 @@ function sweepStaging(maxAgeMs = 24 * 60 * 60 * 1e3, now = Date.now()) {
26568
26823
  }
26569
26824
  for (const entry of entries) {
26570
26825
  try {
26571
- const meta4 = JSON.parse(readFileSync11(join22(root, entry, "meta.json"), "utf8"));
26826
+ const meta4 = JSON.parse(readFileSync12(join23(root, entry, "meta.json"), "utf8"));
26572
26827
  const at = typeof meta4.generatedAt === "string" ? Date.parse(meta4.generatedAt) : 0;
26573
26828
  if (at && now - at < maxAgeMs) continue;
26574
26829
  } catch {
26575
26830
  }
26576
- rmSync5(join22(root, entry), { recursive: true, force: true });
26831
+ rmSync5(join23(root, entry), { recursive: true, force: true });
26577
26832
  log(`swept abandoned staging ${entry}`);
26578
26833
  }
26579
26834
  }
@@ -26970,7 +27225,7 @@ var AgentSession = class {
26970
27225
  return failure("RUN_INACTIVE", "This agent run is no longer active");
26971
27226
  }
26972
27227
  const emit = (event) => this.deps.emit(runId, event);
26973
- const toolId = randomUUID9();
27228
+ const toolId = randomUUID10();
26974
27229
  emit({ kind: "tool", toolId, action, input: input2 });
26975
27230
  if (action === SAVE_SITE_MAP_ACTION) {
26976
27231
  if (!run.map) {
@@ -27064,8 +27319,8 @@ var AgentSession = class {
27064
27319
  message: `No skills found. Looked in ${skillDirNames().join(" and ")} \u2014 reinstall the package, or add a skill of your own.`
27065
27320
  });
27066
27321
  }
27067
- const config4 = readAgentConfig();
27068
- const limit = maxConcurrentRuns(config4);
27322
+ const config5 = readAgentConfig();
27323
+ const limit = maxConcurrentRuns(config5);
27069
27324
  if ((this.deps.running?.() ?? this.runs.size) >= limit) {
27070
27325
  return emit({
27071
27326
  kind: "error",
@@ -27086,24 +27341,24 @@ var AgentSession = class {
27086
27341
  if (mapping) {
27087
27342
  log(`agent run ${runId}: ignoring the attached agent skill \u2014 mapping runs build their own prompt`);
27088
27343
  } else {
27089
- const resolved = resolveAgentSkill(context.agentSkillId, config4);
27344
+ const resolved = resolveAgentSkill(context.agentSkillId, config5);
27090
27345
  if ("error" in resolved) return emit({ kind: "error", ...resolved.error });
27091
27346
  attached = resolved.skill;
27092
27347
  }
27093
27348
  }
27094
27349
  const overlayNames = routed.overlays.map((overlay) => overlay.name);
27095
- const policy = policyFrom(config4.guardrails, config4.requireApproval);
27350
+ const policy = policyFrom(config5.guardrails, config5.requireApproval);
27096
27351
  const scope = scopeFor({
27097
27352
  url: context?.url,
27098
27353
  tabId: context?.tabId,
27099
27354
  instruction: text2,
27100
- extraHosts: config4.guardrails?.hosts,
27355
+ extraHosts: config5.guardrails?.hosts,
27101
27356
  pinTab: true
27102
27357
  });
27103
27358
  const run = {
27104
27359
  id: runId,
27105
27360
  sessionId: sessionId2,
27106
- config: config4,
27361
+ config: config5,
27107
27362
  policy,
27108
27363
  scope,
27109
27364
  site: siteOf(context?.url),
@@ -27116,20 +27371,20 @@ var AgentSession = class {
27116
27371
  };
27117
27372
  this.runs.set(runId, run);
27118
27373
  if (run.liveTools && sessionId2) this.codeListed.add(sessionId2);
27119
- const runner = activeRunner(await agentState(config4));
27374
+ const runner = activeRunner(await agentState(config5));
27120
27375
  if (!runner?.ready) {
27121
27376
  const problem = runner?.problem;
27122
27377
  this.release(run);
27123
- log(`agent run ${runId} refused: ${AGENTS[config4.agent].label} is not ready`);
27378
+ log(`agent run ${runId} refused: ${AGENTS[config5.agent].label} is not ready`);
27124
27379
  return emit({
27125
27380
  kind: "error",
27126
27381
  code: problem?.code ?? "AGENT_MISSING",
27127
- message: `${AGENTS[config4.agent].label} cannot run. ${problem?.message ?? "It is not available."}${problem?.fix ? ` ${problem.fix}` : ""}`
27382
+ message: `${AGENTS[config5.agent].label} cannot run. ${problem?.message ?? "It is not available."}${problem?.fix ? ` ${problem.fix}` : ""}`
27128
27383
  });
27129
27384
  }
27130
27385
  const holding = sessionId2 ? this.held.get(sessionId2) : void 0;
27131
- const held = holding?.agent === config4.agent ? holding.sessionId : null;
27132
- const supplied = mapping ? null : sessionFrom(context, config4.agent);
27386
+ const held = holding?.agent === config5.agent ? holding.sessionId : null;
27387
+ const supplied = mapping ? null : sessionFrom(context, config5.agent);
27133
27388
  const resuming = mapping ? null : supplied ?? held;
27134
27389
  let built;
27135
27390
  let instructionText = routed.text;
@@ -27143,7 +27398,7 @@ var AgentSession = class {
27143
27398
  } else {
27144
27399
  const files = resuming ? context?.files : context?.files?.map((file2) => ({ ...file2, delivered: false }));
27145
27400
  handover = await handOver(files, {
27146
- agent: config4.agent,
27401
+ agent: config5.agent,
27147
27402
  signal: run.abort.signal,
27148
27403
  emit,
27149
27404
  wait: (fileId, signal) => this.deps.awaitAnalysis?.(fileId, signal) ?? Promise.resolve(null)
@@ -27191,11 +27446,11 @@ var AgentSession = class {
27191
27446
  });
27192
27447
  if (!mapping) {
27193
27448
  if (sessionId2) {
27194
- if (outcome.sessionId) this.held.set(sessionId2, { agent: config4.agent, sessionId: outcome.sessionId });
27449
+ if (outcome.sessionId) this.held.set(sessionId2, { agent: config5.agent, sessionId: outcome.sessionId });
27195
27450
  else this.held.delete(sessionId2);
27196
27451
  }
27197
27452
  if (outcome.sessionId !== resuming) {
27198
- emit({ kind: "session", agent: config4.agent, agentSessionId: outcome.sessionId });
27453
+ emit({ kind: "session", agent: config5.agent, agentSessionId: outcome.sessionId });
27199
27454
  }
27200
27455
  }
27201
27456
  log(`agent run ${runId} finished (${outcome.stopReason})`);
@@ -27228,7 +27483,7 @@ var AgentSession = class {
27228
27483
  sweepStaging();
27229
27484
  const settings = siteMapSettings(run.config);
27230
27485
  const staging = prepareStaging();
27231
- const toolId = randomUUID9();
27486
+ const toolId = randomUUID10();
27232
27487
  emit({ kind: "tool", toolId, action: READ_SITEMAP_ACTION, input: { origin: target.target.origin } });
27233
27488
  let index;
27234
27489
  try {
@@ -27431,27 +27686,27 @@ function clip2(text2) {
27431
27686
  }
27432
27687
 
27433
27688
  // agent/captcha-analyst.ts
27434
- import { randomUUID as randomUUID10 } from "crypto";
27689
+ import { randomUUID as randomUUID11 } from "crypto";
27435
27690
  import { mkdirSync as mkdirSync11, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
27436
- import { join as join23 } from "path";
27691
+ import { join as join24 } from "path";
27437
27692
  var MAX_POINTS = 16;
27438
27693
  var ANSWER_TIMEOUT_MS = 45e3;
27439
27694
  var OPEN2 = "=== ANSWER ===";
27440
27695
  var QUICK_EFFORT = "low";
27441
- var analystSees = (config4) => runnerFor(config4).runner.opens?.includes("image") === true;
27442
- async function answerChallenge(challenge, config4, signal) {
27696
+ var analystSees = (config5) => runnerFor(config5).runner.opens?.includes("image") === true;
27697
+ async function answerChallenge(challenge, config5, signal) {
27443
27698
  const picture = decodeImage(challenge.image);
27444
- if (!picture || !analystSees(config4) || signal.aborted) return null;
27445
- const dir = taskDir(config4);
27699
+ if (!picture || !analystSees(config5) || signal.aborted) return null;
27700
+ const dir = taskDir(config5);
27446
27701
  mkdirSync11(dir, { recursive: true, mode: 448 });
27447
- const path = join23(dir, `${randomUUID10()}-captcha.${picture.extension}`);
27702
+ const path = join24(dir, `${randomUUID11()}-captcha.${picture.extension}`);
27448
27703
  writeFileSync10(path, picture.bytes, { mode: 384 });
27449
27704
  const stop = new AbortController();
27450
27705
  const timer = setTimeout(() => stop.abort(new RunError("TIMEOUT", "The captcha analyst took too long.")), ANSWER_TIMEOUT_MS);
27451
27706
  const cancelled = () => stop.abort(signal.reason);
27452
27707
  signal.addEventListener("abort", cancelled, { once: true });
27453
27708
  try {
27454
- const output = await runAgentJson(promptFor(path, challenge), quick(config4), stop.signal, {
27709
+ const output = await runAgentJson(promptFor(path, challenge), quick(config5), stop.signal, {
27455
27710
  reads: true,
27456
27711
  timedOut: "The captcha analyst took too long.",
27457
27712
  empty: "The captcha analyst returned nothing.",
@@ -27540,10 +27795,10 @@ ${OPEN2}
27540
27795
  ].filter(Boolean).join("\n\n");
27541
27796
  }
27542
27797
  var refusal2 = (errors) => errors?.length ? `The last answer was refused with: ${JSON.stringify(scrub(errors.join(" "), 200))}. Look again more carefully.` : "";
27543
- function quick(config4) {
27544
- const { runner } = runnerFor(config4);
27545
- if (!runner.efforts.includes(QUICK_EFFORT)) return config4;
27546
- return { ...config4, agents: { ...config4.agents, [runner.kind]: { ...config4.agents[runner.kind], effort: QUICK_EFFORT } } };
27798
+ function quick(config5) {
27799
+ const { runner } = runnerFor(config5);
27800
+ if (!runner.efforts.includes(QUICK_EFFORT)) return config5;
27801
+ return { ...config5, agents: { ...config5.agents, [runner.kind]: { ...config5.agents[runner.kind], effort: QUICK_EFFORT } } };
27547
27802
  }
27548
27803
  function decodeImage(dataUrl) {
27549
27804
  const match = dataUrl ? /^data:image\/(jpeg|png|webp);base64,(.+)$/s.exec(dataUrl) : null;
@@ -27555,12 +27810,12 @@ function decodeImage(dataUrl) {
27555
27810
  var MAX_ROUNDS = 10;
27556
27811
  var ROUND_FLOOR_MS = 12e3;
27557
27812
  var CLICK_RESERVE_MS = 8e3;
27558
- async function solveCaptchaWithAnalyst(invoke, input2, config4, answer = answerChallenge) {
27813
+ async function solveCaptchaWithAnalyst(invoke, input2, config5, answer = answerChallenge) {
27559
27814
  const asked = input2 ?? {};
27560
27815
  const budget = typeof asked.timeoutMs === "number" ? asked.timeoutMs : SOLVE_CAPTCHA_TIMEOUT_MS;
27561
27816
  const until = Date.now() + budget;
27562
27817
  const first = await invoke({ ...asked, timeoutMs: budget });
27563
- if (asked.tiles !== void 0 || asked.reload !== void 0 || !analystSees(config4)) return first;
27818
+ if (asked.tiles !== void 0 || asked.reload !== void 0 || !analystSees(config5)) return first;
27564
27819
  let result = first;
27565
27820
  const answers = [];
27566
27821
  let stopped;
@@ -27574,7 +27829,7 @@ async function solveCaptchaWithAnalyst(invoke, input2, config4, answer = answerC
27574
27829
  stopped = "ran out of time";
27575
27830
  break;
27576
27831
  }
27577
- const given = await answerWithin(answer, openChallenge(result), config4, left - CLICK_RESERVE_MS);
27832
+ const given = await answerWithin(answer, openChallenge(result), config5, left - CLICK_RESERVE_MS);
27578
27833
  if (!given) {
27579
27834
  stopped = "could not answer this round";
27580
27835
  break;
@@ -27595,11 +27850,11 @@ function openChallenge(result) {
27595
27850
  const data = result.data;
27596
27851
  return data?.state === "challenge" && data.challenge?.image ? data.challenge : null;
27597
27852
  }
27598
- async function answerWithin(answer, challenge, config4, ms) {
27853
+ async function answerWithin(answer, challenge, config5, ms) {
27599
27854
  const controller = new AbortController();
27600
27855
  const timer = setTimeout(() => controller.abort(), ms);
27601
27856
  try {
27602
- return await answer(challenge, config4, controller.signal);
27857
+ return await answer(challenge, config5, controller.signal);
27603
27858
  } finally {
27604
27859
  clearTimeout(timer);
27605
27860
  }
@@ -27628,9 +27883,9 @@ function handBack(result, rounds, why) {
27628
27883
  }
27629
27884
 
27630
27885
  // agent/file-analyst.ts
27631
- import { randomUUID as randomUUID11 } from "crypto";
27886
+ import { randomUUID as randomUUID12 } from "crypto";
27632
27887
  import { mkdirSync as mkdirSync12, unlinkSync as unlinkSync3, writeFileSync as writeFileSync11 } from "fs";
27633
- import { join as join24 } from "path";
27888
+ import { join as join25 } from "path";
27634
27889
 
27635
27890
  // agent/file-screen.ts
27636
27891
  var SAMPLE_BYTES = 64 * 1024;
@@ -27713,12 +27968,12 @@ var FileAnalyses = class {
27713
27968
  kept = /* @__PURE__ */ new Map();
27714
27969
  running = 0;
27715
27970
  waiting = [];
27716
- start(req, config4, owner) {
27971
+ start(req, config5, owner) {
27717
27972
  this.cancel(req.fileId);
27718
27973
  this.take(req.fileId);
27719
27974
  const controller = new AbortController();
27720
27975
  const report = this.acquire(controller.signal).then(
27721
- (release) => analyzeFile(req, config4, controller.signal).finally(release)
27976
+ (release) => analyzeFile(req, config5, controller.signal).finally(release)
27722
27977
  );
27723
27978
  const analysis = { owner, controller, report };
27724
27979
  this.live.set(req.fileId, analysis);
@@ -27796,8 +28051,8 @@ var FileAnalyses = class {
27796
28051
  });
27797
28052
  }
27798
28053
  };
27799
- async function analyzeFile(req, config4, signal) {
27800
- const { runner } = runnerFor(config4);
28054
+ async function analyzeFile(req, config5, signal) {
28055
+ const { runner } = runnerFor(config5);
27801
28056
  const agent = runner.kind;
27802
28057
  if (signal.aborted) return failedWith(agent, signal.reason);
27803
28058
  const bytes = Buffer.from(typeof req.content === "string" ? req.content : "", "base64");
@@ -27811,9 +28066,9 @@ async function analyzeFile(req, config4, signal) {
27811
28066
  return refusal("rejected", agent, screened.code, screened.message, screened.kind);
27812
28067
  }
27813
28068
  const { kind, extension: extension2 } = screened;
27814
- const tmpDir = taskDir(config4);
28069
+ const tmpDir = taskDir(config5);
27815
28070
  mkdirSync12(tmpDir, { recursive: true, mode: 448 });
27816
- const path = join24(tmpDir, `${randomUUID11()}-${safeName2(req.name)}.${extension2}`);
28071
+ const path = join25(tmpDir, `${randomUUID12()}-${safeName2(req.name)}.${extension2}`);
27817
28072
  writeFileSync11(path, bytes, { mode: 384 });
27818
28073
  const stop = new AbortController();
27819
28074
  const timer = setTimeout(() => stop.abort(new RunError("TIMEOUT", "Reading the file took too long.")), ANALYZE_TIMEOUT_MS);
@@ -27821,7 +28076,7 @@ async function analyzeFile(req, config4, signal) {
27821
28076
  signal.addEventListener("abort", cancelled, { once: true });
27822
28077
  log(`file analyst reading ${req.name} (${bytes.length} bytes, ${kind}) with ${AGENTS[agent].label}`);
27823
28078
  try {
27824
- const output = await runAgentJson(promptFor2(path, req.name, kind), config4, stop.signal, {
28079
+ const output = await runAgentJson(promptFor2(path, req.name, kind), config5, stop.signal, {
27825
28080
  reads: true,
27826
28081
  timedOut: "Reading the file took too long.",
27827
28082
  empty: "The file analyst returned nothing.",
@@ -27905,9 +28160,9 @@ function safeName2(name) {
27905
28160
  }
27906
28161
 
27907
28162
  // agent/recording.ts
27908
- import { randomUUID as randomUUID12 } from "crypto";
28163
+ import { randomUUID as randomUUID13 } from "crypto";
27909
28164
  import { mkdirSync as mkdirSync13, unlinkSync as unlinkSync4, writeFileSync as writeFileSync12 } from "fs";
27910
- import { join as join25 } from "path";
28165
+ import { join as join26 } from "path";
27911
28166
 
27912
28167
  // ../lib/recordings/workflow.ts
27913
28168
  var MAX_STEPS = 80;
@@ -28064,22 +28319,22 @@ function trimToBudget2(workflow, warnings) {
28064
28319
  var ANALYZE_TIMEOUT_MS2 = 11e4;
28065
28320
  var MAX_TRACE_BYTES = 4 * 1024 * 1024;
28066
28321
  var OPEN4 = "=== WORKFLOW ===";
28067
- async function analyzeRecording(req, config4) {
28322
+ async function analyzeRecording(req, config5) {
28068
28323
  const recording = req.recording;
28069
28324
  if (!recording?.events?.length) return failure("INVALID_INPUT", "The recording has no steps.");
28070
28325
  const trace = JSON.stringify({ ...recording, events: recording.events }, null, 1);
28071
28326
  if (Buffer.byteLength(trace) > MAX_TRACE_BYTES) {
28072
28327
  return failure("RECORDING_TOO_LARGE", "The recorded trace is too large to summarize.");
28073
28328
  }
28074
- const tmpDir = taskDir(config4);
28329
+ const tmpDir = taskDir(config5);
28075
28330
  mkdirSync13(tmpDir, { recursive: true, mode: 448 });
28076
- const path = join25(tmpDir, `${randomUUID12()}-recording.json`);
28331
+ const path = join26(tmpDir, `${randomUUID13()}-recording.json`);
28077
28332
  writeFileSync12(path, trace, { mode: 384 });
28078
28333
  const controller = new AbortController();
28079
28334
  const timer = setTimeout(() => controller.abort(), ANALYZE_TIMEOUT_MS2);
28080
28335
  log(`analyzing recording ${recording.name} (${recording.events.length} events on ${recording.host})`);
28081
28336
  try {
28082
- const output = await runAgentJson(promptFor3(path, recording), config4, controller.signal, {
28337
+ const output = await runAgentJson(promptFor3(path, recording), config5, controller.signal, {
28083
28338
  reads: true,
28084
28339
  timedOut: "Splitting the recording into steps took too long.",
28085
28340
  empty: "The agent returned an empty workflow."
@@ -28151,13 +28406,13 @@ var MAX_TITLE_CHARS = 60;
28151
28406
  var MAX_MESSAGES = 12;
28152
28407
  var MAX_MESSAGE_CHARS = 400;
28153
28408
  var NAME_TIMEOUT_MS = 3e4;
28154
- async function nameSession(req, config4) {
28409
+ async function nameSession(req, config5) {
28155
28410
  const messages = (Array.isArray(req.messages) ? req.messages : []).filter((message) => typeof message === "string" && message.trim().length > 0).slice(-MAX_MESSAGES).map((message) => message.trim().slice(0, MAX_MESSAGE_CHARS));
28156
28411
  if (!messages.length) return failure("INVALID_INPUT", "Nothing was said in that conversation yet.");
28157
28412
  const controller = new AbortController();
28158
28413
  const timer = setTimeout(() => controller.abort(), NAME_TIMEOUT_MS);
28159
28414
  try {
28160
- const output = await runAgentJson(promptFor4(messages, req.host), config4, controller.signal, {
28415
+ const output = await runAgentJson(promptFor4(messages, req.host), config5, controller.signal, {
28161
28416
  timedOut: "Naming the conversation took too long.",
28162
28417
  empty: "The agent returned an empty name."
28163
28418
  });
@@ -28186,7 +28441,7 @@ function clamp3(output) {
28186
28441
 
28187
28442
  // agent/skill-store.ts
28188
28443
  import { existsSync as existsSync6, mkdirSync as mkdirSync14, readdirSync as readdirSync5, renameSync as renameSync4, rmSync as rmSync6, writeFileSync as writeFileSync13, chmodSync as chmodSync6 } from "fs";
28189
- import { dirname as dirname7, join as join26, resolve as resolve3 } from "path";
28444
+ import { dirname as dirname8, join as join27, resolve as resolve3 } from "path";
28190
28445
  var MAX_SKILL_FILES = 50;
28191
28446
  function saveSkill(draft) {
28192
28447
  const checked = validateSkillDraft(draft, { reservedNames: bundledSkillNames() });
@@ -28201,7 +28456,7 @@ function saveSkill(draft) {
28201
28456
  const dir = uploadedSkillsDir();
28202
28457
  const path = pathIn(dir, skill.name);
28203
28458
  if (!path) return failure("INVALID_INPUT", `"${skill.name}" is not a usable skill name.`);
28204
- if (existsSync6(join26(dir, skill.name, SKILL_FILE))) {
28459
+ if (existsSync6(join27(dir, skill.name, SKILL_FILE))) {
28205
28460
  return failure(
28206
28461
  "NAME_TAKEN",
28207
28462
  `"${skill.name}" is a mapped site. Remove that map first, or give this skill another name.`
@@ -28242,7 +28497,7 @@ function deleteSiteMap(name) {
28242
28497
  const path = pathIn(dir, name);
28243
28498
  if (!path) return failure("INVALID_INPUT", `"${name}" is not a usable skill name.`);
28244
28499
  const mapDir = path.replace(/\.md$/, "");
28245
- if (!existsSync6(join26(mapDir, SKILL_FILE))) {
28500
+ if (!existsSync6(join27(mapDir, SKILL_FILE))) {
28246
28501
  return failure("NOT_FOUND", `No mapped site called "${name}".`);
28247
28502
  }
28248
28503
  try {
@@ -28256,13 +28511,13 @@ function deleteSiteMap(name) {
28256
28511
  }
28257
28512
  function pathIn(dir, name) {
28258
28513
  if (!SKILL_NAME_RE.test(name)) return null;
28259
- const candidate = resolve3(join26(dir, `${name}.md`));
28260
- return dirname7(candidate) === resolve3(dir) ? candidate : null;
28514
+ const candidate = resolve3(join27(dir, `${name}.md`));
28515
+ return dirname8(candidate) === resolve3(dir) ? candidate : null;
28261
28516
  }
28262
28517
  function countSkills(dir) {
28263
28518
  try {
28264
28519
  return readdirSync5(dir, { withFileTypes: true }).filter(
28265
- (entry) => !entry.name.startsWith(".") && (entry.isFile() ? entry.name.endsWith(".md") : entry.isDirectory() && existsSync6(join26(dir, entry.name, SKILL_FILE)))
28520
+ (entry) => !entry.name.startsWith(".") && (entry.isFile() ? entry.name.endsWith(".md") : entry.isDirectory() && existsSync6(join27(dir, entry.name, SKILL_FILE)))
28266
28521
  ).length;
28267
28522
  } catch {
28268
28523
  return 0;
@@ -28270,7 +28525,7 @@ function countSkills(dir) {
28270
28525
  }
28271
28526
 
28272
28527
  // extension-link.ts
28273
- import { randomUUID as randomUUID13 } from "crypto";
28528
+ import { randomUUID as randomUUID14 } from "crypto";
28274
28529
  var PING_INTERVAL_MS = 2e4;
28275
28530
  var DEFAULT_TIMEOUT_MS2 = 3e4;
28276
28531
  var DESCRIBE_TIMEOUT_MS = 1e4;
@@ -28292,7 +28547,7 @@ var ExtensionLink = class {
28292
28547
  log("extension socket error", error51);
28293
28548
  this.dispose("socket error");
28294
28549
  });
28295
- this.ping = setInterval(() => this.send({ t: "ping", id: randomUUID13() }), PING_INTERVAL_MS);
28550
+ this.ping = setInterval(() => this.send({ t: "ping", id: randomUUID14() }), PING_INTERVAL_MS);
28296
28551
  }
28297
28552
  socket;
28298
28553
  onClose;
@@ -28316,11 +28571,11 @@ var ExtensionLink = class {
28316
28571
  return !this.closed && this.socket.readyState === this.socket.OPEN;
28317
28572
  }
28318
28573
  invoke(action, input2, opts) {
28319
- const frame = { t: "invoke", id: randomUUID13(), action, input: input2, ...opts };
28574
+ const frame = { t: "invoke", id: randomUUID14(), action, input: input2, ...opts };
28320
28575
  return this.request(frame, timeoutFor(action, input2));
28321
28576
  }
28322
28577
  async describe() {
28323
- const result = await this.request({ t: "describe", id: randomUUID13() }, DESCRIBE_TIMEOUT_MS);
28578
+ const result = await this.request({ t: "describe", id: randomUUID14() }, DESCRIBE_TIMEOUT_MS);
28324
28579
  return result.ok ? result.data : null;
28325
28580
  }
28326
28581
  send(frame) {
@@ -28771,8 +29026,8 @@ async function startDaemon({ version: version3, idleExit = true }) {
28771
29026
  writeGuardrailSetting(request.setting, request.value);
28772
29027
  log(`guardrail ${request.setting} \u2192 ${request.value === null ? "default" : String(request.value)}`);
28773
29028
  }
28774
- const config4 = readAgentConfig();
28775
- return success(guardrailSettings(config4.guardrails ?? {}, config4.requireApproval, configPath));
29029
+ const config5 = readAgentConfig();
29030
+ return success(guardrailSettings(config5.guardrails ?? {}, config5.requireApproval, configPath));
28776
29031
  }
28777
29032
  async function pushAgentState(target) {
28778
29033
  const state = await agentState(readAgentConfig());
@@ -28780,7 +29035,7 @@ async function startDaemon({ version: version3, idleExit = true }) {
28780
29035
  }
28781
29036
  function skillCatalogNow(refresh = false) {
28782
29037
  try {
28783
- const config4 = readAgentConfig();
29038
+ const config5 = readAgentConfig();
28784
29039
  const skills = loadSkills().map((skill) => ({
28785
29040
  name: skill.name,
28786
29041
  description: skill.description,
@@ -28788,7 +29043,7 @@ async function startDaemon({ version: version3, idleExit = true }) {
28788
29043
  domains: skill.domains,
28789
29044
  source: skill.source
28790
29045
  }));
28791
- return success({ agent: config4.agent, skills, agentSkills: agentSkills(config4, { refresh }) });
29046
+ return success({ agent: config5.agent, skills, agentSkills: agentSkills(config5, { refresh }) });
28792
29047
  } catch (error51) {
28793
29048
  return failure("AGENT_FAILED", String(error51));
28794
29049
  }
@@ -28849,8 +29104,8 @@ async function startDaemon({ version: version3, idleExit = true }) {
28849
29104
  return { tools: running[0].tools.filter(({ name }) => !offer.withheld.includes(name)), reserved: offer.reserved };
28850
29105
  }
28851
29106
  const tools = routeFor(binding)?.tools ?? bundled;
28852
- const config4 = readAgentConfig();
28853
- const policy = policyFrom(config4.guardrails, config4.requireApproval);
29107
+ const config5 = readAgentConfig();
29108
+ const policy = policyFrom(config5.guardrails, config5.requireApproval);
28854
29109
  const denied = (action) => decide({ action, input: {}, caller: "external", scope: ANYWHERE }, policy).effect === "deny";
28855
29110
  return {
28856
29111
  tools: tools.filter(({ name }) => !((name === INJECT_ACTION || name === RUN_CODE_ACTION) && denied(name))),
@@ -28971,15 +29226,15 @@ async function startDaemon({ version: version3, idleExit = true }) {
28971
29226
  return persistDownload(action, persistScreenshot(action, input2, result, opts?.saveTo), opts?.hosts);
28972
29227
  }
28973
29228
  async function invokeExternal(target, action, input2, client) {
28974
- const toolId = randomUUID14();
29229
+ const toolId = randomUUID15();
28975
29230
  const tell = (event) => {
28976
29231
  if (target?.isOpen) target.send({ t: "run", id: EXTERNAL_RUN_ID, event });
28977
29232
  };
28978
29233
  tell({ kind: "tool", toolId, action, input: input2, source: "external" });
28979
- const config4 = readAgentConfig();
29234
+ const config5 = readAgentConfig();
28980
29235
  const decision = decide(
28981
29236
  { action, input: input2, caller: "external", scope: ANYWHERE },
28982
- policyFrom(config4.guardrails, config4.requireApproval)
29237
+ policyFrom(config5.guardrails, config5.requireApproval)
28983
29238
  );
28984
29239
  if (decision.effect === "deny") {
28985
29240
  log(`external ${client} \u2192 ${action} blocked: ${describe4(decision)}`);
@@ -29084,7 +29339,8 @@ X-Browsentic-Reason: ${reason}\r
29084
29339
  // package.json
29085
29340
  var package_default = {
29086
29341
  name: "browsentic",
29087
- version: "0.7.4",
29342
+ mcpName: "io.github.imshaikot/browsentic",
29343
+ version: "0.7.5",
29088
29344
  description: "A browser extension with an AI side panel that hands your real, logged-in browser to the AI agent you already run. Installs the extension, runs the local daemon, and optionally speaks MCP.",
29089
29345
  type: "module",
29090
29346
  license: "MIT",