@aspectlylabs/mikka 0.0.85 → 0.0.87

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/cli.js CHANGED
@@ -878,10 +878,10 @@ var require_truncate = __commonJS({
878
878
  "../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/truncate.js"(exports, module) {
879
879
  "use strict";
880
880
  var parse2 = require_parse();
881
- var constants3 = require_constants();
881
+ var constants4 = require_constants();
882
882
  var SemVer = require_semver();
883
883
  var truncate = (version, truncation, options) => {
884
- if (!constants3.RELEASE_TYPES.includes(truncation)) {
884
+ if (!constants4.RELEASE_TYPES.includes(truncation)) {
885
885
  return null;
886
886
  }
887
887
  const clonedVersion = cloneInputVersion(version, options);
@@ -1930,7 +1930,7 @@ var require_semver2 = __commonJS({
1930
1930
  "../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/index.js"(exports, module) {
1931
1931
  "use strict";
1932
1932
  var internalRe = require_re();
1933
- var constants3 = require_constants();
1933
+ var constants4 = require_constants();
1934
1934
  var SemVer = require_semver();
1935
1935
  var identifiers = require_identifiers();
1936
1936
  var parse2 = require_parse();
@@ -2014,8 +2014,8 @@ var require_semver2 = __commonJS({
2014
2014
  re: internalRe.re,
2015
2015
  src: internalRe.src,
2016
2016
  tokens: internalRe.t,
2017
- SEMVER_SPEC_VERSION: constants3.SEMVER_SPEC_VERSION,
2018
- RELEASE_TYPES: constants3.RELEASE_TYPES,
2017
+ SEMVER_SPEC_VERSION: constants4.SEMVER_SPEC_VERSION,
2018
+ RELEASE_TYPES: constants4.RELEASE_TYPES,
2019
2019
  compareIdentifiers: identifiers.compareIdentifiers,
2020
2020
  rcompareIdentifiers: identifiers.rcompareIdentifiers
2021
2021
  };
@@ -2804,6 +2804,12 @@ var harnessInfoSchema = z6.object({
2804
2804
  computerBroker: z6.literal(true).optional(),
2805
2805
  /** Run-scoped CLI approvals and policy-version checks are supported. */
2806
2806
  toolApproval: z6.literal(true).optional(),
2807
+ /**
2808
+ * The CLI exists but `installed` stays false because the daemon cannot run it
2809
+ * in a sandbox. `missing` names the absent packages; empty means the machine
2810
+ * itself refused isolation.
2811
+ */
2812
+ sandboxBlocked: z6.object({ missing: z6.array(z6.string().trim().min(1).max(64)).max(8) }).optional(),
2807
2813
  /** Version string reported by the CLI, null when not installed. */
2808
2814
  version: z6.string().nullable()
2809
2815
  });
@@ -4431,7 +4437,7 @@ import { join as join4 } from "node:path";
4431
4437
  // package.json
4432
4438
  var package_default = {
4433
4439
  name: "@mikka/daemon",
4434
- version: "0.0.85",
4440
+ version: "0.0.87",
4435
4441
  description: "Local Mikka daemon: runs agents through the user's own CLIs",
4436
4442
  type: "module",
4437
4443
  private: true,
@@ -4490,7 +4496,7 @@ var package_default = {
4490
4496
  engines: {
4491
4497
  node: ">=24"
4492
4498
  },
4493
- gitHead: "796745cfecdfca0ae6cf51d3844457654c4e8a36"
4499
+ gitHead: "f2ef942a44623501b27027f8926e0666a640bcfe"
4494
4500
  };
4495
4501
 
4496
4502
  // src/paths.ts
@@ -7072,6 +7078,7 @@ var RunContext = class {
7072
7078
  ],
7073
7079
  allowDomains: [
7074
7080
  ...HARNESS_DOMAINS[this.options.harness],
7081
+ ...this.options.harnessDomains ?? [],
7075
7082
  ...runAppsHost(this.options.apps) ? [runAppsHost(this.options.apps)] : [],
7076
7083
  ...runDomains(
7077
7084
  runPolicy(this.options.policy),
@@ -7595,15 +7602,15 @@ sandbox_mode = "read-only"
7595
7602
  // src/main.ts
7596
7603
  import { randomBytes as randomBytes3, randomUUID as randomUUID10 } from "node:crypto";
7597
7604
  import {
7598
- chmodSync as chmodSync4,
7599
- existsSync as existsSync7,
7600
- mkdirSync as mkdirSync16,
7601
- readFileSync as readFileSync11,
7602
- rmSync as rmSync17,
7605
+ chmodSync as chmodSync5,
7606
+ existsSync as existsSync8,
7607
+ mkdirSync as mkdirSync17,
7608
+ readFileSync as readFileSync12,
7609
+ rmSync as rmSync18,
7603
7610
  realpathSync as realpathSync13
7604
7611
  } from "node:fs";
7605
7612
  import { arch, hostname, platform as platform2 } from "node:os";
7606
- import { join as join28 } from "node:path";
7613
+ import { join as join32 } from "node:path";
7607
7614
  import { fileURLToPath as fileURLToPath3 } from "node:url";
7608
7615
 
7609
7616
  // src/adapters/claude.ts
@@ -7629,7 +7636,52 @@ function computerSandboxPolicy(policy, spec) {
7629
7636
  import { randomUUID as randomUUID3 } from "node:crypto";
7630
7637
  import { realpathSync as realpathSync9 } from "node:fs";
7631
7638
  import { homedir as homedir6 } from "node:os";
7632
- import { join as join14 } from "node:path";
7639
+
7640
+ // src/adapters/claude-gateway.ts
7641
+ import { readFileSync as readFileSync9 } from "node:fs";
7642
+ import { join as join13 } from "node:path";
7643
+ var GATEWAY_ENV = [
7644
+ "ANTHROPIC_BASE_URL",
7645
+ "ANTHROPIC_AUTH_TOKEN",
7646
+ "ANTHROPIC_API_KEY",
7647
+ "ANTHROPIC_MODEL",
7648
+ "ANTHROPIC_DEFAULT_FABLE_MODEL",
7649
+ "ANTHROPIC_DEFAULT_OPUS_MODEL",
7650
+ "ANTHROPIC_DEFAULT_SONNET_MODEL",
7651
+ "ANTHROPIC_DEFAULT_HAIKU_MODEL",
7652
+ "CLAUDE_CODE_SUBAGENT_MODEL"
7653
+ ];
7654
+ function readClaudeGateway(configDir) {
7655
+ let settings;
7656
+ try {
7657
+ settings = JSON.parse(readFileSync9(join13(configDir, "settings.json"), "utf8"));
7658
+ } catch {
7659
+ return null;
7660
+ }
7661
+ const declared = settings?.env;
7662
+ if (!declared || typeof declared !== "object") return null;
7663
+ const values = declared;
7664
+ const domain = gatewayDomain(values.ANTHROPIC_BASE_URL);
7665
+ if (!domain) return null;
7666
+ const env = {};
7667
+ for (const name of GATEWAY_ENV) {
7668
+ const value = values[name];
7669
+ if (typeof value === "string" && value) env[name] = value;
7670
+ }
7671
+ return { env, domain };
7672
+ }
7673
+ function gatewayDomain(baseUrl) {
7674
+ if (typeof baseUrl !== "string") return null;
7675
+ try {
7676
+ const url = new URL(baseUrl);
7677
+ return url.protocol === "https:" || url.protocol === "http:" ? url.hostname : null;
7678
+ } catch {
7679
+ return null;
7680
+ }
7681
+ }
7682
+
7683
+ // src/adapters/claude.ts
7684
+ import { join as join15 } from "node:path";
7633
7685
  import {
7634
7686
  query as query2,
7635
7687
  createSdkMcpServer as createSdkMcpServer2
@@ -7890,15 +7942,15 @@ function runNetworkApproval(runId, harness, gate, maximum, localComputerEndpoint
7890
7942
  // src/subscription-native-broker.ts
7891
7943
  import { chmodSync, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync4, rmSync as rmSync10 } from "node:fs";
7892
7944
  import { createServer } from "node:http";
7893
- import { join as join13 } from "node:path";
7945
+ import { join as join14 } from "node:path";
7894
7946
  import { once } from "node:events";
7895
7947
  var NATIVE_AUTH_PLACEHOLDER = "mikka-native-broker-placeholder";
7896
7948
  async function createNativeSubscriptionBroker(host) {
7897
7949
  await host.check();
7898
7950
  mkdirSync10(host.root, { recursive: true, mode: 448 });
7899
- const dir = mkdtempSync4(join13(host.root, "native-broker-"));
7951
+ const dir = mkdtempSync4(join14(host.root, "native-broker-"));
7900
7952
  chmodSync(dir, 448);
7901
- const socket = join13(dir, "broker.sock");
7953
+ const socket = join14(dir, "broker.sock");
7902
7954
  const pending = /* @__PURE__ */ new Set();
7903
7955
  let halted = false;
7904
7956
  const server = createServer(async (request, response) => {
@@ -8252,10 +8304,12 @@ var ClaudeAdapter = class {
8252
8304
  throw new Error(SUBSCRIPTION_ERROR);
8253
8305
  }
8254
8306
  }
8255
- const nativeHome = externalAuth ? join14(runDir, "native-home") : void 0;
8256
- const configDir = nativeHome ? join14(nativeHome, ".claude") : this.options.configDir ?? join14(homedir6(), ".claude");
8307
+ const nativeHome = externalAuth ? join15(runDir, "native-home") : void 0;
8308
+ const configDir = nativeHome ? join15(nativeHome, ".claude") : this.options.configDir ?? join15(homedir6(), ".claude");
8257
8309
  if (nativeHome) ownDir(configDir);
8310
+ const gateway = nativeHome || externalAuth ? null : readClaudeGateway(configDir);
8258
8311
  const ctx = new RunContext({
8312
+ harnessDomains: gateway ? [gateway.domain] : void 0,
8259
8313
  github: options.github,
8260
8314
  apps: options.apps,
8261
8315
  githubRuntime: options.githubRuntime,
@@ -8274,8 +8328,8 @@ var ClaudeAdapter = class {
8274
8328
  privateDir: paths.privateDir,
8275
8329
  // Its session store, so `resume` works; keyed by the cwd it sees.
8276
8330
  extraWrite: [
8277
- join14(configDir, "projects", claudeProjectDirName(options.cwd)),
8278
- join14(
8331
+ join15(configDir, "projects", claudeProjectDirName(options.cwd)),
8332
+ join15(
8279
8333
  configDir,
8280
8334
  "projects",
8281
8335
  claudeProjectDirName(realpathSync9(options.cwd))
@@ -8334,6 +8388,7 @@ var ClaudeAdapter = class {
8334
8388
  ...options.env,
8335
8389
  ...ctx.tempEnv(),
8336
8390
  ...CLAUDE_RUN_ENV,
8391
+ ...gateway?.env,
8337
8392
  CLAUDE_CODE_TMPDIR: ctx.tmpDir,
8338
8393
  ...nativeHome ? {
8339
8394
  HOME: nativeHome,
@@ -8520,7 +8575,26 @@ var ClaudeAdapter = class {
8520
8575
  };
8521
8576
 
8522
8577
  // src/adapters/codex.ts
8523
- import { isAbsolute as isAbsolute4, join as join15, relative as relative3 } from "node:path";
8578
+ import { isAbsolute as isAbsolute4, join as join16, relative as relative3 } from "node:path";
8579
+
8580
+ // src/adapters/codex-version.ts
8581
+ var VERSION = /(?:^|[ /])(\d+)\.(\d+)\.(\d+)(?:$|[ ();])/;
8582
+ var MINIMUM_APPROVAL_VERSION = [0, 162, 0];
8583
+ var AUDITED_DYNAMIC_TOOLS = "0.163.0";
8584
+ function codexSupportsRunApproval(version) {
8585
+ const match = VERSION.exec(version ?? "");
8586
+ if (!match) return false;
8587
+ for (const [index, minimum] of MINIMUM_APPROVAL_VERSION.entries()) {
8588
+ const part = Number(match[index + 1]);
8589
+ if (part !== minimum) return part > minimum;
8590
+ }
8591
+ return true;
8592
+ }
8593
+ function codexHasAuditedDynamicTools(version) {
8594
+ return VERSION.exec(version ?? "")?.slice(1).join(".") === AUDITED_DYNAMIC_TOOLS;
8595
+ }
8596
+
8597
+ // src/adapters/codex.ts
8524
8598
  var SUBSCRIPTION_AUTH_ERROR = "Codex subscription authentication unavailable";
8525
8599
  var SUBSCRIPTION_EXECUTION_ERROR2 = "Codex subscription execution failed";
8526
8600
  function subscriptionTokens(value, accountId) {
@@ -8689,8 +8763,8 @@ var CodexAdapter = class {
8689
8763
  options.continueSession.runId
8690
8764
  ).runDir;
8691
8765
  carryHistory(
8692
- join15(earlier2, "codex-home"),
8693
- join15(runDir, "codex-home"),
8766
+ join16(earlier2, "codex-home"),
8767
+ join16(runDir, "codex-home"),
8694
8768
  CODEX_HISTORY
8695
8769
  );
8696
8770
  } catch (error) {
@@ -8702,7 +8776,7 @@ var CodexAdapter = class {
8702
8776
  }
8703
8777
  }
8704
8778
  const codexHome = prepareCodexHome(
8705
- join15(runDir, "codex-home"),
8779
+ join16(runDir, "codex-home"),
8706
8780
  log,
8707
8781
  this.options.userHome,
8708
8782
  !externalAuth
@@ -9041,7 +9115,8 @@ var CodexAdapter = class {
9041
9115
  }),
9042
9116
  "initialize"
9043
9117
  );
9044
- if ((options.mailbox || !rawCliShellAvailable(runPolicy(options.policy))) && !/(?:^|[ /])0\.163\.0(?:$|[ ();])/.test(initialized.userAgent ?? "")) throw new Error("Run permissions require the audited Codex0.163.0 tool interface");
9118
+ if (options.mailbox && !codexHasAuditedDynamicTools(initialized.userAgent)) throw new Error("Run permissions require the audited Codex0.163.0 tool interface");
9119
+ if (!codexSupportsRunApproval(initialized.userAgent)) throw new Error("This Codex version is too old for Mikka; update Codex");
9045
9120
  await rpc.sendNotification("initialized");
9046
9121
  if (externalAuth) {
9047
9122
  try {
@@ -9154,7 +9229,7 @@ var CodexAdapter = class {
9154
9229
  // src/adapters/cursor.ts
9155
9230
  import { Readable, Writable } from "node:stream";
9156
9231
  import * as acp from "@agentclientprotocol/sdk";
9157
- import { join as join16 } from "node:path";
9232
+ import { join as join17 } from "node:path";
9158
9233
  function acpPermissionResponse(params, policy) {
9159
9234
  const pick = (kinds) => {
9160
9235
  for (const kind of kinds) {
@@ -9289,15 +9364,15 @@ var CursorAdapter = class {
9289
9364
  options.continueSession.runId
9290
9365
  ).runDir;
9291
9366
  carryHistory(
9292
- join16(earlier2, "cursor-home"),
9293
- join16(paths.runDir, "cursor-home"),
9367
+ join17(earlier2, "cursor-home"),
9368
+ join17(paths.runDir, "cursor-home"),
9294
9369
  CURSOR_HISTORY
9295
9370
  );
9296
9371
  } catch (error) {
9297
9372
  log.info({ err: error }, "could not copy the earlier cursor chats");
9298
9373
  }
9299
9374
  }
9300
- const configDir = prepareCursorHome(join16(paths.runDir, "cursor-home"));
9375
+ const configDir = prepareCursorHome(join17(paths.runDir, "cursor-home"));
9301
9376
  const ctx = new RunContext({
9302
9377
  github: options.github,
9303
9378
  childPath: options.env.PATH,
@@ -9529,8 +9604,8 @@ ${firstMessage}` : firstMessage
9529
9604
 
9530
9605
  // src/adapters/mock.ts
9531
9606
  import { randomUUID as randomUUID4 } from "node:crypto";
9532
- import { mkdirSync as mkdirSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
9533
- import { dirname as dirname9, join as join17, resolve as resolve8 } from "node:path";
9607
+ import { mkdirSync as mkdirSync11, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "node:fs";
9608
+ import { dirname as dirname9, join as join18, resolve as resolve8 } from "node:path";
9534
9609
  import { setTimeout as sleep2 } from "node:timers/promises";
9535
9610
  import { execa as execa5 } from "execa";
9536
9611
 
@@ -9586,7 +9661,7 @@ var MockAdapter = class {
9586
9661
  fixturesDir;
9587
9662
  speed;
9588
9663
  stateFile(sessionId) {
9589
- return join17(
9664
+ return join18(
9590
9665
  this.options.stateDir,
9591
9666
  `mock-${assertSafeId("session id", sessionId)}.json`
9592
9667
  );
@@ -9630,7 +9705,7 @@ var MockAdapter = class {
9630
9705
  open(sessionId, state, options) {
9631
9706
  const name = assertSafeId("fixture", state.fixture.replace(/\.jsonl$/, ""));
9632
9707
  const lines = parseFixture(
9633
- readFileSync9(join17(this.fixturesDir, `${name}.jsonl`), "utf8"),
9708
+ readFileSync10(join18(this.fixturesDir, `${name}.jsonl`), "utf8"),
9634
9709
  name
9635
9710
  );
9636
9711
  const abort = new AbortController();
@@ -9744,18 +9819,18 @@ var MockAdapter = class {
9744
9819
 
9745
9820
  // src/agents/verify.ts
9746
9821
  import { mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync5, realpathSync as realpathSync10, rmSync as rmSync11 } from "node:fs";
9747
- import { join as join18 } from "node:path";
9822
+ import { join as join19 } from "node:path";
9748
9823
  var OUTPUT_TAIL = 4e3;
9749
9824
  async function runVerify(sandbox, options) {
9750
9825
  mkdirSync12(options.tmpRoot, { recursive: true, mode: 448 });
9751
- const tmpDir = mkdtempSync5(join18(realpathSync10(options.tmpRoot), "verify-"));
9826
+ const tmpDir = mkdtempSync5(join19(realpathSync10(options.tmpRoot), "verify-"));
9752
9827
  const child = await spawnCli(sandbox, ["/bin/sh", "-c", options.command], {
9753
9828
  cwd: options.worktree,
9754
9829
  env: { ...options.env, TMPDIR: tmpDir },
9755
9830
  policy: {
9756
9831
  allowWrite: options.readOnly ? [tmpDir] : [options.worktree, tmpDir],
9757
9832
  denyWrite: [
9758
- join18(options.worktree, ".git"),
9833
+ join19(options.worktree, ".git"),
9759
9834
  ...options.readOnly ? [options.worktree] : []
9760
9835
  ],
9761
9836
  denyRead: [
@@ -9926,7 +10001,7 @@ import { SandboxManager as SandboxManager2 } from "@anthropic-ai/sandbox-runtime
9926
10001
  import { execFile as execFile3 } from "node:child_process";
9927
10002
  import { readdir as readdir2, realpath, stat } from "node:fs/promises";
9928
10003
  import { homedir as homedir7, platform } from "node:os";
9929
- import { basename as basename2, dirname as dirname10, isAbsolute as isAbsolute5, join as join19, resolve as resolve9 } from "node:path";
10004
+ import { basename as basename2, dirname as dirname10, isAbsolute as isAbsolute5, join as join20, resolve as resolve9 } from "node:path";
9930
10005
  import { promisify as promisify3 } from "node:util";
9931
10006
  var run = promisify3(execFile3);
9932
10007
  async function git(cwd, args) {
@@ -9996,7 +10071,7 @@ async function browseFolder(request) {
9996
10071
  let truncated = false;
9997
10072
  const dirs = names.filter((e) => e.isDirectory() || e.isSymbolicLink()).filter((e) => request.showHidden || !e.name.startsWith(".")).sort((a, b) => a.name.localeCompare(b.name));
9998
10073
  for (const entry of dirs) {
9999
- const full = join19(path, entry.name);
10074
+ const full = join20(path, entry.name);
10000
10075
  if (entry.isSymbolicLink() && !await isDir(full)) continue;
10001
10076
  if (folders.length >= FS_MAX_ENTRIES) {
10002
10077
  truncated = true;
@@ -10005,7 +10080,7 @@ async function browseFolder(request) {
10005
10080
  folders.push({
10006
10081
  name: entry.name,
10007
10082
  path: full,
10008
- isRepo: await exists(join19(full, ".git"))
10083
+ isRepo: await exists(join20(full, ".git"))
10009
10084
  });
10010
10085
  }
10011
10086
  const parent = dirname10(path);
@@ -10062,6 +10137,9 @@ async function detectDeviceType() {
10062
10137
  }
10063
10138
 
10064
10139
  // src/harnesses.ts
10140
+ import { readdirSync as readdirSync4, statSync as statSync2 } from "node:fs";
10141
+ import { homedir as homedir8, userInfo } from "node:os";
10142
+ import { delimiter as delimiter3, isAbsolute as isAbsolute7, join as join22 } from "node:path";
10065
10143
  import { execa as execa9 } from "execa";
10066
10144
 
10067
10145
  // src/harness-models.ts
@@ -10207,12 +10285,99 @@ async function discover(harness, binary) {
10207
10285
  return {};
10208
10286
  }
10209
10287
 
10288
+ // src/path-search.ts
10289
+ import { accessSync, constants as constants2, statSync } from "node:fs";
10290
+ import { delimiter as delimiter2, isAbsolute as isAbsolute6, join as join21 } from "node:path";
10291
+ function findOnPath(command, path = process.env.PATH ?? "") {
10292
+ for (const directory of path.split(delimiter2)) {
10293
+ if (!directory) continue;
10294
+ const candidate = join21(directory, command);
10295
+ try {
10296
+ accessSync(candidate, constants2.X_OK);
10297
+ if (statSync(candidate).isFile()) return candidate;
10298
+ } catch {
10299
+ }
10300
+ }
10301
+ return null;
10302
+ }
10303
+ function appendToPath(directories) {
10304
+ const entries = (process.env.PATH ?? "").split(delimiter2).filter(Boolean);
10305
+ for (const directory of directories) {
10306
+ if (isAbsolute6(directory) && !entries.includes(directory)) entries.push(directory);
10307
+ }
10308
+ process.env.PATH = entries.join(delimiter2);
10309
+ }
10310
+
10210
10311
  // src/harnesses.ts
10211
10312
  var HARNESS_BINARIES = {
10212
10313
  claude_code: ["claude"],
10213
10314
  codex: ["codex"],
10214
10315
  cursor: ["cursor-agent", "agent"]
10215
10316
  };
10317
+ var LOGIN_SHELL_TIMEOUT_MS = 5e3;
10318
+ var PATH_MARKER = "__MIKKA_PATH__";
10319
+ function subdirectories(directory) {
10320
+ try {
10321
+ return readdirSync4(directory).map((name) => join22(directory, name));
10322
+ } catch {
10323
+ return [];
10324
+ }
10325
+ }
10326
+ function knownAgentDirectories(home = homedir8()) {
10327
+ return [
10328
+ join22(home, ".local/bin"),
10329
+ join22(home, "bin"),
10330
+ join22(home, ".claude/local"),
10331
+ join22(home, ".npm-global/bin"),
10332
+ join22(home, ".bun/bin"),
10333
+ join22(home, ".volta/bin"),
10334
+ join22(home, ".local/share/pnpm"),
10335
+ join22(home, "Library/pnpm"),
10336
+ join22(home, ".asdf/shims"),
10337
+ join22(home, ".local/share/mise/shims"),
10338
+ ...subdirectories(join22(home, ".nvm/versions/node")).map((version) => join22(version, "bin")),
10339
+ ...subdirectories(join22(home, ".local/share/fnm/node-versions")).map((version) => join22(version, "installation/bin")),
10340
+ "/opt/homebrew/bin",
10341
+ "/usr/local/bin",
10342
+ "/snap/bin"
10343
+ ];
10344
+ }
10345
+ async function loginShellPath(shell = process.env.SHELL || userInfo().shell) {
10346
+ if (!shell || !isAbsolute7(shell)) return [];
10347
+ let timer;
10348
+ try {
10349
+ const shellProcess = execa9(shell, ["-ilc", `printf '${PATH_MARKER}%s${PATH_MARKER}' "$PATH"`], {
10350
+ stdin: "ignore",
10351
+ detached: true,
10352
+ reject: false
10353
+ });
10354
+ const timedOut = new Promise((resolve12) => {
10355
+ timer = setTimeout(() => {
10356
+ try {
10357
+ process.kill(-shellProcess.pid, "SIGKILL");
10358
+ } catch {
10359
+ }
10360
+ resolve12(null);
10361
+ }, LOGIN_SHELL_TIMEOUT_MS);
10362
+ });
10363
+ const result = await Promise.race([shellProcess, timedOut]);
10364
+ return String(result?.stdout ?? "").split(PATH_MARKER)[1]?.split(delimiter3).filter(isAbsolute7) ?? [];
10365
+ } catch {
10366
+ return [];
10367
+ } finally {
10368
+ clearTimeout(timer);
10369
+ }
10370
+ }
10371
+ function harnessFingerprint() {
10372
+ appendToPath(knownAgentDirectories());
10373
+ return Object.values(HARNESS_BINARIES).map((binaries) => {
10374
+ for (const binary of binaries) {
10375
+ const found = findOnPath(binary);
10376
+ if (found) return `${found}:${statSync2(found).mtimeMs}`;
10377
+ }
10378
+ return "";
10379
+ }).join("|");
10380
+ }
10216
10381
  async function versionOf(binary) {
10217
10382
  try {
10218
10383
  const { stdout } = await execa9(binary, ["--version"], {
@@ -10228,6 +10393,7 @@ async function versionOf(binary) {
10228
10393
  }
10229
10394
  }
10230
10395
  async function detectHarnesses() {
10396
+ if (!daemonTestMode(process.env)) appendToPath([...await loginShellPath(), ...knownAgentDirectories()]);
10231
10397
  const detected = await Promise.all(
10232
10398
  Object.keys(HARNESS_BINARIES).map(
10233
10399
  async (harness) => {
@@ -10248,16 +10414,136 @@ async function detectHarnesses() {
10248
10414
  ];
10249
10415
  }
10250
10416
 
10417
+ // src/sandbox-tools.ts
10418
+ import { chmodSync as chmodSync2, existsSync as existsSync5, lstatSync as lstatSync8, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync6, readdirSync as readdirSync5, renameSync as renameSync2, rmSync as rmSync12, writeFileSync as writeFileSync7 } from "node:fs";
10419
+ import { join as join23 } from "node:path";
10420
+ import { execa as execa10 } from "execa";
10421
+ var TOOLS = [
10422
+ { command: "bwrap", package: "bubblewrap", versionFlag: "--version" },
10423
+ { command: "socat", package: "socat", versionFlag: "-V" },
10424
+ { command: "rg", package: "ripgrep", versionFlag: "--version" }
10425
+ ];
10426
+ var APT_TIMEOUT_MS = 12e4;
10427
+ var TOOL_CHECK_TIMEOUT_MS = 5e3;
10428
+ var runTool = async (file, args, cwd, env) => {
10429
+ const { stdout } = await execa10(file, args, { cwd, env, stdin: "ignore", timeout: file === "apt-get" ? APT_TIMEOUT_MS : TOOL_CHECK_TIMEOUT_MS });
10430
+ return { stdout: String(stdout) };
10431
+ };
10432
+ function sandboxToolsDir(dataDir) {
10433
+ return join23(dataDir, "sandbox-tools", "bin");
10434
+ }
10435
+ function missingSandboxTools() {
10436
+ return TOOLS.filter((tool2) => !findOnPath(tool2.command)).map((tool2) => tool2.package);
10437
+ }
10438
+ async function ensureSandboxTools(options) {
10439
+ if ((options.platform ?? process.platform) !== "linux") return [];
10440
+ const { dataDir, log, run: run2 = runTool } = options;
10441
+ const binDir = sandboxToolsDir(dataDir);
10442
+ appendToPath([binDir]);
10443
+ const needed = TOOLS.filter((tool2) => !findOnPath(tool2.command));
10444
+ if (needed.length === 0) return [];
10445
+ const packages = needed.map((tool2) => tool2.package);
10446
+ if (!findOnPath("apt-get") || !findOnPath("dpkg-deb")) {
10447
+ log.warn({ missing: packages }, "sandbox tools missing and apt is unavailable");
10448
+ return packages;
10449
+ }
10450
+ mkdirSync13(binDir, { recursive: true, mode: 448 });
10451
+ const work = mkdtempSync6(join23(dataDir, "sandbox-tools", ".install-"));
10452
+ try {
10453
+ const apt = await downloadPackages(work, packages, run2);
10454
+ for (const tool2 of needed) {
10455
+ try {
10456
+ await installTool(tool2, { work, dataDir, apt, run: run2 });
10457
+ } catch (err) {
10458
+ log.warn({ package: tool2.package, err: errorMessage2(err) }, "sandbox tool unusable on this machine");
10459
+ }
10460
+ }
10461
+ } catch (err) {
10462
+ log.warn({ missing: packages, err: errorMessage2(err) }, "could not download sandbox tools");
10463
+ } finally {
10464
+ rmSync12(work, { recursive: true, force: true });
10465
+ }
10466
+ const missing = missingSandboxTools();
10467
+ if (missing.length < packages.length) log.info({ installed: packages.filter((name) => !missing.includes(name)) }, "sandbox tools installed");
10468
+ return missing;
10469
+ }
10470
+ async function installTool(tool2, context) {
10471
+ const { work, dataDir, apt, run: run2 } = context;
10472
+ const root = join23(work, "root");
10473
+ await run2("dpkg-deb", ["-x", join23(work, debFile(work, tool2.package)), root], work);
10474
+ const unpacked = join23(root, "usr", "bin", tool2.command);
10475
+ chmodSync2(unpacked, 493);
10476
+ const binDir = sandboxToolsDir(dataDir);
10477
+ try {
10478
+ await run2(unpacked, [tool2.versionFlag], work);
10479
+ renameSync2(unpacked, join23(binDir, tool2.command));
10480
+ return;
10481
+ } catch {
10482
+ }
10483
+ const plan = await run2("apt-get", ["-s", "-qq", ...apt, "install", tool2.package], work);
10484
+ const dependencies = [...String(plan?.stdout ?? "").matchAll(/^Inst (\S+)/gm)].map((match) => match[1]).filter((name) => name !== tool2.package);
10485
+ if (dependencies.length === 0) throw new Error("the tool does not run and has no missing dependencies");
10486
+ await run2("apt-get", ["-qq", ...apt, "download", ...dependencies], work);
10487
+ for (const dependency of dependencies) await run2("dpkg-deb", ["-x", join23(work, debFile(work, dependency)), root], work);
10488
+ const libDir = join23(dataDir, "sandbox-tools", "lib");
10489
+ mkdirSync13(libDir, { recursive: true, mode: 448 });
10490
+ for (const directory of [join23(root, "lib"), join23(root, "usr", "lib")]) moveSharedLibraries(directory, libDir);
10491
+ await run2(unpacked, [tool2.versionFlag], work, { LD_LIBRARY_PATH: libDir });
10492
+ const realDir = join23(dataDir, "sandbox-tools", "libexec");
10493
+ mkdirSync13(realDir, { recursive: true, mode: 448 });
10494
+ renameSync2(unpacked, join23(realDir, tool2.command));
10495
+ const launcher = join23(work, `${tool2.command}.launcher`);
10496
+ writeFileSync7(launcher, `#!/bin/sh
10497
+ LD_LIBRARY_PATH=${shellQuote2(libDir)} exec ${shellQuote2(join23(realDir, tool2.command))} "$@"
10498
+ `, { mode: 493 });
10499
+ renameSync2(launcher, join23(binDir, tool2.command));
10500
+ }
10501
+ function moveSharedLibraries(directory, libDir) {
10502
+ if (!existsSync5(directory)) return;
10503
+ for (const entry of readdirSync5(directory)) {
10504
+ const path = join23(directory, entry);
10505
+ if (lstatSync8(path).isDirectory()) moveSharedLibraries(path, libDir);
10506
+ else if (entry.includes(".so") && !existsSync5(join23(libDir, entry))) renameSync2(path, join23(libDir, entry));
10507
+ }
10508
+ }
10509
+ function shellQuote2(value) {
10510
+ return `'${value.replaceAll("'", `'\\''`)}'`;
10511
+ }
10512
+ async function downloadPackages(work, packages, run2) {
10513
+ const cache = join23(work, "cache");
10514
+ mkdirSync13(join23(cache, "archives", "partial"), { recursive: true });
10515
+ const unprivileged = ["-o", `Dir::Cache=${cache}`, "-o", "Debug::NoLocking=1", "-o", "APT::Sandbox::User="];
10516
+ try {
10517
+ await run2("apt-get", ["-qq", ...unprivileged, "download", ...packages], work);
10518
+ return unprivileged;
10519
+ } catch {
10520
+ const lists = join23(work, "lists");
10521
+ mkdirSync13(join23(lists, "partial"), { recursive: true });
10522
+ const privateLists = [...unprivileged, "-o", `Dir::State::lists=${lists}`];
10523
+ await run2("apt-get", ["-qq", ...privateLists, "update"], work);
10524
+ await run2("apt-get", ["-qq", ...privateLists, "download", ...packages], work);
10525
+ return privateLists;
10526
+ }
10527
+ }
10528
+ function debFile(work, name) {
10529
+ const file = readdirSync5(work).find((entry) => entry === `${name}.deb` || entry.startsWith(`${name}_`));
10530
+ if (!file) throw new Error(`${name} package was not downloaded`);
10531
+ return file;
10532
+ }
10533
+ function errorMessage2(err) {
10534
+ return err instanceof Error ? err.message.slice(0, 300) : "unknown error";
10535
+ }
10536
+
10251
10537
  // src/outbox.ts
10252
10538
  import {
10253
10539
  appendFileSync,
10254
- mkdirSync as mkdirSync13,
10255
- readFileSync as readFileSync10,
10256
- readdirSync as readdirSync4,
10257
- rmSync as rmSync12,
10540
+ mkdirSync as mkdirSync14,
10541
+ readFileSync as readFileSync11,
10542
+ readdirSync as readdirSync6,
10543
+ rmSync as rmSync13,
10258
10544
  truncateSync
10259
10545
  } from "node:fs";
10260
- import { join as join20 } from "node:path";
10546
+ import { join as join24 } from "node:path";
10261
10547
  var Outbox = class {
10262
10548
  constructor(dir, link, log, retryMs = 1e3, onUndeliverable = () => void 0, retainResources = false) {
10263
10549
  this.dir = dir;
@@ -10266,7 +10552,7 @@ var Outbox = class {
10266
10552
  this.retryMs = retryMs;
10267
10553
  this.onUndeliverable = onUndeliverable;
10268
10554
  this.retainResources = retainResources;
10269
- mkdirSync13(dir, { recursive: true, mode: 448 });
10555
+ mkdirSync14(dir, { recursive: true, mode: 448 });
10270
10556
  }
10271
10557
  dir;
10272
10558
  link;
@@ -10280,15 +10566,15 @@ var Outbox = class {
10280
10566
  /** Runs whose file was checked for a torn tail since this process started. */
10281
10567
  repaired = /* @__PURE__ */ new Set();
10282
10568
  file(runId) {
10283
- return join20(this.dir, `${assertSafeId("run id", runId)}.jsonl`);
10569
+ return join24(this.dir, `${assertSafeId("run id", runId)}.jsonl`);
10284
10570
  }
10285
10571
  ackFile(runId) {
10286
- return join20(this.dir, `${assertSafeId("run id", runId)}.ack.json`);
10572
+ return join24(this.dir, `${assertSafeId("run id", runId)}.ack.json`);
10287
10573
  }
10288
10574
  parse(runId) {
10289
10575
  let data;
10290
10576
  try {
10291
- data = readFileSync10(this.file(runId));
10577
+ data = readFileSync11(this.file(runId));
10292
10578
  } catch {
10293
10579
  return { items: [], validBytes: 0, torn: false, tornTail: false };
10294
10580
  }
@@ -10373,7 +10659,7 @@ var Outbox = class {
10373
10659
  }
10374
10660
  runIds() {
10375
10661
  try {
10376
- return readdirSync4(this.dir).filter((name) => name.endsWith(".jsonl")).map((name) => name.slice(0, -".jsonl".length)).filter((runId) => isSafeId(runId));
10662
+ return readdirSync6(this.dir).filter((name) => name.endsWith(".jsonl")).map((name) => name.slice(0, -".jsonl".length)).filter((runId) => isSafeId(runId));
10377
10663
  } catch {
10378
10664
  return [];
10379
10665
  }
@@ -10441,8 +10727,8 @@ var Outbox = class {
10441
10727
  }
10442
10728
  remove(runId) {
10443
10729
  if (this.retainResources) return;
10444
- rmSync12(this.file(runId), { force: true });
10445
- rmSync12(this.ackFile(runId), { force: true });
10730
+ rmSync13(this.file(runId), { force: true });
10731
+ rmSync13(this.ackFile(runId), { force: true });
10446
10732
  }
10447
10733
  scheduleRetry() {
10448
10734
  if (this.retryTimer || this.closed) return;
@@ -10467,15 +10753,15 @@ var Outbox = class {
10467
10753
  };
10468
10754
 
10469
10755
  // src/quota/local-credentials.ts
10470
- import { homedir as homedir8 } from "node:os";
10471
- import { join as join22 } from "node:path";
10756
+ import { homedir as homedir9 } from "node:os";
10757
+ import { join as join26 } from "node:path";
10472
10758
 
10473
10759
  // src/quota/files.ts
10474
10760
  import { execFile as execFile4 } from "node:child_process";
10475
- import { constants as constants2 } from "node:fs";
10761
+ import { constants as constants3 } from "node:fs";
10476
10762
  import { lstat as lstat2, open } from "node:fs/promises";
10477
10763
  import { promisify as promisify4 } from "node:util";
10478
- var NOFOLLOW = constants2.O_NOFOLLOW ?? 0;
10764
+ var NOFOLLOW = constants3.O_NOFOLLOW ?? 0;
10479
10765
  var execFileAsync = promisify4(execFile4);
10480
10766
  var KEYCHAIN_TIMEOUT_MS = 3e4;
10481
10767
  var MAX_CREDENTIAL_BYTES = 64 * 1024;
@@ -10551,7 +10837,7 @@ async function readSecureFile(filePath, maxBytes = MAX_CREDENTIAL_BYTES) {
10551
10837
  if (before.size > maxBytes) {
10552
10838
  throw new Error(`Credential file exceeds ${maxBytes} bytes: ${filePath}`);
10553
10839
  }
10554
- const handle = await open(filePath, constants2.O_RDONLY | NOFOLLOW);
10840
+ const handle = await open(filePath, constants3.O_RDONLY | NOFOLLOW);
10555
10841
  try {
10556
10842
  const after = await handle.stat();
10557
10843
  assertRegularFile(after, filePath);
@@ -10966,12 +11252,12 @@ async function readCodexQuota(options) {
10966
11252
 
10967
11253
  // src/quota/cursor.ts
10968
11254
  import { lstat as lstat3 } from "node:fs/promises";
10969
- import { join as join21 } from "node:path";
11255
+ import { join as join25 } from "node:path";
10970
11256
  var ENDPOINT3 = "https://cursor.com/api/usage-summary";
10971
11257
  var ACCESS_TOKEN_KEY = "cursorAuth/accessToken";
10972
11258
  function cursorDatabasePath(platform3, home) {
10973
11259
  if (platform3 === "darwin") {
10974
- return join21(
11260
+ return join25(
10975
11261
  home,
10976
11262
  "Library",
10977
11263
  "Application Support",
@@ -10982,7 +11268,7 @@ function cursorDatabasePath(platform3, home) {
10982
11268
  );
10983
11269
  }
10984
11270
  if (platform3 === "win32") {
10985
- return join21(
11271
+ return join25(
10986
11272
  home,
10987
11273
  "AppData",
10988
11274
  "Roaming",
@@ -10992,7 +11278,7 @@ function cursorDatabasePath(platform3, home) {
10992
11278
  "state.vscdb"
10993
11279
  );
10994
11280
  }
10995
- return join21(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb");
11281
+ return join25(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb");
10996
11282
  }
10997
11283
  var CURSOR_PLANS = {
10998
11284
  pro: "Pro",
@@ -11164,10 +11450,10 @@ async function readCursorQuota(options) {
11164
11450
  // src/quota/local-credentials.ts
11165
11451
  var KEYCHAIN_SERVICE = "Claude Code-credentials";
11166
11452
  function claudeCredentialPath(home) {
11167
- return join22(home, ".claude", ".credentials.json");
11453
+ return join26(home, ".claude", ".credentials.json");
11168
11454
  }
11169
11455
  function codexAuthPath(home) {
11170
- return join22(home, ".codex", "auth.json");
11456
+ return join26(home, ".codex", "auth.json");
11171
11457
  }
11172
11458
  function unknown2(vendor, observedAt) {
11173
11459
  return {
@@ -11186,7 +11472,7 @@ var LocalCredentialsSource = class {
11186
11472
  options;
11187
11473
  id = "local_credentials";
11188
11474
  async read(signal) {
11189
- const home = this.options.home ?? homedir8();
11475
+ const home = this.options.home ?? homedir9();
11190
11476
  const platform3 = this.options.platform ?? process.platform;
11191
11477
  const fetchImpl = this.options.fetch ?? globalThis.fetch;
11192
11478
  const now = this.options.now ?? Date.now;
@@ -11319,7 +11605,7 @@ var QuotaPoller = class {
11319
11605
 
11320
11606
  // src/rpc/server.ts
11321
11607
  import { timingSafeEqual } from "node:crypto";
11322
- import { chmodSync as chmodSync2, rmSync as rmSync13 } from "node:fs";
11608
+ import { chmodSync as chmodSync3, rmSync as rmSync14 } from "node:fs";
11323
11609
  import { createServer as createServer2 } from "node:net";
11324
11610
  import {
11325
11611
  createMessageConnection as createMessageConnection2,
@@ -11360,13 +11646,13 @@ var RpcServer = class {
11360
11646
  sockets = /* @__PURE__ */ new Set();
11361
11647
  async listen() {
11362
11648
  const { socketPath } = this.options;
11363
- rmSync13(socketPath, { force: true });
11649
+ rmSync14(socketPath, { force: true });
11364
11650
  const server = createServer2((socket) => this.accept(socket));
11365
11651
  await new Promise((resolve12, reject) => {
11366
11652
  server.once("error", reject);
11367
11653
  server.listen(socketPath, () => resolve12());
11368
11654
  });
11369
- chmodSync2(socketPath, 384);
11655
+ chmodSync3(socketPath, 384);
11370
11656
  this.server = server;
11371
11657
  }
11372
11658
  accept(socket) {
@@ -11433,7 +11719,7 @@ var RpcServer = class {
11433
11719
  this.server.close(() => resolve12());
11434
11720
  });
11435
11721
  this.server = null;
11436
- rmSync13(this.options.socketPath, { force: true });
11722
+ rmSync14(this.options.socketPath, { force: true });
11437
11723
  }
11438
11724
  };
11439
11725
 
@@ -11496,12 +11782,12 @@ var RunJournal = class {
11496
11782
  };
11497
11783
 
11498
11784
  // src/runs/check.ts
11499
- import { execa as execa10 } from "execa";
11785
+ import { execa as execa11 } from "execa";
11500
11786
  async function fixedCheckSnapshot(repoPath, worktree, sha) {
11501
11787
  if (!/^[a-f0-9]{40}$/.test(sha))
11502
11788
  throw new Error("Check requires an exact candidate SHA");
11503
11789
  const gitDir = await worktreeGitDir(repoPath, worktree);
11504
- const git2 = async (...args) => (await execa10(
11790
+ const git2 = async (...args) => (await execa11(
11505
11791
  "git",
11506
11792
  [
11507
11793
  "-c",
@@ -11656,15 +11942,15 @@ function createRunMasker(secrets) {
11656
11942
 
11657
11943
  // src/runs/supervisor.ts
11658
11944
  import { createHash as createHash4, randomUUID as randomUUID6 } from "node:crypto";
11659
- import { join as join24, resolve as resolve10 } from "node:path";
11660
- import { execa as execa12 } from "execa";
11945
+ import { join as join28, resolve as resolve10 } from "node:path";
11946
+ import { execa as execa13 } from "execa";
11661
11947
 
11662
11948
  // src/agents/checkpoint.ts
11663
- import { mkdtempSync as mkdtempSync6, rmSync as rmSync14, writeFileSync as writeFileSync7 } from "node:fs";
11949
+ import { mkdtempSync as mkdtempSync7, rmSync as rmSync15, writeFileSync as writeFileSync8 } from "node:fs";
11664
11950
  import { tmpdir as tmpdir2 } from "node:os";
11665
- import { join as join23 } from "node:path";
11951
+ import { join as join27 } from "node:path";
11666
11952
  import { gunzipSync as gunzipSync2, gzipSync as gzipSync2 } from "node:zlib";
11667
- import { execa as execa11 } from "execa";
11953
+ import { execa as execa12 } from "execa";
11668
11954
  var CheckpointRecoveryError = class extends Error {
11669
11955
  constructor(message) {
11670
11956
  super(message);
@@ -11709,12 +11995,12 @@ async function takeCheckpoint(options) {
11709
11995
  }
11710
11996
  async function applyCheckpoint(options) {
11711
11997
  const git2 = await pinnedGit(options.repoPath, options.worktree);
11712
- const dir = mkdtempSync6(join23(tmpdir2(), "mikka-checkpoint-"));
11713
- const file = join23(dir, "checkpoint.patch");
11998
+ const dir = mkdtempSync7(join27(tmpdir2(), "mikka-checkpoint-"));
11999
+ const file = join27(dir, "checkpoint.patch");
11714
12000
  try {
11715
12001
  const patch = gunzipSync2(Buffer.from(options.checkpoint.patch, "base64"));
11716
12002
  if (!patch.length && options.checkpoint.files === 0) return true;
11717
- writeFileSync7(file, patch);
12003
+ writeFileSync8(file, patch);
11718
12004
  for (const args of [
11719
12005
  ["apply", "--binary", "--whitespace=nowarn", file],
11720
12006
  ["apply", "--binary", "--3way", "--whitespace=nowarn", file]
@@ -11727,11 +12013,11 @@ async function applyCheckpoint(options) {
11727
12013
  }
11728
12014
  return false;
11729
12015
  } finally {
11730
- rmSync14(dir, { recursive: true, force: true });
12016
+ rmSync15(dir, { recursive: true, force: true });
11731
12017
  }
11732
12018
  }
11733
12019
  async function hasCommit(repoPath, commit) {
11734
- return execa11(
12020
+ return execa12(
11735
12021
  "git",
11736
12022
  ["-c", "core.hooksPath=/dev/null", "cat-file", "-e", `${commit}^{commit}`],
11737
12023
  { cwd: repoPath, stdin: "ignore" }
@@ -12948,7 +13234,7 @@ var RunSupervisor = class {
12948
13234
  if (!this.deps.adapterStateDir || assignment.harness === "mock" || !session.sessionId || resuming && session.sessionId !== entry.sessionId || assignment.requireSessionResume && session.sessionId !== assignment.resumeSessionId)
12949
13235
  throw new Error("Skill mount requires the actual original harness session");
12950
13236
  verifySkillAssignment(assignment);
12951
- const file = join24(
13237
+ const file = join28(
12952
13238
  runPaths(this.deps.adapterStateDir, runId).runDir,
12953
13239
  SKILLS_DIR,
12954
13240
  skillSlug(assignment.skills[0].name),
@@ -13181,7 +13467,7 @@ function stopReasonOf(event, run2) {
13181
13467
  return event.outcome === "failed" ? "error" : "turn_ended";
13182
13468
  }
13183
13469
  async function unsandboxedVerify(run2) {
13184
- const result = await execa12(run2.command, {
13470
+ const result = await execa13(run2.command, {
13185
13471
  shell: true,
13186
13472
  cwd: run2.worktree,
13187
13473
  env: buildChildEnv(),
@@ -13212,10 +13498,10 @@ function fixedCandidateCheck(assignment) {
13212
13498
 
13213
13499
  // src/repositories.ts
13214
13500
  import { createHash as createHash5, randomUUID as randomUUID7 } from "node:crypto";
13215
- import { existsSync as existsSync5, lstatSync as lstatSync8, mkdirSync as mkdirSync14, readdirSync as readdirSync5, realpathSync as realpathSync11, renameSync as renameSync2, rmSync as rmSync15 } from "node:fs";
13216
- import { dirname as dirname11, join as join25, resolve as resolve11 } from "node:path";
13501
+ import { existsSync as existsSync6, lstatSync as lstatSync9, mkdirSync as mkdirSync15, readdirSync as readdirSync7, realpathSync as realpathSync11, renameSync as renameSync3, rmSync as rmSync16 } from "node:fs";
13502
+ import { dirname as dirname11, join as join29, resolve as resolve11 } from "node:path";
13217
13503
  import { setTimeout as delay2 } from "node:timers/promises";
13218
- import { execa as execa13 } from "execa";
13504
+ import { execa as execa14 } from "execa";
13219
13505
  import lockfile2 from "proper-lockfile";
13220
13506
  var RepositoryCache = class {
13221
13507
  constructor(root, allowFile = daemonTestMode(process.env), retainResources = false) {
@@ -13241,19 +13527,19 @@ var RepositoryCache = class {
13241
13527
  const release = await this.lock(path, signal);
13242
13528
  try {
13243
13529
  signal?.throwIfAborted();
13244
- const git2 = (args, cwd) => execa13("git", [
13530
+ const git2 = (args, cwd) => execa14("git", [
13245
13531
  ...FETCH_GIT,
13246
13532
  "-c",
13247
13533
  `protocol.file.allow=${this.allowFile ? "always" : "never"}`,
13248
13534
  ...args
13249
13535
  ], { cwd, env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }, stdin: "ignore", timeout: FETCH_TIMEOUT_MS, cancelSignal: signal, killDescendants: true });
13250
- if (!existsSync5(path)) {
13536
+ if (!existsSync6(path)) {
13251
13537
  const temporary = `${path}.clone-${randomUUID7()}`;
13252
13538
  try {
13253
13539
  await git2(["clone", "--no-checkout", "--no-hardlinks", "--no-recurse-submodules", "--", remote, temporary]);
13254
- renameSync2(temporary, path);
13540
+ renameSync3(temporary, path);
13255
13541
  } finally {
13256
- if (!this.retainResources) rmSync15(temporary, { recursive: true, force: true });
13542
+ if (!this.retainResources) rmSync16(temporary, { recursive: true, force: true });
13257
13543
  }
13258
13544
  } else {
13259
13545
  this.assertClone(path);
@@ -13273,36 +13559,36 @@ var RepositoryCache = class {
13273
13559
  await assertBranchName(baseBranch);
13274
13560
  return `origin/${baseBranch}`;
13275
13561
  }
13276
- const { stdout } = await execa13("git", [...FETCH_GIT, "symbolic-ref", "--short", "refs/remotes/origin/HEAD"], { cwd: path, stdin: "ignore" });
13562
+ const { stdout } = await execa14("git", [...FETCH_GIT, "symbolic-ref", "--short", "refs/remotes/origin/HEAD"], { cwd: path, stdin: "ignore" });
13277
13563
  if (!stdout.startsWith("origin/")) throw new Error("Repository default branch unavailable");
13278
13564
  return stdout.trim();
13279
13565
  }
13280
13566
  /** Cache paths for retention/orphan cleanup; never consult user checkouts. */
13281
13567
  paths() {
13282
13568
  const visit = (path) => {
13283
- if (!existsSync5(path) || lstatSync8(path).isSymbolicLink()) return [];
13284
- if (existsSync5(join25(path, ".git"))) {
13569
+ if (!existsSync6(path) || lstatSync9(path).isSymbolicLink()) return [];
13570
+ if (existsSync6(join29(path, ".git"))) {
13285
13571
  this.assertClone(path);
13286
13572
  return [path];
13287
13573
  }
13288
- return readdirSync5(path, { withFileTypes: true }).flatMap((entry) => entry.isDirectory() && !entry.name.endsWith(".lock") && !entry.name.includes(".clone-") ? visit(join25(path, entry.name)) : []);
13574
+ return readdirSync7(path, { withFileTypes: true }).flatMap((entry) => entry.isDirectory() && !entry.name.endsWith(".lock") && !entry.name.includes(".clone-") ? visit(join29(path, entry.name)) : []);
13289
13575
  };
13290
13576
  return visit(resolve11(this.root));
13291
13577
  }
13292
13578
  ensureParents(path) {
13293
- mkdirSync14(this.root, { recursive: true, mode: 448 });
13294
- if (lstatSync8(this.root).isSymbolicLink()) throw new Error("Repository cache contains a symlink");
13579
+ mkdirSync15(this.root, { recursive: true, mode: 448 });
13580
+ if (lstatSync9(this.root).isSymbolicLink()) throw new Error("Repository cache contains a symlink");
13295
13581
  const root = realpathSync11(this.root);
13296
13582
  let current = resolve11(this.root);
13297
13583
  for (const part of path.slice(current.length + 1).split("/").filter(Boolean)) {
13298
- current = join25(current, part);
13299
- if (existsSync5(current) && lstatSync8(current).isSymbolicLink()) throw new Error("Repository cache contains a symlink");
13300
- mkdirSync14(current, { recursive: true, mode: 448 });
13584
+ current = join29(current, part);
13585
+ if (existsSync6(current) && lstatSync9(current).isSymbolicLink()) throw new Error("Repository cache contains a symlink");
13586
+ mkdirSync15(current, { recursive: true, mode: 448 });
13301
13587
  if (!isInside(root, realpathSync11(current))) throw new Error("Repository cache escapes its directory");
13302
13588
  }
13303
13589
  }
13304
13590
  assertClone(path) {
13305
- if (lstatSync8(path).isSymbolicLink() || !lstatSync8(join25(path, ".git")).isDirectory() || lstatSync8(join25(path, ".git")).isSymbolicLink()) throw new Error("Repository cache is not a standard clone");
13591
+ if (lstatSync9(path).isSymbolicLink() || !lstatSync9(join29(path, ".git")).isDirectory() || lstatSync9(join29(path, ".git")).isSymbolicLink()) throw new Error("Repository cache is not a standard clone");
13306
13592
  if (!isInside(realpathSync11(this.root), realpathSync11(path))) throw new Error("Repository cache escapes its directory");
13307
13593
  }
13308
13594
  async lock(path, signal) {
@@ -13323,16 +13609,16 @@ var RepositoryCache = class {
13323
13609
  import { execFile as execFile5, spawn as spawn4 } from "node:child_process";
13324
13610
  import { randomUUID as randomUUID8 } from "node:crypto";
13325
13611
  import {
13326
- existsSync as existsSync6,
13327
- lstatSync as lstatSync9,
13328
- mkdirSync as mkdirSync15,
13329
- mkdtempSync as mkdtempSync7,
13330
- readdirSync as readdirSync6,
13612
+ existsSync as existsSync7,
13613
+ lstatSync as lstatSync10,
13614
+ mkdirSync as mkdirSync16,
13615
+ mkdtempSync as mkdtempSync8,
13616
+ readdirSync as readdirSync8,
13331
13617
  realpathSync as realpathSync12,
13332
- rmSync as rmSync16
13618
+ rmSync as rmSync17
13333
13619
  } from "node:fs";
13334
13620
  import { createRequire } from "node:module";
13335
- import { delimiter as delimiter2, dirname as dirname12, join as join27 } from "node:path";
13621
+ import { delimiter as delimiter4, dirname as dirname12, join as join31 } from "node:path";
13336
13622
  import { fileURLToPath as fileURLToPath2 } from "node:url";
13337
13623
  import { promisify as promisify5 } from "node:util";
13338
13624
 
@@ -13342,7 +13628,7 @@ import { chmod, mkdtemp, rm as rm2 } from "node:fs/promises";
13342
13628
  import { createServer as createServer3, request as httpRequest } from "node:http";
13343
13629
  import { connect, isIP as isIP3 } from "node:net";
13344
13630
  import { tmpdir as tmpdir3 } from "node:os";
13345
- import { join as join26 } from "node:path";
13631
+ import { join as join30 } from "node:path";
13346
13632
 
13347
13633
  // ../../lib/search/url-policy.ts
13348
13634
  import { BlockList, isIP as isIP2 } from "node:net";
@@ -13411,9 +13697,9 @@ function endToEndHeaders(input) {
13411
13697
  }
13412
13698
  async function startPublicEgress(options) {
13413
13699
  options.signal.throwIfAborted();
13414
- const directory = await mkdtemp(join26(tmpdir3(), "mikka-egress-"));
13700
+ const directory = await mkdtemp(join30(tmpdir3(), "mikka-egress-"));
13415
13701
  await chmod(directory, 448);
13416
- const socketPath = join26(directory, "egress.sock");
13702
+ const socketPath = join30(directory, "egress.sock");
13417
13703
  const lifetime = new AbortController();
13418
13704
  const signal = AbortSignal.any([options.signal, lifetime.signal]);
13419
13705
  const sockets = /* @__PURE__ */ new Set();
@@ -13570,8 +13856,8 @@ async function createHostedBotRunner(host) {
13570
13856
  }).catch(() => {
13571
13857
  throw unavailable2();
13572
13858
  });
13573
- const resources = join27(host.stateDir, "native-resources");
13574
- mkdirSync15(resources, { recursive: true, mode: 448 });
13859
+ const resources = join31(host.stateDir, "native-resources");
13860
+ mkdirSync16(resources, { recursive: true, mode: 448 });
13575
13861
  const removeResource = async (file, record) => {
13576
13862
  if (!/^mikka-native-(?:verify-)?[a-f0-9-]{36}$/.test(record.name) || !/^[a-f0-9-]{36}$/.test(record.nonce))
13577
13863
  throw unavailable2();
@@ -13591,9 +13877,9 @@ async function createHostedBotRunner(host) {
13591
13877
  if (record.state === "created" && /No such (?:container|object)/i.test(
13592
13878
  String(error.stderr)
13593
13879
  )) {
13594
- if (record.assets?.startsWith(join27(host.stateDir, "native-verify-git-")))
13595
- rmSync16(record.assets, { recursive: true, force: true });
13596
- rmSync16(file, { force: true });
13880
+ if (record.assets?.startsWith(join31(host.stateDir, "native-verify-git-")))
13881
+ rmSync17(record.assets, { recursive: true, force: true });
13882
+ rmSync17(file, { force: true });
13597
13883
  return;
13598
13884
  }
13599
13885
  throw new Error("native_runtime_cleanup_unconfirmed");
@@ -13602,13 +13888,13 @@ async function createHostedBotRunner(host) {
13602
13888
  if (labels?.["com.mikka.native.resource"] !== record.nonce || record.id && record.id !== id4)
13603
13889
  throw unavailable2();
13604
13890
  await exec3("docker", ["rm", "-f", id4], { env: bareEnv, timeout: 3e4 });
13605
- if (record.assets?.startsWith(join27(host.stateDir, "native-verify-git-")))
13606
- rmSync16(record.assets, { recursive: true, force: true });
13607
- rmSync16(file, { force: true });
13891
+ if (record.assets?.startsWith(join31(host.stateDir, "native-verify-git-")))
13892
+ rmSync17(record.assets, { recursive: true, force: true });
13893
+ rmSync17(file, { force: true });
13608
13894
  };
13609
- for (const name of readdirSync6(resources))
13895
+ for (const name of readdirSync8(resources))
13610
13896
  if (name.endsWith(".json")) {
13611
- const file = join27(resources, name);
13897
+ const file = join31(resources, name);
13612
13898
  const record = readJson(file);
13613
13899
  if (!record) throw unavailable2();
13614
13900
  await removeResource(file, record);
@@ -13619,7 +13905,7 @@ async function createHostedBotRunner(host) {
13619
13905
  const bundled = createRequire(
13620
13906
  require2.resolve("@anthropic-ai/claude-agent-sdk")
13621
13907
  );
13622
- claude ??= join27(
13908
+ claude ??= join31(
13623
13909
  dirname12(
13624
13910
  bundled.resolve(
13625
13911
  "@anthropic-ai/claude-agent-sdk-linux-x64/package.json"
@@ -13629,13 +13915,13 @@ async function createHostedBotRunner(host) {
13629
13915
  );
13630
13916
  } catch {
13631
13917
  }
13632
- const codex = host.binaries?.codex ?? (process.env.PATH ?? "").split(delimiter2).map((dir) => join27(dir, "codex")).find((path) => existsSync6(path));
13918
+ const codex = host.binaries?.codex ?? (process.env.PATH ?? "").split(delimiter4).map((dir) => join31(dir, "codex")).find((path) => existsSync7(path));
13633
13919
  const binaries = { codex, claude_code: claude };
13634
13920
  for (const kind of ["codex", "claude_code"])
13635
13921
  if (binaries[kind]) {
13636
13922
  try {
13637
13923
  binaries[kind] = realpathSync12(binaries[kind]);
13638
- if (!lstatSync9(binaries[kind]).isFile()) delete binaries[kind];
13924
+ if (!lstatSync10(binaries[kind]).isFile()) delete binaries[kind];
13639
13925
  } catch {
13640
13926
  delete binaries[kind];
13641
13927
  }
@@ -13643,12 +13929,12 @@ async function createHostedBotRunner(host) {
13643
13929
  if (!binaries.codex && !binaries.claude_code) throw unavailable2();
13644
13930
  const command = async (worktree, script, readOnly, timeoutMs, signal) => {
13645
13931
  const name = `mikka-native-verify-${randomUUID8()}`;
13646
- const file = join27(resources, name + ".json");
13932
+ const file = join31(resources, name + ".json");
13647
13933
  const resource = { name, nonce: randomUUID8(), state: "creating" };
13648
13934
  const timing = { createMs: 0, runMs: 0 };
13649
13935
  let phase = "create";
13650
13936
  let began = performance.now();
13651
- const gitCopy = mkdtempSync7(join27(host.stateDir, "native-verify-git-"));
13937
+ const gitCopy = mkdtempSync8(join31(host.stateDir, "native-verify-git-"));
13652
13938
  resource.assets = gitCopy;
13653
13939
  try {
13654
13940
  const cwd = realpathSync12(worktree);
@@ -13664,7 +13950,7 @@ async function createHostedBotRunner(host) {
13664
13950
  "--no-checkout",
13665
13951
  "--",
13666
13952
  cwd,
13667
- join27(gitCopy, "repo")
13953
+ join31(gitCopy, "repo")
13668
13954
  ],
13669
13955
  {
13670
13956
  env: {
@@ -13679,8 +13965,8 @@ async function createHostedBotRunner(host) {
13679
13965
  maxBuffer: 1024 * 1024
13680
13966
  }
13681
13967
  );
13682
- const safeGit = join27(gitCopy, "repo", ".git");
13683
- const protectedGit = lstatSync9(join27(cwd, ".git")).isFile() ? join27(cwd, ".git") : safeGit;
13968
+ const safeGit = join31(gitCopy, "repo", ".git");
13969
+ const protectedGit = lstatSync10(join31(cwd, ".git")).isFile() ? join31(cwd, ".git") : safeGit;
13684
13970
  writeJsonAtomic(file, resource);
13685
13971
  const created = await exec3(
13686
13972
  "docker",
@@ -13767,8 +14053,8 @@ async function createHostedBotRunner(host) {
13767
14053
  timing
13768
14054
  };
13769
14055
  } finally {
13770
- if (existsSync6(file)) await removeResource(file, resource);
13771
- else rmSync16(gitCopy, { recursive: true, force: true });
14056
+ if (existsSync7(file)) await removeResource(file, resource);
14057
+ else rmSync17(gitCopy, { recursive: true, force: true });
13772
14058
  }
13773
14059
  };
13774
14060
  return {
@@ -13784,7 +14070,7 @@ async function createHostedBotRunner(host) {
13784
14070
  throw unavailable2();
13785
14071
  const name = `mikka-native-${randomUUID8()}`;
13786
14072
  const nonce = randomUUID8();
13787
- const resourceFile = join27(resources, name + ".json");
14073
+ const resourceFile = join31(resources, name + ".json");
13788
14074
  const resource = { name, nonce, state: "creating" };
13789
14075
  const lifetime = new AbortController();
13790
14076
  let closed = false;
@@ -13823,7 +14109,7 @@ async function createHostedBotRunner(host) {
13823
14109
  void brokerClosed.catch(() => void 0);
13824
14110
  try {
13825
14111
  await creating?.catch(() => void 0);
13826
- if (existsSync6(resourceFile)) await removeResource(resourceFile, resource);
14112
+ if (existsSync7(resourceFile)) await removeResource(resourceFile, resource);
13827
14113
  } finally {
13828
14114
  try {
13829
14115
  await brokerClosed;
@@ -13856,11 +14142,11 @@ async function createHostedBotRunner(host) {
13856
14142
  return current;
13857
14143
  };
13858
14144
  const paths = runPaths(host.stateDir, assignment.runId);
13859
- mkdirSync15(paths.runDir, { recursive: true, mode: 448 });
13860
- const shadow = join27(paths.runDir, "runtime-git");
13861
- const emptyHome = join27(paths.runDir, "git-bootstrap-home");
13862
- mkdirSync15(emptyHome, { recursive: true, mode: 448 });
13863
- if (!existsSync6(shadow))
14145
+ mkdirSync16(paths.runDir, { recursive: true, mode: 448 });
14146
+ const shadow = join31(paths.runDir, "runtime-git");
14147
+ const emptyHome = join31(paths.runDir, "git-bootstrap-home");
14148
+ mkdirSync16(emptyHome, { recursive: true, mode: 448 });
14149
+ if (!existsSync7(shadow))
13864
14150
  await exec3(
13865
14151
  "git",
13866
14152
  [
@@ -13889,9 +14175,9 @@ async function createHostedBotRunner(host) {
13889
14175
  }
13890
14176
  );
13891
14177
  await live();
13892
- if (!lstatSync9(shadow).isDirectory() || !lstatSync9(join27(shadow, ".git")).isDirectory() || !realpathSync12(shadow).startsWith(realpathSync12(paths.runDir) + "/"))
14178
+ if (!lstatSync10(shadow).isDirectory() || !lstatSync10(join31(shadow, ".git")).isDirectory() || !realpathSync12(shadow).startsWith(realpathSync12(paths.runDir) + "/"))
13893
14179
  throw unavailable2();
13894
- const ledger = join27(
14180
+ const ledger = join31(
13895
14181
  host.stateDir,
13896
14182
  "native-ledgers",
13897
14183
  assignment.runId + ".json"
@@ -13908,15 +14194,15 @@ async function createHostedBotRunner(host) {
13908
14194
  modelId: first.modelId
13909
14195
  };
13910
14196
  const previous = readJson(ledger);
13911
- if (existsSync6(ledger) && (!previous || JSON.stringify(previous.scope) !== JSON.stringify(scope) || typeof previous.sessionId !== "string"))
14197
+ if (existsSync7(ledger) && (!previous || JSON.stringify(previous.scope) !== JSON.stringify(scope) || typeof previous.sessionId !== "string"))
13912
14198
  throw unavailable2();
13913
- mkdirSync15(dirname12(ledger), { recursive: true, mode: 448 });
14199
+ mkdirSync16(dirname12(ledger), { recursive: true, mode: 448 });
13914
14200
  const record = async (sessionId) => {
13915
14201
  assertOpen();
13916
14202
  writeJsonAtomic(ledger, { scope, sessionId });
13917
14203
  };
13918
14204
  brokerStarting = createNativeSubscriptionBroker({
13919
- root: join27(host.stateDir, "native-brokers"),
14205
+ root: join31(host.stateDir, "native-brokers"),
13920
14206
  revision,
13921
14207
  check,
13922
14208
  worker: {
@@ -13943,7 +14229,7 @@ async function createHostedBotRunner(host) {
13943
14229
  });
13944
14230
  const readyBroker = await brokerStarting;
13945
14231
  await live();
13946
- const githubCli = options.github ? (process.env.PATH ?? "").split(delimiter2).map((dir) => join27(dir, "gh")).find((file) => existsSync6(file)) : void 0;
14232
+ const githubCli = options.github ? (process.env.PATH ?? "").split(delimiter4).map((dir) => join31(dir, "gh")).find((file) => existsSync7(file)) : void 0;
13947
14233
  if (options.github && !githubCli) throw new Error("Install GitHub CLI (gh) in the cloud worker image");
13948
14234
  const githubDomains = options.github ? ["github.com", "api.github.com", new URL(options.github.endpoint).host] : [];
13949
14235
  const egressMaximum = runDomains(runPolicy(options.policy), [...options.networkAllow ?? [], ...githubDomains]);
@@ -13960,8 +14246,8 @@ async function createHostedBotRunner(host) {
13960
14246
  }
13961
14247
  const cwd = realpathSync12(options.cwd);
13962
14248
  const runDir = realpathSync12(paths.runDir);
13963
- const protectedGit = lstatSync9(join27(cwd, ".git")).isFile() ? join27(cwd, ".git") : join27(shadow, ".git");
13964
- if (!lstatSync9(runDir).isDirectory() || !runDir.startsWith(realpathSync12(host.stateDir) + "/runs/")) {
14249
+ const protectedGit = lstatSync10(join31(cwd, ".git")).isFile() ? join31(cwd, ".git") : join31(shadow, ".git");
14250
+ if (!lstatSync10(runDir).isDirectory() || !runDir.startsWith(realpathSync12(host.stateDir) + "/runs/")) {
13965
14251
  await close();
13966
14252
  throw unavailable2();
13967
14253
  }
@@ -14014,7 +14300,7 @@ async function createHostedBotRunner(host) {
14014
14300
  `${MCP_SERVER_SCRIPT}:${MCP_SERVER_SCRIPT}:ro`,
14015
14301
  ...githubCli ? ["-v", `${githubCli}:/usr/local/bin/mikka-gh:ro`, "-v", `${GITHUB_CREDENTIAL_SCRIPT}:${GITHUB_CREDENTIAL_SCRIPT}:ro`] : [],
14016
14302
  "-v",
14017
- `${join27(dirname12(MCP_SERVER_SCRIPT), "capture-copy.mjs")}:${join27(dirname12(MCP_SERVER_SCRIPT), "capture-copy.mjs")}:ro`,
14303
+ `${join31(dirname12(MCP_SERVER_SCRIPT), "capture-copy.mjs")}:${join31(dirname12(MCP_SERVER_SCRIPT), "capture-copy.mjs")}:ro`,
14018
14304
  "-v",
14019
14305
  `${readyBroker.socket}:/run/mikka-native.sock:ro`,
14020
14306
  ...computerGrant ? [
@@ -14057,13 +14343,13 @@ async function createHostedBotRunner(host) {
14057
14343
  }
14058
14344
  const spawnHosted = (config) => {
14059
14345
  assertOpen();
14060
- const home = join27(runDir, "runtime-home");
14061
- mkdirSync15(home, { recursive: true, mode: 448 });
14346
+ const home = join31(runDir, "runtime-home");
14347
+ mkdirSync16(home, { recursive: true, mode: 448 });
14062
14348
  const env = {
14063
14349
  ...config.env,
14064
14350
  HOME: kind === "claude_code" ? config.env.HOME ?? home : home,
14065
- PATH: `${options.github ? join27(runDir, "github", "bin") + ":" : ""}/usr/local/bin:/usr/bin:/bin`,
14066
- GIT_DIR: join27(shadow, ".git"),
14351
+ PATH: `${options.github ? join31(runDir, "github", "bin") + ":" : ""}/usr/local/bin:/usr/bin:/bin`,
14352
+ GIT_DIR: join31(shadow, ".git"),
14067
14353
  GIT_WORK_TREE: cwd,
14068
14354
  GIT_CONFIG_GLOBAL: "/dev/null",
14069
14355
  GIT_CONFIG_SYSTEM: "/dev/null",
@@ -14082,7 +14368,7 @@ async function createHostedBotRunner(host) {
14082
14368
  signal: AbortSignal.any([lifetime.signal, ...config.signal ? [config.signal] : []])
14083
14369
  });
14084
14370
  child.stdin.write(
14085
- JSON.stringify({ kind, args: config.args, cwd, env: task.env, publicEgress: Boolean(egress), ...options.github ? { githubBin: join27(runDir, "github", "bin") } : {} }) + "\n"
14371
+ JSON.stringify({ kind, args: config.args, cwd, env: task.env, publicEgress: Boolean(egress), ...options.github ? { githubBin: join31(runDir, "github", "bin") } : {} }) + "\n"
14086
14372
  );
14087
14373
  return child;
14088
14374
  };
@@ -14217,7 +14503,7 @@ import {
14217
14503
  createHash as createHash7,
14218
14504
  timingSafeEqual as timingSafeEqual2
14219
14505
  } from "node:crypto";
14220
- import { chmodSync as chmodSync3 } from "node:fs";
14506
+ import { chmodSync as chmodSync4 } from "node:fs";
14221
14507
  import { createServer as createServer4 } from "node:net";
14222
14508
 
14223
14509
  // src/computer/driver.mjs
@@ -14467,7 +14753,7 @@ var ComputerBroker = class {
14467
14753
  else this.server.listen(this.socketPath, resolve12);
14468
14754
  });
14469
14755
  if (this.transport === "tcp") this.socketPath = `tcp://127.0.0.1:${this.server.address().port}`;
14470
- else chmodSync3(this.socketPath, 384);
14756
+ else chmodSync4(this.socketPath, 384);
14471
14757
  }
14472
14758
  request(token, value, signal) {
14473
14759
  const grant = this.grants.get(token);
@@ -14691,6 +14977,11 @@ function loadDeviceFile(raw) {
14691
14977
  }
14692
14978
  return file;
14693
14979
  }
14980
+ var HARNESS_RESCAN_MS = 5 * 6e4;
14981
+ var SANDBOX_REPAIR_RETRY_MS = 60 * 6e4;
14982
+ function missingToolsUnchanged(issue) {
14983
+ return !issue?.missing.length || missingSandboxTools().length === issue.missing.length;
14984
+ }
14694
14985
  var Daemon = class {
14695
14986
  constructor(options) {
14696
14987
  this.options = options;
@@ -14762,8 +15053,8 @@ var Daemon = class {
14762
15053
  }
14763
15054
  async startInternal() {
14764
15055
  const { paths } = this;
14765
- mkdirSync16(paths.dataDir, { recursive: true, mode: 448 });
14766
- if (process.platform !== "win32") chmodSync4(paths.dataDir, 448);
15056
+ mkdirSync17(paths.dataDir, { recursive: true, mode: 448 });
15057
+ if (process.platform !== "win32") chmodSync5(paths.dataDir, 448);
14767
15058
  this.releaseLock = await acquireLock(
14768
15059
  paths.dataDir,
14769
15060
  paths.lock,
@@ -14776,7 +15067,7 @@ var Daemon = class {
14776
15067
  if (this.options.credential)
14777
15068
  this.saveDevice({ credential: this.options.credential });
14778
15069
  this.journal = new RunJournal(paths.journal);
14779
- this.captureUploader = new CaptureUploader((runId) => join28(runPaths(paths.adapterState, runId).runDir, CAPTURES_DIR), this.options.link, this.log, join28(paths.dataDir, "capture-pending"));
15070
+ this.captureUploader = new CaptureUploader((runId) => join32(runPaths(paths.adapterState, runId).runDir, CAPTURES_DIR), this.options.link, this.log, join32(paths.dataDir, "capture-pending"));
14780
15071
  this.outbox = new Outbox(
14781
15072
  paths.outbox,
14782
15073
  this.options.link,
@@ -14793,7 +15084,7 @@ var Daemon = class {
14793
15084
  if (this.localComputerDriver && this.options.link.syncComputerReviews && this.options.link.localComputerAuthority) {
14794
15085
  this.localComputerBroker = new ComputerBroker({
14795
15086
  driver: this.localComputerDriver,
14796
- socketPath: join28(paths.dataDir, "local-computer.sock"),
15087
+ socketPath: join32(paths.dataDir, "local-computer.sock"),
14797
15088
  transport: "tcp",
14798
15089
  permitted: (runId) => {
14799
15090
  const assignment = this.journal.get(runId)?.assignment;
@@ -14835,7 +15126,7 @@ var Daemon = class {
14835
15126
  };
14836
15127
  this.cloudComputerBroker = new ComputerBroker({
14837
15128
  driver,
14838
- socketPath: join28(paths.dataDir, "computer.sock"),
15129
+ socketPath: join32(paths.dataDir, "computer.sock"),
14839
15130
  permitted: (runId) => {
14840
15131
  const entry = this.journal.get(runId);
14841
15132
  return Boolean(!this.stopped && this.registered && entry?.assignment.computer && this.supervisor?.computerRunAllowed(runId));
@@ -14958,7 +15249,7 @@ var Daemon = class {
14958
15249
  worktree,
14959
15250
  tree,
14960
15251
  github: { ...assignment.github, runToken: assignment.mcp.token, issueId: assignment.issueId },
14961
- privateDir: join28(paths.adapterState, "pids"),
15252
+ privateDir: join32(paths.adapterState, "pids"),
14962
15253
  ...hosted ? { hosted: true } : {},
14963
15254
  env: githubReceiptEnvironment()
14964
15255
  }) : publishRun({
@@ -14999,17 +15290,16 @@ var Daemon = class {
14999
15290
  if (this.stopped) throw new Error("daemon stopped during startup");
15000
15291
  const detected = hosted ? hosted.harnesses.map((harness) => ({ harness, installed: true, version: null, subscriptionBroker: true, ...this.cloudComputerBroker ? { computerBroker: true } : {} })) : this.options.harnesses ?? await detectHarnesses();
15001
15292
  if (this.stopped) throw new Error("daemon stopped during startup");
15002
- const runApprovalTransport = typeof this.options.link.prepareRunToolApproval === "function";
15003
- const localIsolation = this.ownedSandbox !== null && this.sandbox === this.ownedSandbox;
15004
- this.harnesses = detected.filter((info) => this.adapters.has(info.harness)).map((info) => ({
15293
+ if (!hosted && !this.options.harnesses) this.harnessPrint = harnessFingerprint();
15294
+ this.harnesses = [...detected.filter((info) => this.adapters.has(info.harness)).map((info) => ({
15005
15295
  ...info,
15006
15296
  // Local enforcement requires the successful owned SRT probe. Hosted
15007
15297
  // enforcement uses its existing network-none/read-only Docker boundary
15008
15298
  // and gate-bound broker/container teardown; general egress stays outside
15009
15299
  // that ceiling. Codex also verifies its pinned interface at initialize.
15010
- toolApproval: runApprovalTransport && (hosted || localIsolation && (info.harness === "claude_code" || info.harness === "codex" && /(?:^|[ /])0\.163\.0(?:$|[ ();])/.test(info.version ?? ""))) ? true : void 0,
15300
+ toolApproval: this.supportsToolApproval(info),
15011
15301
  coordinator: hosted ? false : coordinatorCapable(info)
15012
- }));
15302
+ })), ...this.sandboxBlockedHarnesses(detected)];
15013
15303
  await this.supervisor.resumeAll();
15014
15304
  if (!hosted && this.options.quotaSource !== null) {
15015
15305
  this.quota = new QuotaPoller(
@@ -15193,6 +15483,58 @@ var Daemon = class {
15193
15483
  }
15194
15484
  return acted;
15195
15485
  }
15486
+ supportsToolApproval(info) {
15487
+ if (typeof this.options.link.prepareRunToolApproval !== "function") return void 0;
15488
+ if (this.options.hostedSubscriptions) return true;
15489
+ if (this.ownedSandbox === null || this.sandbox !== this.ownedSandbox) return void 0;
15490
+ return info.harness === "claude_code" || info.harness === "codex" && codexSupportsRunApproval(info.version) ? true : void 0;
15491
+ }
15492
+ /** Report a CLI the daemon cannot run as present but not installed, so no run is pinned here. */
15493
+ sandboxBlockedHarnesses(detected) {
15494
+ const issue = this.sandboxIssue;
15495
+ if (!issue) return [];
15496
+ return detected.filter((info) => info.installed && !this.adapters.has(info.harness)).map((info) => ({
15497
+ harness: info.harness,
15498
+ installed: false,
15499
+ version: info.version,
15500
+ sandboxBlocked: { missing: issue.missing }
15501
+ }));
15502
+ }
15503
+ async redetectHarnesses() {
15504
+ const repairing = this.sandboxIssue?.missing.length;
15505
+ if (this.sandboxIssue && repairing) {
15506
+ this.lastSandboxRepair = Date.now();
15507
+ this.sandboxIssue = { missing: await ensureSandboxTools({ dataDir: this.paths.dataDir, log: this.log }) };
15508
+ }
15509
+ const detected = await detectHarnesses();
15510
+ this.harnessPrint = harnessFingerprint();
15511
+ this.harnesses = [...detected.filter((info) => this.adapters.has(info.harness)).map((info) => {
15512
+ return { ...info, toolApproval: this.supportsToolApproval(info), coordinator: coordinatorCapable(info) };
15513
+ }), ...this.sandboxBlockedHarnesses(detected)];
15514
+ if (repairing && this.sandboxIssue?.missing.length === 0) {
15515
+ this.startMaintenance(() => restartService(false, () => {
15516
+ }, void 0, () => !this.stopped));
15517
+ }
15518
+ }
15519
+ /** Notice an agent installed after startup, or retry a failed tool download, without spawning anything per tick. */
15520
+ rescanHarnesses() {
15521
+ const now = Date.now();
15522
+ if (this.options.hostedSubscriptions || this.options.harnesses || this.rescanning || now - this.lastHarnessScan < HARNESS_RESCAN_MS) return;
15523
+ this.lastHarnessScan = now;
15524
+ const retryRepair = Boolean(this.sandboxIssue?.missing.length) && now - this.lastSandboxRepair >= SANDBOX_REPAIR_RETRY_MS;
15525
+ if (harnessFingerprint() === this.harnessPrint && !retryRepair && missingToolsUnchanged(this.sandboxIssue)) return;
15526
+ this.rescanning = true;
15527
+ void this.redetectHarnesses().then(() => this.syncWithServer()).catch((err) => {
15528
+ this.log.warn({ err }, "harness rescan failed");
15529
+ }).finally(() => {
15530
+ this.rescanning = false;
15531
+ });
15532
+ }
15533
+ harnessPrint = "";
15534
+ lastHarnessScan = Date.now();
15535
+ lastSandboxRepair = Date.now();
15536
+ rescanning = false;
15537
+ sandboxIssue = null;
15196
15538
  sandbox = null;
15197
15539
  ownedSandbox = null;
15198
15540
  /** The agent sandbox (one per daemon), or null where there is none. */
@@ -15201,8 +15543,10 @@ var Daemon = class {
15201
15543
  }
15202
15544
  async createAgentSandbox() {
15203
15545
  if (this.options.agentSandbox !== void 0) return this.options.agentSandbox;
15546
+ const missing = await ensureSandboxTools({ dataDir: this.paths.dataDir, log: this.log });
15204
15547
  if (!SandboxRuntime.supported()) {
15205
- this.log.warn("agent sandbox unavailable: platform or dependencies unsupported");
15548
+ this.log.warn({ missing }, "agent sandbox unavailable: platform or dependencies unsupported");
15549
+ this.sandboxIssue = { missing };
15206
15550
  return null;
15207
15551
  }
15208
15552
  const sandbox = new SandboxRuntime(async ({ host }) => {
@@ -15219,6 +15563,7 @@ var Daemon = class {
15219
15563
  return sandbox;
15220
15564
  } catch (err) {
15221
15565
  this.log.warn({ err }, "agent sandbox unavailable");
15566
+ this.sandboxIssue = { missing: [] };
15222
15567
  if (!this.options.retainResources) await sandbox.reset().catch((resetError) => {
15223
15568
  this.log.warn({ err: resetError }, "agent sandbox cleanup failed");
15224
15569
  });
@@ -15242,11 +15587,11 @@ var Daemon = class {
15242
15587
  return runVerify(sandbox, {
15243
15588
  command,
15244
15589
  worktree: realpathSync13(worktree),
15245
- tmpRoot: join28(this.paths.adapterState, "verify", runId),
15590
+ tmpRoot: join32(this.paths.adapterState, "verify", runId),
15246
15591
  privateDir: paths.privateDir,
15247
15592
  pidFile: paths.pidFile.replace(/\.pgid$/, ".verify.pgid"),
15248
15593
  networkAllow: repo.networkAllow,
15249
- env: { ...buildChildEnv(), MIKKA_HANDOFF_FILE: join28(paths.runDir, "handoff.json") },
15594
+ env: { ...buildChildEnv(), MIKKA_HANDOFF_FILE: join32(paths.runDir, "handoff.json") },
15250
15595
  timeoutMs,
15251
15596
  signal,
15252
15597
  readOnly
@@ -15291,8 +15636,8 @@ var Daemon = class {
15291
15636
  void this.stop().finally(() => this.options.onLockLost?.(error));
15292
15637
  }
15293
15638
  ensureSecret() {
15294
- if (existsSync7(this.paths.secret)) {
15295
- return readFileSync11(this.paths.secret, "utf8").trim();
15639
+ if (existsSync8(this.paths.secret)) {
15640
+ return readFileSync12(this.paths.secret, "utf8").trim();
15296
15641
  }
15297
15642
  const secret = randomBytes3(32).toString("hex");
15298
15643
  writeFileAtomic(this.paths.secret, `${secret}
@@ -15389,6 +15734,7 @@ var Daemon = class {
15389
15734
  this.revokeLocalComputerGrants();
15390
15735
  this.registered = false;
15391
15736
  }
15737
+ this.rescanHarnesses();
15392
15738
  const command = deviceCommands?.[0];
15393
15739
  if (command?.kind === "uninstall") {
15394
15740
  this.draining = true;
@@ -15402,7 +15748,7 @@ var Daemon = class {
15402
15748
  setImmediate(() => {
15403
15749
  void this.stop().then(() => uninstallService(() => {
15404
15750
  this.saveDevice({ credential: void 0 });
15405
- rmSync17(this.paths.secret, { force: true });
15751
+ rmSync18(this.paths.secret, { force: true });
15406
15752
  })).catch(() => {
15407
15753
  this.options.onDrained?.();
15408
15754
  });
@@ -15414,11 +15760,7 @@ var Daemon = class {
15414
15760
  if (command.kind === "redetect" || this.maintenanceIdle()) {
15415
15761
  try {
15416
15762
  if (command.kind === "redetect") {
15417
- const detected = await detectHarnesses();
15418
- this.harnesses = detected.filter((info) => this.adapters.has(info.harness)).map((info) => {
15419
- const previous = this.harnesses.find((old) => old.harness === info.harness && old.version === info.version);
15420
- return { ...info, toolApproval: previous?.toolApproval, coordinator: coordinatorCapable(info) };
15421
- });
15763
+ await this.redetectHarnesses();
15422
15764
  this.saveDevice({ completedDeviceCommands: [command.id], failedDeviceCommands: [] });
15423
15765
  void this.syncWithServer().catch(() => void 0);
15424
15766
  } else {
@@ -15731,7 +16073,7 @@ var Daemon = class {
15731
16073
  if (!job || controller.signal.aborted) return;
15732
16074
  await runNativeCoordinatorJob(job, {
15733
16075
  deviceId,
15734
- stateDir: join28(this.paths.dataDir, "coordinator"),
16076
+ stateDir: join32(this.paths.dataDir, "coordinator"),
15735
16077
  link,
15736
16078
  signal: controller.signal,
15737
16079
  sandbox,
@@ -15774,7 +16116,7 @@ var Daemon = class {
15774
16116
  }, void 0, () => !this.stopped);
15775
16117
  });
15776
16118
  } else if (!this.updateCheck && !this.updateVersion) {
15777
- this.updateCheck = checkLatestVersion(join28(this.paths.dataDir, "auto-update.json"), DAEMON_VERSION, runtime).then((version) => {
16119
+ this.updateCheck = checkLatestVersion(join32(this.paths.dataDir, "auto-update.json"), DAEMON_VERSION, runtime).then((version) => {
15778
16120
  this.updateVersion = version;
15779
16121
  }).catch(() => {
15780
16122
  this.log.warn("Daemon automatic update check failed");
@@ -15832,7 +16174,7 @@ async function startDaemon(options) {
15832
16174
  }
15833
16175
 
15834
16176
  // src/rpc/client.ts
15835
- import { readFileSync as readFileSync12 } from "node:fs";
16177
+ import { readFileSync as readFileSync13 } from "node:fs";
15836
16178
  import { connect as connect2 } from "node:net";
15837
16179
  import {
15838
16180
  createMessageConnection as createMessageConnection3,
@@ -15912,7 +16254,7 @@ function connectDaemonInDataDir(dataDir, client, extra = {}) {
15912
16254
  const paths = daemonPaths(dataDir);
15913
16255
  let secret;
15914
16256
  try {
15915
- secret = readFileSync12(paths.secret, "utf8").trim();
16257
+ secret = readFileSync13(paths.secret, "utf8").trim();
15916
16258
  } catch (error) {
15917
16259
  return Promise.reject(error);
15918
16260
  }