@aspectlylabs/mikka 0.0.155 → 0.0.157

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/mikka.js CHANGED
@@ -4481,7 +4481,7 @@ import { join as join4 } from "node:path";
4481
4481
  // package.json
4482
4482
  var package_default = {
4483
4483
  name: "@mikka/daemon",
4484
- version: "0.0.155",
4484
+ version: "0.0.157",
4485
4485
  description: "Local Mikka daemon: runs agents through the user's own CLIs",
4486
4486
  type: "module",
4487
4487
  private: true,
@@ -4541,7 +4541,7 @@ var package_default = {
4541
4541
  engines: {
4542
4542
  node: ">=24"
4543
4543
  },
4544
- gitHead: "a0295ff9031591fc84944f77c47074e8345d129e"
4544
+ gitHead: "6b1b0ad1cf20baee870da2c08c5d57c49a02daba"
4545
4545
  };
4546
4546
 
4547
4547
  // src/paths.ts
@@ -4816,7 +4816,7 @@ function classifySegment(segment, depth) {
4816
4816
  if (entry && verb && entry[1].includes(verb)) return "install_dependency";
4817
4817
  return null;
4818
4818
  }
4819
- function claudePermissionRules(policy) {
4819
+ function claudePermissionRules(policy, agentDelivers = false) {
4820
4820
  const install2 = INSTALL_COMMANDS.flatMap(
4821
4821
  ([tool2, verbs]) => verbs.map((verb) => `Bash(${tool2} ${verb}:*)`)
4822
4822
  );
@@ -4826,7 +4826,7 @@ function claudePermissionRules(policy) {
4826
4826
  ask: decision === "ask" ? install2 : [],
4827
4827
  deny: [
4828
4828
  ...!rawCliShellAvailable(policy) ? ["Bash", "PowerShell", "Monitor"] : [],
4829
- "Bash(git push:*)",
4829
+ ...agentDelivers && !policy.readOnly ? [] : ["Bash(git push:*)"],
4830
4830
  ...policy.readOnly ? READ_ONLY_TOOLS : [],
4831
4831
  ...decision === "deny" ? install2 : []
4832
4832
  ]
@@ -4882,11 +4882,251 @@ function classifyWrite(worktree, target) {
4882
4882
  return ok2 ? "write_in_worktree" : "write_outside_repo";
4883
4883
  }
4884
4884
 
4885
+ // src/agents/run-sandbox-launcher.ts
4886
+ import { spawn } from "node:child_process";
4887
+ import { isIP } from "node:net";
4888
+ import { isAbsolute as isAbsolute2 } from "node:path";
4889
+ import { fileURLToPath } from "node:url";
4890
+ import { NetworkConfigSchema } from "@anthropic-ai/sandbox-runtime";
4891
+ var MAX_PENDING_NETWORK = 128;
4892
+ var NETWORK_DECISION_MS = 5 * 6e4;
4893
+ function canonicalHost(host) {
4894
+ if (!host || /[\s\x00-\x1f\x7f%\\/@?#*]/u.test(host)) return;
4895
+ const bare = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
4896
+ if (isIP(bare) === 6) return new URL(`http://[${bare}]`).hostname.slice(1, -1);
4897
+ if (bare.includes(":")) return;
4898
+ try {
4899
+ return new URL(`http://${bare}`).hostname.toLowerCase();
4900
+ } catch {
4901
+ return void 0;
4902
+ }
4903
+ }
4904
+ function matchesRunSandboxDomain(request, domains) {
4905
+ const host = canonicalHost(request.host);
4906
+ if (!host || !Number.isInteger(request.port) || request.port < 1 || request.port > 65535) return false;
4907
+ return domains.some((entry) => {
4908
+ if (entry === "*") return true;
4909
+ const parts = /^(\[[^\]]+\]|[^:]+?)(?::([1-9][0-9]{0,4}))?$/u.exec(entry);
4910
+ if (!parts || parts[2] !== void 0 && (Number(parts[2]) > 65535 || Number(parts[2]) !== request.port)) return false;
4911
+ const wildcard = parts[1].startsWith("*.");
4912
+ const pattern = canonicalHost(wildcard ? parts[1].slice(2) : parts[1]);
4913
+ if (!pattern) return false;
4914
+ if (!wildcard) return host === pattern;
4915
+ return !isIP(host) && pattern.includes(".") && host.endsWith(`.${pattern}`);
4916
+ });
4917
+ }
4918
+ function signalGroup(pid, signal) {
4919
+ if (pid === void 0) return;
4920
+ try {
4921
+ process.kill(-pid, signal);
4922
+ } catch (error) {
4923
+ if (error.code !== "ESRCH") throw error;
4924
+ }
4925
+ }
4926
+ function spawnRunSandbox(options) {
4927
+ if (process.platform !== "linux" && process.platform !== "darwin") {
4928
+ throw new Error("run sandbox launcher requires Linux or macOS process groups");
4929
+ }
4930
+ if (!options.command || options.command.includes("\0") || !isAbsolute2(options.cwd)) {
4931
+ throw new Error("run sandbox requires a command and absolute cwd");
4932
+ }
4933
+ if (options.authToken !== void 0 && (!options.authToken || Buffer.byteLength(options.authToken) > 1048576)) {
4934
+ throw new Error("invalid run sandbox auth pipe payload length");
4935
+ }
4936
+ const maximum = Object.freeze([...options.maxAllowDomains]);
4937
+ const modelApiDomains = Object.freeze([...options.modelApiDomains ?? []]);
4938
+ NetworkConfigSchema.parse({ allowedDomains: maximum.filter((entry) => entry !== "*"), deniedDomains: [] });
4939
+ NetworkConfigSchema.parse({ allowedDomains: [...modelApiDomains], deniedDomains: [] });
4940
+ const onNetwork = options.onNetwork;
4941
+ const abortSignal = options.signal;
4942
+ const launch = {
4943
+ type: "init",
4944
+ command: options.command,
4945
+ args: [...options.args],
4946
+ cwd: options.cwd,
4947
+ env: { ...options.env },
4948
+ policy: {
4949
+ allowWrite: [...options.policy.allowWrite],
4950
+ denyWrite: [...options.policy.denyWrite ?? []],
4951
+ denyRead: [...options.policy.denyRead ?? []]
4952
+ },
4953
+ maxAllowDomains: [...maximum],
4954
+ modelApiDomains: [...modelApiDomains],
4955
+ hasAuthToken: options.authToken !== void 0
4956
+ };
4957
+ const child = spawn(process.execPath, [fileURLToPath(new URL("./run-sandbox-worker.mjs", import.meta.url))], {
4958
+ // Runtime helpers use the trusted daemon's environment. CLI env arrives
4959
+ // over IPC and is passed explicitly by the worker, without inheritance.
4960
+ env: process.env,
4961
+ cwd: options.cwd,
4962
+ stdio: ["pipe", "pipe", "pipe", "pipe", "ipc"],
4963
+ detached: true
4964
+ });
4965
+ const lifetime = new AbortController();
4966
+ const pending = /* @__PURE__ */ new Map();
4967
+ let stderr = "";
4968
+ let stopped = false;
4969
+ let gone = false;
4970
+ let outcome;
4971
+ let hardStop;
4972
+ let settle;
4973
+ const exited = new Promise((resolve13) => {
4974
+ settle = resolve13;
4975
+ });
4976
+ function send(message) {
4977
+ if (!child.connected || gone) return;
4978
+ child.send(message, (error) => {
4979
+ if (error) stop(0);
4980
+ });
4981
+ }
4982
+ function stop(graceMs = 3e3) {
4983
+ if (gone) return;
4984
+ if (!stopped) {
4985
+ stopped = true;
4986
+ lifetime.abort();
4987
+ for (const request of pending.values()) {
4988
+ clearTimeout(request.timer);
4989
+ request.controller.abort();
4990
+ }
4991
+ pending.clear();
4992
+ send({ type: "stop", graceMs });
4993
+ }
4994
+ if (!hardStop) hardStop = setTimeout(() => {
4995
+ stderr += "\nrun sandbox worker did not stop; terminating its process group";
4996
+ try {
4997
+ signalGroup(child.pid, "SIGKILL");
4998
+ } catch (error) {
4999
+ stderr += `
5000
+ run sandbox group termination failed: ${String(error)}`;
5001
+ }
5002
+ }, Math.max(0, Math.min(graceMs, 3e4)) + 5e3);
5003
+ }
5004
+ child.stderr.on("data", (data) => {
5005
+ stderr = (stderr + data.toString()).slice(-16e3);
5006
+ });
5007
+ child.stdin.on("error", () => void 0);
5008
+ child.on("message", (message) => {
5009
+ if (!message || typeof message !== "object") return;
5010
+ const value = message;
5011
+ if (value.type === "finished") {
5012
+ if ((value.code === null || Number.isInteger(value.code)) && (value.signal === null || typeof value.signal === "string")) {
5013
+ outcome = { code: value.code, signal: value.signal };
5014
+ }
5015
+ return;
5016
+ }
5017
+ if (value.type !== "network" || !Number.isSafeInteger(value.id) || value.id < 1 || pending.has(value.id)) return;
5018
+ const id4 = value.id;
5019
+ const request = {
5020
+ host: typeof value.host === "string" ? value.host : "",
5021
+ port: typeof value.port === "number" ? value.port : 0
5022
+ };
5023
+ if (typeof value.port !== "number" || stopped || lifetime.signal.aborted || pending.size >= MAX_PENDING_NETWORK || !matchesRunSandboxDomain(request, maximum)) {
5024
+ send({ type: "decision", id: id4, allow: false });
5025
+ return;
5026
+ }
5027
+ const controller = new AbortController();
5028
+ const timer = setTimeout(() => {
5029
+ if (!pending.delete(id4)) return;
5030
+ controller.abort();
5031
+ send({ type: "decision", id: id4, allow: false });
5032
+ }, NETWORK_DECISION_MS);
5033
+ pending.set(id4, { controller, timer });
5034
+ void Promise.resolve().then(() => {
5035
+ if (stopped || lifetime.signal.aborted) return false;
5036
+ return onNetwork(request, controller.signal);
5037
+ }).then((allow) => {
5038
+ if (!pending.delete(id4)) return;
5039
+ clearTimeout(timer);
5040
+ send({ type: "decision", id: id4, allow: allow === true && !stopped && !lifetime.signal.aborted && !controller.signal.aborted });
5041
+ }, () => {
5042
+ if (pending.delete(id4)) {
5043
+ clearTimeout(timer);
5044
+ send({ type: "decision", id: id4, allow: false });
5045
+ }
5046
+ });
5047
+ });
5048
+ function onParentExit() {
5049
+ try {
5050
+ signalGroup(child.pid, "SIGKILL");
5051
+ } catch {
5052
+ }
5053
+ }
5054
+ process.once("exit", onParentExit);
5055
+ function finish(code, signal) {
5056
+ if (gone) return;
5057
+ gone = true;
5058
+ stopped = true;
5059
+ lifetime.abort();
5060
+ for (const request of pending.values()) {
5061
+ clearTimeout(request.timer);
5062
+ request.controller.abort();
5063
+ }
5064
+ pending.clear();
5065
+ clearTimeout(hardStop);
5066
+ abortSignal?.removeEventListener("abort", onAbort);
5067
+ process.removeListener("exit", onParentExit);
5068
+ try {
5069
+ signalGroup(child.pid, "SIGKILL");
5070
+ } catch (error) {
5071
+ stderr += `
5072
+ run sandbox group cleanup failed: ${String(error)}`;
5073
+ }
5074
+ settle(outcome ?? { code, signal });
5075
+ }
5076
+ child.once("error", (error) => {
5077
+ stderr += `
5078
+ ${error.message}`;
5079
+ stop(0);
5080
+ if (child.pid === void 0) finish(null, null);
5081
+ });
5082
+ child.once("exit", finish);
5083
+ child.once("disconnect", () => {
5084
+ if (!outcome) stop(0);
5085
+ });
5086
+ const authPipe = child.stdio[3];
5087
+ authPipe.on("error", () => stop(0));
5088
+ authPipe.end(options.authToken);
5089
+ function onAbort() {
5090
+ stop(0);
5091
+ }
5092
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
5093
+ child.kill = (signal = "SIGTERM") => {
5094
+ if (gone) return false;
5095
+ if (signal === 0) {
5096
+ try {
5097
+ process.kill(child.pid, 0);
5098
+ return true;
5099
+ } catch {
5100
+ return false;
5101
+ }
5102
+ }
5103
+ stop(signal === "SIGKILL" ? 0 : 3e3);
5104
+ return true;
5105
+ };
5106
+ Object.defineProperty(child, "killed", { get: () => stopped });
5107
+ if (abortSignal?.aborted) stop(0);
5108
+ else send(launch);
5109
+ return {
5110
+ process: child,
5111
+ exited,
5112
+ stderrTail: () => stderr.slice(-4e3).trim(),
5113
+ async kill(graceMs = 3e3) {
5114
+ stop(graceMs);
5115
+ await exited;
5116
+ },
5117
+ async revoke() {
5118
+ stop(0);
5119
+ await exited;
5120
+ }
5121
+ };
5122
+ }
5123
+
4885
5124
  // src/agents/tool-approval-policy.ts
4886
- function toolApprovalDecision(request, policy, worktree, apps = []) {
5125
+ function toolApprovalDecision(request, policy, worktree, apps = [], runHosts = []) {
4887
5126
  if (request.method === "sandbox/network" && policy) {
4888
5127
  const value = request.input;
4889
5128
  if (!value || typeof value.host !== "string" || /[*/:]/.test(value.host) || !networkDomainSchema.safeParse(value.host).success || !Number.isInteger(value.port) || Number(value.port) < 1 || Number(value.port) > 65535) return "deny";
5129
+ if (policy.permissions.network === "ask" && matchesRunSandboxDomain({ host: value.host, port: Number(value.port) }, [...policy.networkAllow, ...runHosts])) return "allow";
4890
5130
  return policy.permissions.network;
4891
5131
  }
4892
5132
  if (request.method.startsWith("cli/") && policy && worktree) {
@@ -5249,7 +5489,13 @@ var HttpServerLink = class {
5249
5489
  const response = z26.object({ decision: z26.enum(["pending", "allow", "deny"]) });
5250
5490
  const liveness = new AbortController();
5251
5491
  const gate = new ToolApprovalGate(assignment.runId, {
5252
- permission: (request) => toolApprovalDecision(request, assignment.policy, worktree, runApps(assignment.mcp)?.apps.map((app) => app.name)),
5492
+ permission: (request) => toolApprovalDecision(
5493
+ request,
5494
+ assignment.policy,
5495
+ worktree,
5496
+ runApps(assignment.mcp)?.apps.map((app) => app.name),
5497
+ assignment.github ? [new URL(assignment.github.endpoint).host] : []
5498
+ ),
5253
5499
  // Keep the callback pending at the tool boundary. The adapter's current
5254
5500
  // session and checkpoint remain intact; handback resolves this same call.
5255
5501
  isLive: async () => {
@@ -5755,7 +6001,7 @@ function buildChildEnv(options = {}) {
5755
6001
  }
5756
6002
 
5757
6003
  // src/agents/child.ts
5758
- import { spawn } from "node:child_process";
6004
+ import { spawn as spawn2 } from "node:child_process";
5759
6005
  import {
5760
6006
  mkdirSync as mkdirSync4,
5761
6007
  readFileSync as readFileSync3,
@@ -5920,7 +6166,7 @@ function endedSession(sessionId, exit) {
5920
6166
  };
5921
6167
  }
5922
6168
  var STDERR_TAIL = 4e3;
5923
- function signalGroup(pgid, signal) {
6169
+ function signalGroup2(pgid, signal) {
5924
6170
  if (pgid === void 0) return;
5925
6171
  try {
5926
6172
  process.kill(-pgid, signal);
@@ -5942,7 +6188,7 @@ async function reapStale(pidFile2, marker) {
5942
6188
  const [group, ...command] = line.trim().split(/\s+/);
5943
6189
  return Number(group) === pgid && command.join(" ").includes(marker);
5944
6190
  });
5945
- if (member) signalGroup(pgid, "SIGKILL");
6191
+ if (member) signalGroup2(pgid, "SIGKILL");
5946
6192
  }
5947
6193
  async function claimRunDir(paths) {
5948
6194
  ownDir(paths.runDir);
@@ -5953,7 +6199,7 @@ async function claimRunDir(paths) {
5953
6199
  async function spawnCli(sandbox, argv, options) {
5954
6200
  await reapStale(options.pidFile, options.marker);
5955
6201
  const wrapped = await sandbox.wrap(argv, options.policy);
5956
- const child = spawn("/bin/sh", ["-c", wrapped.command], {
6202
+ const child = spawn2("/bin/sh", ["-c", wrapped.command], {
5957
6203
  cwd: options.cwd,
5958
6204
  env: options.env,
5959
6205
  stdio: ["pipe", "pipe", "pipe"],
@@ -5980,7 +6226,7 @@ ${error.message}`;
5980
6226
  child.once("exit", (code, signal) => resolve13({ code, signal }));
5981
6227
  }
5982
6228
  ).finally(() => {
5983
- signalGroup(pgid, "SIGKILL");
6229
+ signalGroup2(pgid, "SIGKILL");
5984
6230
  rmSync3(options.pidFile, { force: true });
5985
6231
  wrapped.release();
5986
6232
  });
@@ -5990,7 +6236,7 @@ ${error.message}`;
5990
6236
  exited,
5991
6237
  stderrTail: () => wrapped.annotate(stderr.slice(-STDERR_TAIL)).trim(),
5992
6238
  async kill(graceMs = 3e3) {
5993
- signalGroup(pgid, "SIGTERM");
6239
+ signalGroup2(pgid, "SIGTERM");
5994
6240
  let timer;
5995
6241
  await Promise.race([
5996
6242
  exited,
@@ -5999,7 +6245,7 @@ ${error.message}`;
5999
6245
  })
6000
6246
  ]);
6001
6247
  clearTimeout(timer);
6002
- signalGroup(pgid, "SIGKILL");
6248
+ signalGroup2(pgid, "SIGKILL");
6003
6249
  await exited;
6004
6250
  }
6005
6251
  };
@@ -6154,7 +6400,7 @@ import {
6154
6400
  rmSync as rmSync4
6155
6401
  } from "node:fs";
6156
6402
  import { lstat, readdir } from "node:fs/promises";
6157
- import { dirname as dirname6, isAbsolute as isAbsolute2, join as join7, relative as relative2, resolve as resolve4 } from "node:path";
6403
+ import { dirname as dirname6, isAbsolute as isAbsolute3, join as join7, relative as relative2, resolve as resolve4 } from "node:path";
6158
6404
  import { execa as execa2 } from "execa";
6159
6405
  import { simpleGit } from "simple-git";
6160
6406
  var DISK_USAGE_TTL_MS = 5 * 6e4;
@@ -6172,7 +6418,7 @@ function runBranch(runId) {
6172
6418
  }
6173
6419
  function isInside(parent, child) {
6174
6420
  const rel = relative2(parent, child);
6175
- return rel === "" || !rel.startsWith("..") && !isAbsolute2(rel);
6421
+ return rel === "" || !rel.startsWith("..") && !isAbsolute3(rel);
6176
6422
  }
6177
6423
  var configWriters = /* @__PURE__ */ new Map();
6178
6424
  function withRepoConfig(repoPath, work) {
@@ -6310,7 +6556,7 @@ var WorktreeManager = class {
6310
6556
  const worktreeReal = realpathSync4(worktree);
6311
6557
  for (const file of recipe.copyFiles) {
6312
6558
  const rel = relative2(repoPath, resolve4(repoPath, file));
6313
- if (rel === "" || rel.startsWith("..") || isAbsolute2(rel)) {
6559
+ if (rel === "" || rel.startsWith("..") || isAbsolute3(rel)) {
6314
6560
  throw new Error(`setup recipe copies a file outside the repo: ${file}`);
6315
6561
  }
6316
6562
  const from = join7(repoPath, rel);
@@ -7231,6 +7477,14 @@ var RunContext = class {
7231
7477
  tempEnv() {
7232
7478
  return { TMPDIR: this.tmpDir, TMPPREFIX: join11(this.tmpDir, "zsh"), ...withGitConfig(this.githubEnv, RUN_GIT_CONFIG), ...this.options.policyHash ? { MIKKA_POLICY_HASH: this.options.policyHash } : {} };
7233
7479
  }
7480
+ /**
7481
+ * The agent delivers this run itself: it commits, pushes to the issue
7482
+ * branch and opens the pull request with run-scoped GitHub credentials. The
7483
+ * server sends GitHub delivery only for a run whose push is allowed.
7484
+ */
7485
+ get agentDelivers() {
7486
+ return Object.keys(this.githubEnv).length > 0;
7487
+ }
7234
7488
  /** A hand-in from an earlier session would end this one at once. */
7235
7489
  clearHandoff() {
7236
7490
  rmSync8(this.handoffFile, { force: true });
@@ -7811,245 +8065,6 @@ import {
7811
8065
  createSdkMcpServer as createSdkMcpServer2
7812
8066
  } from "@anthropic-ai/claude-agent-sdk";
7813
8067
 
7814
- // src/agents/run-sandbox-launcher.ts
7815
- import { spawn as spawn2 } from "node:child_process";
7816
- import { isIP } from "node:net";
7817
- import { isAbsolute as isAbsolute3 } from "node:path";
7818
- import { fileURLToPath } from "node:url";
7819
- import { NetworkConfigSchema } from "@anthropic-ai/sandbox-runtime";
7820
- var MAX_PENDING_NETWORK = 128;
7821
- var NETWORK_DECISION_MS = 5 * 6e4;
7822
- function canonicalHost(host) {
7823
- if (!host || /[\s\x00-\x1f\x7f%\\/@?#*]/u.test(host)) return;
7824
- const bare = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
7825
- if (isIP(bare) === 6) return new URL(`http://[${bare}]`).hostname.slice(1, -1);
7826
- if (bare.includes(":")) return;
7827
- try {
7828
- return new URL(`http://${bare}`).hostname.toLowerCase();
7829
- } catch {
7830
- return void 0;
7831
- }
7832
- }
7833
- function matchesRunSandboxDomain(request, domains) {
7834
- const host = canonicalHost(request.host);
7835
- if (!host || !Number.isInteger(request.port) || request.port < 1 || request.port > 65535) return false;
7836
- return domains.some((entry) => {
7837
- if (entry === "*") return true;
7838
- const parts = /^(\[[^\]]+\]|[^:]+?)(?::([1-9][0-9]{0,4}))?$/u.exec(entry);
7839
- if (!parts || parts[2] !== void 0 && (Number(parts[2]) > 65535 || Number(parts[2]) !== request.port)) return false;
7840
- const wildcard = parts[1].startsWith("*.");
7841
- const pattern = canonicalHost(wildcard ? parts[1].slice(2) : parts[1]);
7842
- if (!pattern) return false;
7843
- if (!wildcard) return host === pattern;
7844
- return !isIP(host) && pattern.includes(".") && host.endsWith(`.${pattern}`);
7845
- });
7846
- }
7847
- function signalGroup2(pid, signal) {
7848
- if (pid === void 0) return;
7849
- try {
7850
- process.kill(-pid, signal);
7851
- } catch (error) {
7852
- if (error.code !== "ESRCH") throw error;
7853
- }
7854
- }
7855
- function spawnRunSandbox(options) {
7856
- if (process.platform !== "linux" && process.platform !== "darwin") {
7857
- throw new Error("run sandbox launcher requires Linux or macOS process groups");
7858
- }
7859
- if (!options.command || options.command.includes("\0") || !isAbsolute3(options.cwd)) {
7860
- throw new Error("run sandbox requires a command and absolute cwd");
7861
- }
7862
- if (options.authToken !== void 0 && (!options.authToken || Buffer.byteLength(options.authToken) > 1048576)) {
7863
- throw new Error("invalid run sandbox auth pipe payload length");
7864
- }
7865
- const maximum = Object.freeze([...options.maxAllowDomains]);
7866
- const modelApiDomains = Object.freeze([...options.modelApiDomains ?? []]);
7867
- NetworkConfigSchema.parse({ allowedDomains: maximum.filter((entry) => entry !== "*"), deniedDomains: [] });
7868
- NetworkConfigSchema.parse({ allowedDomains: [...modelApiDomains], deniedDomains: [] });
7869
- const onNetwork = options.onNetwork;
7870
- const abortSignal = options.signal;
7871
- const launch = {
7872
- type: "init",
7873
- command: options.command,
7874
- args: [...options.args],
7875
- cwd: options.cwd,
7876
- env: { ...options.env },
7877
- policy: {
7878
- allowWrite: [...options.policy.allowWrite],
7879
- denyWrite: [...options.policy.denyWrite ?? []],
7880
- denyRead: [...options.policy.denyRead ?? []]
7881
- },
7882
- maxAllowDomains: [...maximum],
7883
- modelApiDomains: [...modelApiDomains],
7884
- hasAuthToken: options.authToken !== void 0
7885
- };
7886
- const child = spawn2(process.execPath, [fileURLToPath(new URL("./run-sandbox-worker.mjs", import.meta.url))], {
7887
- // Runtime helpers use the trusted daemon's environment. CLI env arrives
7888
- // over IPC and is passed explicitly by the worker, without inheritance.
7889
- env: process.env,
7890
- cwd: options.cwd,
7891
- stdio: ["pipe", "pipe", "pipe", "pipe", "ipc"],
7892
- detached: true
7893
- });
7894
- const lifetime = new AbortController();
7895
- const pending = /* @__PURE__ */ new Map();
7896
- let stderr = "";
7897
- let stopped = false;
7898
- let gone = false;
7899
- let outcome;
7900
- let hardStop;
7901
- let settle;
7902
- const exited = new Promise((resolve13) => {
7903
- settle = resolve13;
7904
- });
7905
- function send(message) {
7906
- if (!child.connected || gone) return;
7907
- child.send(message, (error) => {
7908
- if (error) stop(0);
7909
- });
7910
- }
7911
- function stop(graceMs = 3e3) {
7912
- if (gone) return;
7913
- if (!stopped) {
7914
- stopped = true;
7915
- lifetime.abort();
7916
- for (const request of pending.values()) {
7917
- clearTimeout(request.timer);
7918
- request.controller.abort();
7919
- }
7920
- pending.clear();
7921
- send({ type: "stop", graceMs });
7922
- }
7923
- if (!hardStop) hardStop = setTimeout(() => {
7924
- stderr += "\nrun sandbox worker did not stop; terminating its process group";
7925
- try {
7926
- signalGroup2(child.pid, "SIGKILL");
7927
- } catch (error) {
7928
- stderr += `
7929
- run sandbox group termination failed: ${String(error)}`;
7930
- }
7931
- }, Math.max(0, Math.min(graceMs, 3e4)) + 5e3);
7932
- }
7933
- child.stderr.on("data", (data) => {
7934
- stderr = (stderr + data.toString()).slice(-16e3);
7935
- });
7936
- child.stdin.on("error", () => void 0);
7937
- child.on("message", (message) => {
7938
- if (!message || typeof message !== "object") return;
7939
- const value = message;
7940
- if (value.type === "finished") {
7941
- if ((value.code === null || Number.isInteger(value.code)) && (value.signal === null || typeof value.signal === "string")) {
7942
- outcome = { code: value.code, signal: value.signal };
7943
- }
7944
- return;
7945
- }
7946
- if (value.type !== "network" || !Number.isSafeInteger(value.id) || value.id < 1 || pending.has(value.id)) return;
7947
- const id4 = value.id;
7948
- const request = {
7949
- host: typeof value.host === "string" ? value.host : "",
7950
- port: typeof value.port === "number" ? value.port : 0
7951
- };
7952
- if (typeof value.port !== "number" || stopped || lifetime.signal.aborted || pending.size >= MAX_PENDING_NETWORK || !matchesRunSandboxDomain(request, maximum)) {
7953
- send({ type: "decision", id: id4, allow: false });
7954
- return;
7955
- }
7956
- const controller = new AbortController();
7957
- const timer = setTimeout(() => {
7958
- if (!pending.delete(id4)) return;
7959
- controller.abort();
7960
- send({ type: "decision", id: id4, allow: false });
7961
- }, NETWORK_DECISION_MS);
7962
- pending.set(id4, { controller, timer });
7963
- void Promise.resolve().then(() => {
7964
- if (stopped || lifetime.signal.aborted) return false;
7965
- return onNetwork(request, controller.signal);
7966
- }).then((allow) => {
7967
- if (!pending.delete(id4)) return;
7968
- clearTimeout(timer);
7969
- send({ type: "decision", id: id4, allow: allow === true && !stopped && !lifetime.signal.aborted && !controller.signal.aborted });
7970
- }, () => {
7971
- if (pending.delete(id4)) {
7972
- clearTimeout(timer);
7973
- send({ type: "decision", id: id4, allow: false });
7974
- }
7975
- });
7976
- });
7977
- function onParentExit() {
7978
- try {
7979
- signalGroup2(child.pid, "SIGKILL");
7980
- } catch {
7981
- }
7982
- }
7983
- process.once("exit", onParentExit);
7984
- function finish(code, signal) {
7985
- if (gone) return;
7986
- gone = true;
7987
- stopped = true;
7988
- lifetime.abort();
7989
- for (const request of pending.values()) {
7990
- clearTimeout(request.timer);
7991
- request.controller.abort();
7992
- }
7993
- pending.clear();
7994
- clearTimeout(hardStop);
7995
- abortSignal?.removeEventListener("abort", onAbort);
7996
- process.removeListener("exit", onParentExit);
7997
- try {
7998
- signalGroup2(child.pid, "SIGKILL");
7999
- } catch (error) {
8000
- stderr += `
8001
- run sandbox group cleanup failed: ${String(error)}`;
8002
- }
8003
- settle(outcome ?? { code, signal });
8004
- }
8005
- child.once("error", (error) => {
8006
- stderr += `
8007
- ${error.message}`;
8008
- stop(0);
8009
- if (child.pid === void 0) finish(null, null);
8010
- });
8011
- child.once("exit", finish);
8012
- child.once("disconnect", () => {
8013
- if (!outcome) stop(0);
8014
- });
8015
- const authPipe = child.stdio[3];
8016
- authPipe.on("error", () => stop(0));
8017
- authPipe.end(options.authToken);
8018
- function onAbort() {
8019
- stop(0);
8020
- }
8021
- abortSignal?.addEventListener("abort", onAbort, { once: true });
8022
- child.kill = (signal = "SIGTERM") => {
8023
- if (gone) return false;
8024
- if (signal === 0) {
8025
- try {
8026
- process.kill(child.pid, 0);
8027
- return true;
8028
- } catch {
8029
- return false;
8030
- }
8031
- }
8032
- stop(signal === "SIGKILL" ? 0 : 3e3);
8033
- return true;
8034
- };
8035
- Object.defineProperty(child, "killed", { get: () => stopped });
8036
- if (abortSignal?.aborted) stop(0);
8037
- else send(launch);
8038
- return {
8039
- process: child,
8040
- exited,
8041
- stderrTail: () => stderr.slice(-4e3).trim(),
8042
- async kill(graceMs = 3e3) {
8043
- stop(graceMs);
8044
- await exited;
8045
- },
8046
- async revoke() {
8047
- stop(0);
8048
- await exited;
8049
- }
8050
- };
8051
- }
8052
-
8053
8068
  // src/agents/run-network.ts
8054
8069
  function runNetworkApproval(runId, harness, gate, maximum, localComputerEndpoint, appHost) {
8055
8070
  if (gate.runId !== runId) throw new Error("Run approval scope mismatch");
@@ -8253,10 +8268,10 @@ function claudeSandboxSettings(policy) {
8253
8268
  }
8254
8269
  };
8255
8270
  }
8256
- function claudeSettings(policy, workspace) {
8271
+ function claudeSettings(policy, workspace, agentDelivers = false) {
8257
8272
  return {
8258
8273
  sandbox: claudeSandboxSettings(policy),
8259
- permissions: claudePermissionRules(runPolicy(workspace))
8274
+ permissions: claudePermissionRules(runPolicy(workspace), agentDelivers)
8260
8275
  };
8261
8276
  }
8262
8277
  function claudeChunks(message) {
@@ -8596,7 +8611,7 @@ var ClaudeAdapter = class {
8596
8611
  },
8597
8612
  settingSources: [],
8598
8613
  settings: {
8599
- ...claudeSettings(ctx.policy(), options.policy),
8614
+ ...claudeSettings(ctx.policy(), options.policy, ctx.agentDelivers),
8600
8615
  // The official runtime launcher encloses the entire CLI. Nesting
8601
8616
  // the CLI's Bash-only sandbox is unsupported on macOS.
8602
8617
  ...outerLocal ? { sandbox: { enabled: false, allowUnsandboxedCommands: true, autoAllowBashIfSandboxed: false } } : {},
@@ -9451,7 +9466,7 @@ var CodexAdapter = class {
9451
9466
  import { Readable, Writable } from "node:stream";
9452
9467
  import * as acp from "@agentclientprotocol/sdk";
9453
9468
  import { join as join16 } from "node:path";
9454
- function acpPermissionResponse(params, policy) {
9469
+ function acpPermissionResponse(params, policy, agentDelivers = false) {
9455
9470
  const pick = (kinds) => {
9456
9471
  for (const kind of kinds) {
9457
9472
  const option = params.options.find((o) => o.kind === kind);
@@ -9463,7 +9478,7 @@ function acpPermissionResponse(params, policy) {
9463
9478
  const raw = params.toolCall.rawInput;
9464
9479
  const command = Array.isArray(raw?.command) ? raw.command.join(" ") : typeof raw?.command === "string" ? raw.command : params.toolCall.title ?? "";
9465
9480
  const action = classifyCommand(command);
9466
- const refused = action === "git_push" || action !== null && decide(action, { policy: runPolicy(policy) }) !== "allow";
9481
+ const refused = action === "git_push" && !agentDelivers || action !== null && decide(action, { policy: runPolicy(policy) }) !== "allow";
9467
9482
  if (refused)
9468
9483
  return { response: pick(["reject_once", "reject_always"]), refused };
9469
9484
  const allowed = pick(["allow_always", "allow_once"]);
@@ -9646,7 +9661,8 @@ var CursorAdapter = class {
9646
9661
  async requestPermission(params) {
9647
9662
  const { response, refused } = acpPermissionResponse(
9648
9663
  params,
9649
- options.policy
9664
+ options.policy,
9665
+ ctx.agentDelivers
9650
9666
  );
9651
9667
  if (refused)
9652
9668
  log.info(