windows-development-sandbox 1.27.1 → 1.28.0

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.
Files changed (121) hide show
  1. package/dist/app/context.d.ts +5 -0
  2. package/dist/app/context.js +13 -0
  3. package/dist/app/features/accounts/accounts.def.d.ts +16 -0
  4. package/dist/app/features/accounts/accounts.run.d.ts +4 -0
  5. package/dist/app/features/accounts/services.def.d.ts +15 -1
  6. package/dist/app/features/accounts/services.def.js +67 -31
  7. package/dist/app/features/accounts/services.run.d.ts +7 -2
  8. package/dist/app/features/accounts/services.run.js +25 -6
  9. package/dist/app/features/defs.d.ts +17 -0
  10. package/dist/app/registry.d.ts +1 -1
  11. package/dist/app/registry.js +15 -2
  12. package/dist/bin/agent-sandbox.js +2 -2
  13. package/dist/bin/{chunk-RSIX7C6D.js → chunk-CB44HTTN.js} +444 -175
  14. package/dist/bin/{chunk-4TLSWPG7.js → chunk-UZ5S3EMB.js} +1 -1
  15. package/dist/bin/{chunk-QUHNXHVG.js → chunk-ZMZI3625.js} +46 -41
  16. package/dist/bin/claude-host.js +2 -2
  17. package/dist/bin/claude-sbx.js +2 -2
  18. package/dist/bin/codex-sbx.js +2 -2
  19. package/dist/bin/gemini-sbx.js +2 -2
  20. package/dist/bin/sandbox.js +2 -2
  21. package/dist/bin/sbx-hide.js +2 -2
  22. package/dist/bin/sbx-show.js +2 -2
  23. package/dist/cli/menu/menu.js +6 -1
  24. package/dist/cli/menu/pages/accounts-page.js +9 -5
  25. package/dist/core/schema/sandbox.js +1 -1
  26. package/dist/guest/apps/app-installer.js +7 -5
  27. package/dist/guest/apps/comet-unpack.js +2 -2
  28. package/dist/guest/daemon/heartbeat.js +1 -0
  29. package/dist/guest/governor/core.d.ts +53 -1
  30. package/dist/guest/governor/core.js +95 -1
  31. package/dist/guest/governor/decide.d.ts +4 -0
  32. package/dist/guest/governor/decide.js +2 -0
  33. package/dist/guest/governor/governor.js +23 -9
  34. package/dist/guest/runtime/pools.d.ts +4 -4
  35. package/dist/guest/runtime/pools.js +4 -15
  36. package/dist/guest/runtime/process.d.ts +5 -2
  37. package/dist/guest/runtime/process.js +23 -3
  38. package/dist/guest/toolchains/engines/epic-launcher.d.ts +6 -0
  39. package/dist/guest/toolchains/engines/epic-launcher.js +19 -1
  40. package/dist/guest/toolchains/engines/unreal-engines.js +2 -1
  41. package/dist/guest/toolchains/installers.d.ts +3 -3
  42. package/dist/guest/toolchains/installers.js +5 -5
  43. package/dist/guest/toolchains/native/unreal.js +22 -3
  44. package/dist/guest/toolchains/native/vcruntime.js +2 -2
  45. package/dist/guest/toolchains/native/vs-persist.d.ts +2 -2
  46. package/dist/guest/toolchains/runtimes.js +7 -6
  47. package/dist/guest/toolchains/signature.d.ts +2 -0
  48. package/dist/guest/toolchains/signature.js +9 -2
  49. package/dist/guest/toolchains/step-scheduler.js +1 -1
  50. package/dist/host/accounts/deliver.js +15 -7
  51. package/dist/host/life/crash-report.d.ts +4 -0
  52. package/dist/host/life/crash-report.js +30 -1
  53. package/dist/host/life/last-end.js +1 -0
  54. package/dist/host/pc-installs/discover.js +5 -2
  55. package/dist/host/pc-installs/unreal.d.ts +3 -1
  56. package/dist/host/pc-installs/unreal.js +27 -1
  57. package/dist/host/tools/windows-terminal.d.ts +4 -1
  58. package/dist/host/tools/windows-terminal.js +25 -9
  59. package/dist/runtime/agent.js +39 -23
  60. package/dist/runtime/daemon.js +279 -121
  61. package/dist/runtime/db.js +37 -17
  62. package/dist/runtime/main.js +340 -109
  63. package/dist/runtime/netproxy.js +31 -13
  64. package/dist/runtime/tool.js +37 -16
  65. package/dist/runtime/view.js +150 -38
  66. package/dist/selftest/agent-paths.test.js +3 -1
  67. package/dist/selftest/app.test.js +20 -2
  68. package/dist/selftest/boot-runner.test.js +15 -1
  69. package/dist/selftest/channel.test.js +33 -0
  70. package/dist/selftest/engines.test.js +22 -1
  71. package/dist/selftest/heavy-load.test.js +116 -4
  72. package/dist/selftest/live-terminal.test.js +103 -0
  73. package/dist/selftest/menu-screens.js +52 -1
  74. package/dist/selftest/menu.test.js +29 -3
  75. package/dist/selftest/personalization.test.js +2 -1
  76. package/dist/selftest/service-accounts.test.js +77 -2
  77. package/dist/selftest/support/engine-golden.js +2 -0
  78. package/dist/selftest/ui-text.test.js +4 -0
  79. package/dist/shared/accounts/kinds.js +9 -9
  80. package/dist/shared/apps/catalogue-gamedev.js +1 -0
  81. package/dist/shared/apps/catalogue.d.ts +2 -0
  82. package/dist/shared/channel/schema.d.ts +2 -0
  83. package/dist/shared/channel/schema.js +4 -0
  84. package/dist/shared/concurrency/pools.js +1 -1
  85. package/dist/shared/download/archive.js +4 -2
  86. package/dist/shared/download/index.d.ts +1 -1
  87. package/dist/shared/download/index.js +1 -1
  88. package/dist/shared/download/spawn.d.ts +7 -1
  89. package/dist/shared/download/spawn.js +16 -1
  90. package/dist/shared/memory/index.d.ts +1 -0
  91. package/dist/shared/memory/index.js +1 -0
  92. package/dist/shared/memory/policy.d.ts +12 -0
  93. package/dist/shared/memory/policy.js +4 -0
  94. package/dist/shared/memory/unreal-build.d.ts +2 -0
  95. package/dist/shared/memory/unreal-build.js +21 -0
  96. package/dist/shared/paths/pc-installs.d.ts +8 -0
  97. package/dist/shared/pty/clipboard-watch.d.ts +4 -0
  98. package/dist/shared/pty/clipboard-watch.js +40 -0
  99. package/dist/shared/pty/ctrl-c.d.ts +13 -7
  100. package/dist/shared/pty/ctrl-c.js +35 -19
  101. package/dist/shared/pty/index.d.ts +1 -0
  102. package/dist/shared/pty/index.js +1 -0
  103. package/dist/shared/pty/terminal.d.ts +1 -1
  104. package/dist/shared/pty/terminal.js +16 -13
  105. package/dist/shared/unreal/launcher-files.d.ts +7 -0
  106. package/dist/shared/unreal/launcher-files.js +32 -1
  107. package/dist/shared/web/ctrl-c.d.ts +26 -0
  108. package/dist/shared/web/ctrl-c.js +64 -0
  109. package/dist/shared/web/index.d.ts +1 -0
  110. package/dist/shared/web/index.js +1 -0
  111. package/dist/web/index.html +1 -1
  112. package/dist/web/{main-VPMYTSUL.js → main-7NY4X6YU.js} +67 -67
  113. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  114. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  115. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  116. package/package.json +1 -1
  117. package/sandbox/csharp/ClipboardWatch.cs +59 -0
  118. package/sandbox/csharp/MemGov.cs +752 -7
  119. package/sandbox/scripts/watch-clipboard.ps1 +8 -0
  120. package/schemas/config.schema.json +10 -10
  121. package/schemas/sandbox-config.schema.json +9 -9
@@ -25521,7 +25521,7 @@ var SandboxSchema = external_exports.strictObject({
25521
25521
  'The last resort that keeps the sandbox up, in MB below its commit limit (RAM plus paging file): builds are slowed down long before (sandbox.pressureResponse); only if everything the agents start together would take the sandbox to the very edge of its memory, where Windows inside it would start failing, is a build refused memory instead of Windows. "auto" = the larger of 1536 MB and 3 % of the commit limit; "none" = no backstop. Yours only: a project file cannot change it.'
25522
25522
  ),
25523
25523
  pressureResponse: external_exports.boolean().describe(
25524
- "When the sandbox (or this PC) runs low on memory, slow builds down in steps, like a PC that gets slower instead of failing: build programs get lower CPU and memory priority, then less CPU, then compilers take turns (the waiting ones briefly suspended and trimmed to the paging file). Nothing is stopped or killed, and the agents themselves are never touched. Yours only: a project file cannot change it."
25524
+ "When the sandbox (or this PC) runs low on memory, slow builds down in steps, like a PC that gets slower instead of failing: build programs get lower CPU and memory priority, then less CPU, then compilers take turns (the waiting ones briefly suspended and trimmed to the paging file). Nothing is stopped or killed, and the agents themselves are never touched. A memory gate also holds new compilers and linkers for a moment when the memory of the build programs grows faster than 4 GB in 3 seconds (3 GB with vGPU), and lets them go one at a time. Yours only: a project file cannot change it."
25525
25525
  ),
25526
25526
  vGpu: external_exports.boolean().describe(
25527
25527
  "Virtual GPU. Off by default: Microsoft notes it widens the sandbox attack surface. The sandbox then uses this PC's graphics card, and with some graphics drivers heavy use can freeze the whole PC: turn it on only when a program in the sandbox needs the GPU (the mesa toolchain gives software OpenGL without it). Browsers in the sandbox never use it (they draw in software)."
@@ -26113,6 +26113,7 @@ var GAMEDEV_APPS = [
26113
26113
  size: "100 MB",
26114
26114
  source: { github: "f3d-app/f3d", asset: "^F3D-[\\d.]+-Windows-x86_64\\.zip$" },
26115
26115
  exe: "f3d.exe",
26116
+ onPath: true,
26116
26117
  needs: ["mesa"]
26117
26118
  },
26118
26119
  {
@@ -27413,7 +27414,7 @@ var SERVICE_SPECS = {
27413
27414
  },
27414
27415
  steam: {
27415
27416
  kind: "steam",
27416
- label: "Steam (SteamCMD)",
27417
+ label: "Steam",
27417
27418
  usedBy: ["steam"],
27418
27419
  sources: ["file", "sandbox"],
27419
27420
  singleHolder: true,
@@ -27449,7 +27450,7 @@ var SERVICE_SPECS = {
27449
27450
  },
27450
27451
  cfx: {
27451
27452
  kind: "cfx",
27452
- label: "Cfx.re (FiveM / RedM server key)",
27453
+ label: "FiveM key",
27453
27454
  usedBy: ["fivem"],
27454
27455
  sources: ["typed", "file"],
27455
27456
  singleHolder: false,
@@ -27478,7 +27479,7 @@ var SERVICE_SPECS = {
27478
27479
  },
27479
27480
  keystore: {
27480
27481
  kind: "keystore",
27481
- label: "Android upload keystore",
27482
+ label: "Android keystore",
27482
27483
  usedBy: ["android"],
27483
27484
  sources: ["file"],
27484
27485
  singleHolder: false,
@@ -27493,7 +27494,7 @@ var SERVICE_SPECS = {
27493
27494
  },
27494
27495
  play: {
27495
27496
  kind: "play",
27496
- label: "Google Play (service account)",
27497
+ label: "Google Play",
27497
27498
  usedBy: ["android"],
27498
27499
  sources: ["file"],
27499
27500
  singleHolder: false,
@@ -27504,7 +27505,7 @@ var SERVICE_SPECS = {
27504
27505
  },
27505
27506
  expo: {
27506
27507
  kind: "expo",
27507
- label: "Expo (EAS)",
27508
+ label: "Expo",
27508
27509
  usedBy: ["reactNative"],
27509
27510
  sources: ["typed"],
27510
27511
  singleHolder: false,
@@ -27515,7 +27516,7 @@ var SERVICE_SPECS = {
27515
27516
  },
27516
27517
  appstore: {
27517
27518
  kind: "appstore",
27518
- label: "App Store Connect API key",
27519
+ label: "App Store Connect",
27519
27520
  usedBy: ["reactNative", "flutter"],
27520
27521
  sources: ["file", "typed"],
27521
27522
  singleHolder: false,
@@ -27529,7 +27530,7 @@ var SERVICE_SPECS = {
27529
27530
  },
27530
27531
  perplexity: {
27531
27532
  kind: "perplexity",
27532
- label: "Perplexity API key",
27533
+ label: "Perplexity API",
27533
27534
  usedBy: ["perplexity"],
27534
27535
  sources: ["pc", "file", "typed"],
27535
27536
  singleHolder: false,
@@ -27553,7 +27554,7 @@ var SERVICE_SPECS = {
27553
27554
  },
27554
27555
  comet: {
27555
27556
  kind: "comet",
27556
- label: "Comet (Perplexity browser)",
27557
+ label: "Comet",
27557
27558
  usedBy: ["comet"],
27558
27559
  sources: ["sandbox"],
27559
27560
  singleHolder: false,
@@ -27569,7 +27570,7 @@ var SERVICE_SPECS = {
27569
27570
  },
27570
27571
  perplexityapp: {
27571
27572
  kind: "perplexityapp",
27572
- label: "Perplexity desktop app",
27573
+ label: "Perplexity app",
27573
27574
  usedBy: ["perplexityapp"],
27574
27575
  sources: ["sandbox"],
27575
27576
  singleHolder: false,
@@ -27921,6 +27922,8 @@ var PressureStatusSchema = external_exports.object({
27921
27922
  lowered: external_exports.number().int().min(0).max(1024),
27922
27923
  /** Compilers waiting their turn right now (name, process id, seconds until they may go on). */
27923
27924
  paused: external_exports.array(external_exports.object({ name: Text(64), pid: external_exports.number().int(), seconds: external_exports.number().int().min(0).max(60) })).max(8),
27925
+ /** Build programs the memory gate holds right now (let go one at a time). */
27926
+ held: external_exports.number().int().min(0).max(1024).optional(),
27924
27927
  /** The CPU share the compilers get together (percent; 100 = no cap). */
27925
27928
  cpuPercent: external_exports.number().int().min(0).max(100).optional(),
27926
27929
  /** The PC itself asked for slowing down (its memory or CPU is low). */
@@ -27941,6 +27944,8 @@ var HeartbeatSchema = external_exports.object({
27941
27944
  topProcesses: external_exports.array(external_exports.object({ name: Text(64), privateMb: external_exports.number().int().min(0).max(1e7) })).max(5),
27942
27945
  /** The disk queue this PC last reported while asking the sandbox to slow down. */
27943
27946
  diskQueueReported: external_exports.number().min(0).max(1e5).optional(),
27947
+ /** Build programs the memory gate holds right now (let go one at a time). */
27948
+ held: external_exports.number().int().min(0).max(1024).optional(),
27944
27949
  /** "ending": the sandbox is shutting down in order (idle shutdown, sandbox stop, Windows shutting down). */
27945
27950
  phase: external_exports.enum(["ready", "working", "ending"])
27946
27951
  });
@@ -28531,10 +28536,20 @@ function scriptArgs(name, args = []) {
28531
28536
  }
28532
28537
 
28533
28538
  // src/shared/download/spawn.ts
28539
+ import os3 from "node:os";
28534
28540
  import { spawn } from "node:child_process";
28541
+ function lowerPriority(pid) {
28542
+ if (pid === void 0) return;
28543
+ try {
28544
+ os3.setPriority(pid, os3.constants.priority.PRIORITY_BELOW_NORMAL);
28545
+ } catch {
28546
+ }
28547
+ }
28535
28548
  function spawnAsync(file3, args, options = {}) {
28549
+ const { lowPriority, ...spawnOptions } = options;
28536
28550
  return new Promise((resolve2, reject) => {
28537
- const child = spawn(file3, args, { stdio: "ignore", windowsHide: true, ...options });
28551
+ const child = spawn(file3, args, { stdio: "ignore", windowsHide: true, ...spawnOptions });
28552
+ if (lowPriority) lowerPriority(child.pid);
28538
28553
  child.once("error", reject);
28539
28554
  child.once("close", (code) => resolve2(code ?? -1));
28540
28555
  });
@@ -28558,13 +28573,16 @@ function extractArchive(archive, destination, members2 = []) {
28558
28573
  }
28559
28574
  async function extract(archive, destination, members2) {
28560
28575
  fs10.mkdirSync(destination, { recursive: true });
28561
- let status = await spawnAsync(SYSTEM.tar(), ["-xf", archive, "-C", destination, ...members2]);
28576
+ let status = await spawnAsync(SYSTEM.tar(), ["-xf", archive, "-C", destination, ...members2], {
28577
+ lowPriority: true
28578
+ });
28562
28579
  if (status !== 0 && /\.(zip|nupkg)$/i.test(archive)) {
28563
28580
  const zip = archive.endsWith(".zip") ? archive : archive + ".zip";
28564
28581
  if (zip !== archive) fs10.copyFileSync(archive, zip);
28565
28582
  status = await spawnAsync(
28566
28583
  SYSTEM.powershell(),
28567
- scriptArgs("expand-archive.ps1", ["-Archive", zip, "-Destination", destination])
28584
+ scriptArgs("expand-archive.ps1", ["-Archive", zip, "-Destination", destination]),
28585
+ { lowPriority: true }
28568
28586
  );
28569
28587
  }
28570
28588
  if (status !== 0) throw new Error("Could not extract " + archive);
@@ -29160,7 +29178,6 @@ function withRequestSecrets(req) {
29160
29178
  }
29161
29179
 
29162
29180
  // src/guest/runtime/pools.ts
29163
- import os4 from "node:os";
29164
29181
  import { spawn as spawn3 } from "node:child_process";
29165
29182
  import path21 from "node:path";
29166
29183
 
@@ -29234,13 +29251,13 @@ function installerRetry(code, attempt) {
29234
29251
  }
29235
29252
 
29236
29253
  // src/shared/concurrency/pools.ts
29237
- var net2 = new Limiter(4, "net");
29254
+ var net2 = new Limiter(3, "net");
29238
29255
  var heavy = new Limiter(2, "heavy");
29239
29256
  var installer = new Limiter(1, "installer");
29240
29257
 
29241
29258
  // src/guest/runtime/process.ts
29242
29259
  import fs16 from "node:fs";
29243
- import os3 from "node:os";
29260
+ import os4 from "node:os";
29244
29261
  import path20 from "node:path";
29245
29262
  import { spawn as spawn2, spawnSync as spawnSync2 } from "node:child_process";
29246
29263
  var outputSink = null;
@@ -29262,13 +29279,15 @@ function logLines(stream) {
29262
29279
  stream.on("end", () => logOutput(partial2));
29263
29280
  }
29264
29281
  async function execAsync(file3, args, options = {}) {
29282
+ const { lowPriority, ...spawnOptions } = options;
29265
29283
  const code = captureOutput() ? await new Promise((resolve2, reject) => {
29266
- const child = spawn2(file3, args, { stdio: ["inherit", "pipe", "pipe"], windowsHide: true, ...options });
29284
+ const child = spawn2(file3, args, { stdio: ["inherit", "pipe", "pipe"], windowsHide: true, ...spawnOptions });
29285
+ if (lowPriority) lowerPriority(child.pid);
29267
29286
  logLines(child.stdout);
29268
29287
  logLines(child.stderr);
29269
29288
  child.once("error", reject);
29270
29289
  child.once("close", (c) => resolve2(c ?? -1));
29271
- }) : await spawnAsync(file3, args, { stdio: "inherit", ...options });
29290
+ }) : await spawnAsync(file3, args, { stdio: "inherit", ...spawnOptions, ...lowPriority ? { lowPriority } : {} });
29272
29291
  if (code !== 0)
29273
29292
  throw new Error(path20.basename(file3) + " " + args.slice(0, 3).join(" ") + " failed with exit code " + code);
29274
29293
  }
@@ -29322,8 +29341,23 @@ function runScriptAsync(name, args = []) {
29322
29341
  function scriptOutput(name, args = [], options = {}) {
29323
29342
  return execOutput(SYSTEM.powershell(), scriptArgs(name, args), options);
29324
29343
  }
29344
+ function scriptOutputAsync(name, args = []) {
29345
+ return new Promise((resolve2) => {
29346
+ const child = spawn2(SYSTEM.powershell(), scriptArgs(name, args), {
29347
+ stdio: ["ignore", "pipe", "ignore"],
29348
+ windowsHide: true
29349
+ });
29350
+ let out = "";
29351
+ child.stdout?.setEncoding("utf8");
29352
+ child.stdout?.on("data", (chunk) => {
29353
+ if (out.length < CAPTURE_BYTES) out += chunk;
29354
+ });
29355
+ child.once("error", () => resolve2(""));
29356
+ child.once("close", (code) => resolve2(code === 0 ? out.trim() : ""));
29357
+ });
29358
+ }
29325
29359
  function runScriptWithJson(name, param, data, args = []) {
29326
- const dir = fs16.mkdtempSync(path20.join(os3.tmpdir(), "sbx-"));
29360
+ const dir = fs16.mkdtempSync(path20.join(os4.tmpdir(), "sbx-"));
29327
29361
  const file3 = path20.join(dir, "input.json");
29328
29362
  try {
29329
29363
  fs16.writeFileSync(file3, JSON.stringify(data), "utf8");
@@ -29333,7 +29367,7 @@ function runScriptWithJson(name, param, data, args = []) {
29333
29367
  }
29334
29368
  }
29335
29369
  async function runScriptWithJsonAsync(name, param, data, args = []) {
29336
- const dir = fs16.mkdtempSync(path20.join(os3.tmpdir(), "sbx-"));
29370
+ const dir = fs16.mkdtempSync(path20.join(os4.tmpdir(), "sbx-"));
29337
29371
  const file3 = path20.join(dir, "input.json");
29338
29372
  try {
29339
29373
  fs16.writeFileSync(file3, JSON.stringify(data), "utf8");
@@ -29404,14 +29438,7 @@ function installPools() {
29404
29438
  downloader.limit = net2;
29405
29439
  limitExtraction(heavy);
29406
29440
  }
29407
- var BELOW_NORMAL = os4.constants.priority.PRIORITY_BELOW_NORMAL;
29408
- function belowNormal(pid) {
29409
- if (pid === void 0) return;
29410
- try {
29411
- os4.setPriority(pid, BELOW_NORMAL);
29412
- } catch {
29413
- }
29414
- }
29441
+ var belowNormal = lowerPriority;
29415
29442
  async function runInstaller(file3, args, o = {}) {
29416
29443
  for (let attempt = 1; ; attempt++) {
29417
29444
  const code = await installer.run(
@@ -29437,7 +29464,7 @@ async function runInstallerChecked(file3, args, o = {}) {
29437
29464
  throw new Error(path21.basename(file3) + " " + args.slice(0, 3).join(" ") + " failed with exit code " + code);
29438
29465
  }
29439
29466
  function execHeavy(file3, args, options = {}) {
29440
- return heavy.run(() => execAsync(file3, args, options));
29467
+ return heavy.run(() => execAsync(file3, args, { ...options, lowPriority: true }));
29441
29468
  }
29442
29469
 
29443
29470
  // src/guest/daemon/daemon.ts
@@ -30361,13 +30388,14 @@ function itemSizes(saved2) {
30361
30388
  }
30362
30389
  return sizes;
30363
30390
  }
30391
+ var ITEM_NAME = /^[A-Za-z0-9_-]{1,80}\.item$/;
30364
30392
  function isItemUnder(text2, root) {
30365
30393
  const at = itemInstallLocation(text2);
30366
30394
  return at !== null && isWithin2(root, at) && win2.resolve(at) !== win2.resolve(root);
30367
30395
  }
30368
30396
  function itemFiles(dir) {
30369
30397
  try {
30370
- return fs22.readdirSync(dir).filter((f) => /^[A-Za-z0-9_-]{1,80}\.item$/.test(f));
30398
+ return fs22.readdirSync(dir).filter((f) => ITEM_NAME.test(f));
30371
30399
  } catch {
30372
30400
  return [];
30373
30401
  }
@@ -31525,7 +31553,10 @@ import path42 from "node:path";
31525
31553
  // src/guest/toolchains/signature.ts
31526
31554
  var SignatureSchema = external_exports.object({ subject: external_exports.string().max(4096), issuer: external_exports.string().max(4096) });
31527
31555
  function signatureOf(file3) {
31528
- const text2 = scriptOutput("get-signature.ps1", ["-Path", file3]).trim();
31556
+ return parseSignature(scriptOutput("get-signature.ps1", ["-Path", file3]));
31557
+ }
31558
+ function parseSignature(output2) {
31559
+ const text2 = output2.trim();
31529
31560
  if (!text2) return null;
31530
31561
  try {
31531
31562
  const parsed = SignatureSchema.safeParse(JSON.parse(text2));
@@ -31537,11 +31568,14 @@ function signatureOf(file3) {
31537
31568
  function signedBy(file3, signer) {
31538
31569
  return signerMatches(signatureOf(file3), signer);
31539
31570
  }
31571
+ async function signedByAsync(file3, signer) {
31572
+ return signerMatches(parseSignature(await scriptOutputAsync("get-signature.ps1", ["-Path", file3])), signer);
31573
+ }
31540
31574
 
31541
31575
  // src/guest/toolchains/installers.ts
31542
31576
  function signatureCheck(signer, what) {
31543
- return (file3) => {
31544
- if (!signedBy(file3, signer)) throw new Error(what + " is not signed by its vendor");
31577
+ return async (file3) => {
31578
+ if (!await signedByAsync(file3, signer)) throw new Error(what + " is not signed by its vendor");
31545
31579
  };
31546
31580
  }
31547
31581
  async function signedInstaller(url2, file3, what, signer, sha256 = null) {
@@ -31550,7 +31584,7 @@ async function signedInstaller(url2, file3, what, signer, sha256 = null) {
31550
31584
  mkdirp(path42.dirname(file3));
31551
31585
  await downloader.file(url2, file3, { sha256, verify: signatureCheck(signer, path42.basename(file3)) });
31552
31586
  }
31553
- if (!signedBy(file3, signer)) {
31587
+ if (!await signedByAsync(file3, signer)) {
31554
31588
  fs25.rmSync(file3, { force: true });
31555
31589
  throw new Error(
31556
31590
  path42.basename(file3) + " is not signed by its vendor; deleted it (it is downloaded again next boot)"
@@ -31658,6 +31692,18 @@ function savedEntries() {
31658
31692
  function restoreLauncherItems() {
31659
31693
  return restoreItems(SAVED_MANIFESTS, launcherManifestsDir(programData()), ENGINES_DIR, exists);
31660
31694
  }
31695
+ function writePcLauncherItems(items) {
31696
+ const dir = launcherManifestsDir(programData());
31697
+ let written = 0;
31698
+ for (const i of items) {
31699
+ if (i.kind !== "unreal" || !i.launcherItem || !ITEM_NAME.test(i.launcherItem.name)) continue;
31700
+ const at = itemInstallLocation(i.launcherItem.text);
31701
+ if (!at || path44.win32.resolve(at).toLowerCase() !== path44.win32.resolve(i.guest).toLowerCase()) continue;
31702
+ fs26.writeFileSync(path44.join(mkdirp(dir), i.launcherItem.name), i.launcherItem.text, "utf8");
31703
+ written++;
31704
+ }
31705
+ return written;
31706
+ }
31661
31707
  function writeLauncherEntries(entries2) {
31662
31708
  if (!entries2.length) return;
31663
31709
  const file3 = launcherInstalledFile(programData());
@@ -31741,6 +31787,7 @@ async function registerEngines(engines, selectorFrom, ctx, aliases = []) {
31741
31787
  ];
31742
31788
  if (values.length) await runScriptWithJsonAsync("set-registry-values.ps1", "PlanFile", { values });
31743
31789
  try {
31790
+ writePcLauncherItems(ctx.pcInstalls.items);
31744
31791
  writeLauncherEntries(launcherEntries(engines, ctx));
31745
31792
  } catch (e) {
31746
31793
  warn("Unreal: the Epic Games Launcher's LauncherInstalled.dat was left as it is (" + errMsg(e) + ")");
@@ -32066,11 +32113,10 @@ async function setupBun(env) {
32066
32113
  tool("bun", "bin"),
32067
32114
  { size: "35 MB" }
32068
32115
  );
32069
- Object.assign(env.vars, {
32070
- BUN_INSTALL: tool("bun"),
32071
- BUN_INSTALL_CACHE_DIR: path48.join(localDir, "bun-cache"),
32072
- BUN_TMPDIR: path48.join(localDir, "bun-tmp")
32073
- });
32116
+ const cache = path48.join(localDir, "bun-cache");
32117
+ const temp = path48.join(localDir, "bun-tmp");
32118
+ if (!dryRun) [cache, temp].forEach((dir) => mkdirp(dir));
32119
+ Object.assign(env.vars, { BUN_INSTALL: tool("bun"), BUN_INSTALL_CACHE_DIR: cache, BUN_TMPDIR: temp });
32074
32120
  }
32075
32121
  async function setupDeno(env) {
32076
32122
  await setupSingleExe(
@@ -32621,7 +32667,7 @@ async function installFromKept(dest, signer, what) {
32621
32667
  if (!installer2) return null;
32622
32668
  const installed = installedCometExe();
32623
32669
  if (fs35.existsSync(installed)) return installed;
32624
- if (!signedBy(installer2, signer)) {
32670
+ if (!await signedByAsync(installer2, signer)) {
32625
32671
  fs35.rmSync(path54.join(dest, ".agent-sandbox-app"), { force: true });
32626
32672
  fs35.rmSync(installer2, { force: true });
32627
32673
  throw new Error(what + "'s installer is not signed by its vendor; deleted it (it is downloaded again next boot)");
@@ -32923,7 +32969,7 @@ var MARKER = ".agent-sandbox-app";
32923
32969
  function prefetchedFile(entry, fileName) {
32924
32970
  return path58.join(tool("apps", ...entry.id.split("/")) + ".download", fileName.replace(/[\\/:*?"<>|]/g, "_"));
32925
32971
  }
32926
- var PARALLEL = 3;
32972
+ var PARALLEL = 2;
32927
32973
  var AppInstaller = class {
32928
32974
  constructor(env, options, parallel = PARALLEL) {
32929
32975
  this.env = env;
@@ -32944,19 +32990,21 @@ var AppInstaller = class {
32944
32990
  const todo = entries2.filter(
32945
32991
  (e) => !("remote" in e.source) && !("locked" in e.source) && !("store" in e.source) && !("npm" in e.source) && e.id !== "7zip" && (this.options.refresh || !exists(path58.join(tool("apps", ...e.id.split("/")), MARKER)))
32946
32992
  );
32947
- await Promise.all(
32948
- todo.map(async (entry) => {
32949
- try {
32950
- const d = await resolve(entry);
32951
- const file3 = prefetchedFile(entry, d.fileName);
32952
- if (exists(file3)) return;
32953
- await downloader.file(d.url, file3, {
32954
- sha256: d.sha256 ?? null,
32955
- ...entry.signer ? { verify: signatureCheck(entry.signer, d.fileName) } : {}
32956
- });
32957
- } catch {
32958
- }
32959
- })
32993
+ await asBackground(
32994
+ () => Promise.all(
32995
+ todo.map(async (entry) => {
32996
+ try {
32997
+ const d = await resolve(entry);
32998
+ const file3 = prefetchedFile(entry, d.fileName);
32999
+ if (exists(file3)) return;
33000
+ await downloader.file(d.url, file3, {
33001
+ sha256: d.sha256 ?? null,
33002
+ ...entry.signer ? { verify: signatureCheck(entry.signer, d.fileName) } : {}
33003
+ });
33004
+ } catch {
33005
+ }
33006
+ })
33007
+ )
32960
33008
  );
32961
33009
  }
32962
33010
  /** Installs the entries (7-Zip first: the NSIS apps need it to unpack), then their shortcuts. */
@@ -33013,7 +33061,7 @@ var AppInstaller = class {
33013
33061
  const exe = findExe(dest, entry.exe) ?? (entry.unpack === "chromium" && entry.signer ? await installFromKept(dest, entry.signer, entry.name) : null);
33014
33062
  if (!exe) throw new Error(entry.exe + " not found in the download");
33015
33063
  await afterInstall(entry, exe);
33016
- if (!entry.service && (entry.cli || entry.id === "sysinternals")) {
33064
+ if (!entry.service && (entry.cli || entry.onPath || entry.id === "sysinternals")) {
33017
33065
  if (entry.id === "rust-analyzer") this.env.prepend.unshift(path58.dirname(exe));
33018
33066
  else this.env.prepend.push(path58.dirname(exe));
33019
33067
  }
@@ -34909,6 +34957,82 @@ import fs58 from "node:fs";
34909
34957
  import os10 from "node:os";
34910
34958
  import path83 from "node:path";
34911
34959
 
34960
+ // src/shared/memory/policy.ts
34961
+ var DIAL_STEPS = {
34962
+ normal: [
34963
+ { availablePercent: 15, commitPercent: 80 },
34964
+ { availablePercent: 10, commitPercent: 88 },
34965
+ { availablePercent: 6, commitPercent: 94, secondsToLimit: 20 },
34966
+ { availablePercent: 4, commitPercent: 97, secondsToLimit: 8 }
34967
+ ],
34968
+ gentle: [
34969
+ { availablePercent: 20, commitPercent: 75 },
34970
+ { availablePercent: 15, commitPercent: 85 },
34971
+ { availablePercent: 8, commitPercent: 92, secondsToLimit: 25 },
34972
+ { availablePercent: 5, commitPercent: 96, secondsToLimit: 10 }
34973
+ ]
34974
+ };
34975
+ var DIAL_EXIT_MARGIN = { availablePercent: 5, commitPercent: 4 };
34976
+ var DIAL_CALM_SAMPLES = 3;
34977
+ var DIAL_CPU_RATE = [100, 100, 60, 35, 15];
34978
+ function compilersAllowed(step, running) {
34979
+ if (step <= 1 || running <= 1) return running;
34980
+ if (step === 2) return Math.max(1, Math.ceil(running * 0.6));
34981
+ if (step === 3) return Math.max(1, Math.ceil(running / 3));
34982
+ return 1;
34983
+ }
34984
+ function buildJobs(cpus, memoryGb) {
34985
+ return Math.max(2, Math.min(cpus, Math.floor((memoryGb - 6) / 3)));
34986
+ }
34987
+ function linkSlotsFor(memoryGb) {
34988
+ return Math.max(2, Math.min(8, Math.floor((memoryGb - 6) / 3)));
34989
+ }
34990
+ function usableCpus(cpus, percent) {
34991
+ if (percent === null || percent >= 100) return cpus;
34992
+ return Math.max(2, Math.min(cpus, Math.floor(cpus * percent / 100)));
34993
+ }
34994
+ function cargoJobs(cpus, memoryGb, sessions2) {
34995
+ const byCpu = Math.max(2, Math.min(8, Math.floor(cpus / 4)));
34996
+ const byMemory = Math.max(1, Math.floor(memoryGb / 4));
34997
+ const jobs = Math.min(byCpu, byMemory);
34998
+ return Math.max(1, Math.min(jobs, Math.floor(buildJobs(cpus, memoryGb) / Math.max(1, sessions2))));
34999
+ }
35000
+ var LOCAL_TARGET_MIN_BYTES = 25 * 1024 ** 3;
35001
+ function chooseTargetDir(targetDir, freeBytes2) {
35002
+ if (targetDir !== "auto") return { targetDir };
35003
+ if (freeBytes2 >= LOCAL_TARGET_MIN_BYTES) return { targetDir: "local" };
35004
+ return {
35005
+ targetDir: "separate",
35006
+ warning: "Rust builds write through the mapped workspace; this can overload your PC's disk (C: has less than 25 GB free for a local build folder)"
35007
+ };
35008
+ }
35009
+ function maxCompilers(cpus, profile) {
35010
+ return profile === "gentle" ? Math.min(4, Math.max(2, Math.floor(cpus / 4))) : null;
35011
+ }
35012
+ function cargoLinkerVar(host) {
35013
+ return "CARGO_TARGET_" + host.toUpperCase().replace(/[^A-Z0-9]/g, "_") + "_LINKER";
35014
+ }
35015
+ var BURST_GATE = {
35016
+ normal: { growthMb: 4096, windowMs: 3e3, releaseMs: 1500, maxHoldMs: 3e4 },
35017
+ gentle: { growthMb: 3072, windowMs: 3e3, releaseMs: 2e3, maxHoldMs: 3e4 }
35018
+ };
35019
+
35020
+ // src/shared/memory/unreal-build.ts
35021
+ var NAMESPACE = "https://www.unrealengine.com/BuildConfiguration";
35022
+ function ubtBuildConfiguration(jobs) {
35023
+ const max = Math.max(1, Math.floor(jobs));
35024
+ return [
35025
+ '<?xml version="1.0" encoding="utf-8"?>',
35026
+ "<!-- Written by the sandbox at boot: compiles at once from the build budget (run\\budget.json, buildJobs). -->",
35027
+ '<Configuration xmlns="' + NAMESPACE + '">',
35028
+ " <BuildConfiguration>",
35029
+ " <MaxParallelActions>" + max + "</MaxParallelActions>",
35030
+ " </BuildConfiguration>",
35031
+ "</Configuration>",
35032
+ ""
35033
+ ].join("\r\n");
35034
+ }
35035
+
34912
35036
  // src/guest/toolchains/native/debuggers.ts
34913
35037
  import path79 from "node:path";
34914
35038
  var SDK_SETUP_URL = "https://go.microsoft.com/fwlink/?linkid=2311806";
@@ -35686,6 +35810,14 @@ async function checkVisualStudio() {
35686
35810
  fs58.rmSync(dir, { recursive: true, force: true });
35687
35811
  }
35688
35812
  }
35813
+ function capUnrealBuilds() {
35814
+ const cpus = usableCpus(os10.cpus().length, session.cpuLimitPercent ?? null);
35815
+ const jobs = buildJobs(cpus, os10.totalmem() / 1024 ** 3);
35816
+ const appData = process.env.APPDATA ?? "C:\\Users\\WDAGUtilityAccount\\AppData\\Roaming";
35817
+ const dir = mkdirp(path83.join(appData, "Unreal Engine", "UnrealBuildTool"));
35818
+ fs58.writeFileSync(path83.join(dir, "BuildConfiguration.xml"), ubtBuildConfiguration(jobs), "utf8");
35819
+ return jobs;
35820
+ }
35689
35821
  async function part(what, work, fallback) {
35690
35822
  try {
35691
35823
  return await work();
@@ -35704,6 +35836,8 @@ async function setupUnreal(env, config2, _ctx) {
35704
35836
  return;
35705
35837
  }
35706
35838
  resetUnrealStatus();
35839
+ const jobs = await part("capping parallel compiles", async () => capUnrealBuilds(), 0);
35840
+ if (jobs) log("Unreal: UnrealBuildTool runs at most " + jobs + " compiles at once (the build budget)");
35707
35841
  await part("the DirectX End-User Runtime", installDirectX, void 0);
35708
35842
  const launcher = await part("the Epic Games Launcher", ensureLauncher, null);
35709
35843
  const place = placeEngines(config2.persistEngines);
@@ -35739,7 +35873,7 @@ async function setupUnreal(env, config2, _ctx) {
35739
35873
  debugger: debuggers ? path83.join(debuggers, "cdb.exe") : null
35740
35874
  }));
35741
35875
  env.summary.push(
35742
- "unreal tools: " + (vs ? "Visual Studio " + vs.version + (check2.ok ? " (UnrealBuildTool finds it)" : " (UnrealBuildTool would NOT find it)") : "no Visual Studio") + ", DirectX End-User Runtime" + (launcher ? ", Epic Games Launcher" : "") + (debuggers ? ", cdb/windbg" : "")
35876
+ "unreal tools: " + (vs ? "Visual Studio " + vs.version + (check2.ok ? " (UnrealBuildTool finds it)" : " (UnrealBuildTool would NOT find it)") : "no Visual Studio") + (jobs ? ", at most " + jobs + " compiles at once" : "") + ", DirectX End-User Runtime" + (launcher ? ", Epic Games Launcher" : "") + (debuggers ? ", cdb/windbg" : "")
35743
35877
  );
35744
35878
  }
35745
35879
 
@@ -36168,7 +36302,7 @@ async function setupVcRuntime(env, summary = true) {
36168
36302
  log("Downloading the Visual C++ runtime (~25 MB once) ...");
36169
36303
  await downloadFile(VC_REDIST, installer2);
36170
36304
  }
36171
- if (!signedBy(installer2, SIGNERS.microsoft)) {
36305
+ if (!await signedByAsync(installer2, SIGNERS.microsoft)) {
36172
36306
  fs62.rmSync(installer2, { force: true });
36173
36307
  throw new Error("vc_redist.x64.exe is not signed by Microsoft; deleted it (it is downloaded again next boot)");
36174
36308
  }
@@ -37586,62 +37720,6 @@ import fs72 from "node:fs";
37586
37720
  import os12 from "node:os";
37587
37721
  import path104 from "node:path";
37588
37722
 
37589
- // src/shared/memory/policy.ts
37590
- var DIAL_STEPS = {
37591
- normal: [
37592
- { availablePercent: 15, commitPercent: 80 },
37593
- { availablePercent: 10, commitPercent: 88 },
37594
- { availablePercent: 6, commitPercent: 94, secondsToLimit: 20 },
37595
- { availablePercent: 4, commitPercent: 97, secondsToLimit: 8 }
37596
- ],
37597
- gentle: [
37598
- { availablePercent: 20, commitPercent: 75 },
37599
- { availablePercent: 15, commitPercent: 85 },
37600
- { availablePercent: 8, commitPercent: 92, secondsToLimit: 25 },
37601
- { availablePercent: 5, commitPercent: 96, secondsToLimit: 10 }
37602
- ]
37603
- };
37604
- var DIAL_EXIT_MARGIN = { availablePercent: 5, commitPercent: 4 };
37605
- var DIAL_CALM_SAMPLES = 3;
37606
- var DIAL_CPU_RATE = [100, 100, 60, 35, 15];
37607
- function compilersAllowed(step, running) {
37608
- if (step <= 1 || running <= 1) return running;
37609
- if (step === 2) return Math.max(1, Math.ceil(running * 0.6));
37610
- if (step === 3) return Math.max(1, Math.ceil(running / 3));
37611
- return 1;
37612
- }
37613
- function buildJobs(cpus, memoryGb) {
37614
- return Math.max(2, Math.min(cpus, Math.floor((memoryGb - 6) / 3)));
37615
- }
37616
- function linkSlotsFor(memoryGb) {
37617
- return Math.max(2, Math.min(8, Math.floor((memoryGb - 6) / 3)));
37618
- }
37619
- function usableCpus(cpus, percent) {
37620
- if (percent === null || percent >= 100) return cpus;
37621
- return Math.max(2, Math.min(cpus, Math.floor(cpus * percent / 100)));
37622
- }
37623
- function cargoJobs(cpus, memoryGb, sessions2) {
37624
- const byCpu = Math.max(2, Math.min(8, Math.floor(cpus / 4)));
37625
- const byMemory = Math.max(1, Math.floor(memoryGb / 4));
37626
- const jobs = Math.min(byCpu, byMemory);
37627
- return Math.max(1, Math.min(jobs, Math.floor(buildJobs(cpus, memoryGb) / Math.max(1, sessions2))));
37628
- }
37629
- var LOCAL_TARGET_MIN_BYTES = 25 * 1024 ** 3;
37630
- function chooseTargetDir(targetDir, freeBytes2) {
37631
- if (targetDir !== "auto") return { targetDir };
37632
- if (freeBytes2 >= LOCAL_TARGET_MIN_BYTES) return { targetDir: "local" };
37633
- return {
37634
- targetDir: "separate",
37635
- warning: "Rust builds write through the mapped workspace; this can overload your PC's disk (C: has less than 25 GB free for a local build folder)"
37636
- };
37637
- }
37638
- function maxCompilers(cpus, profile) {
37639
- return profile === "gentle" ? Math.min(4, Math.max(2, Math.floor(cpus / 4))) : null;
37640
- }
37641
- function cargoLinkerVar(host) {
37642
- return "CARGO_TARGET_" + host.toUpperCase().replace(/[^A-Z0-9]/g, "_") + "_LINKER";
37643
- }
37644
-
37645
37723
  // src/guest/governor/decide.ts
37646
37724
  var COMPILERS = /* @__PURE__ */ new Set([
37647
37725
  "rustc",
@@ -37741,6 +37819,7 @@ function linkerSlots(totalMb, cpus) {
37741
37819
  }
37742
37820
  var isHeavyCompiler = (name) => !isNeverTouched(name) && HEAVY.has(key(name));
37743
37821
  var canPause = (name) => !isNeverTouched(name) && COMPILERS.has(key(name));
37822
+ var GATE_NAMES = [...COMPILERS, "shadercompileworker"].filter((n) => !NEVER2.has(n)).sort();
37744
37823
  var INITIAL_STATE = {
37745
37824
  step: 0,
37746
37825
  calm: 0,
@@ -38204,7 +38283,7 @@ function plan(config2, versions, o) {
38204
38283
 
38205
38284
  // src/guest/toolchains/step-scheduler.ts
38206
38285
  import { AsyncLocalStorage as AsyncLocalStorage4 } from "node:async_hooks";
38207
- var PARALLEL2 = 4;
38286
+ var PARALLEL2 = 3;
38208
38287
  async function runReported(sink2, name, env, work) {
38209
38288
  try {
38210
38289
  await work();
@@ -38703,6 +38782,9 @@ async function sendControl(endpoint, type) {
38703
38782
  // src/shared/pty/output-filter.ts
38704
38783
  var MAX_CLIPBOARD_STRING = 1024 * 1024;
38705
38784
 
38785
+ // src/shared/web/protocol.ts
38786
+ var MAX_CLIENT_MESSAGE_BYTES = 256 * 1024;
38787
+
38706
38788
  // src/guest/sessions/cmdline.ts
38707
38789
  import path106 from "node:path";
38708
38790
  var quoted = (arg) => '"' + arg.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/, "$1$1") + '"';
@@ -39195,6 +39277,63 @@ async function flushAccounts(final = false) {
39195
39277
  import os15 from "node:os";
39196
39278
 
39197
39279
  // src/guest/governor/core.ts
39280
+ function memgovArgs(profile) {
39281
+ const g = BURST_GATE[profile];
39282
+ return [
39283
+ "serve",
39284
+ "--interval",
39285
+ "2",
39286
+ "--gate",
39287
+ [g.growthMb, g.windowMs, g.releaseMs, g.maxHoldMs].join(":"),
39288
+ "--gate-names",
39289
+ GATE_NAMES.join(",")
39290
+ ];
39291
+ }
39292
+ var GateBase = { event: external_exports.literal("gate"), at: external_exports.number() };
39293
+ var GateName = external_exports.string().max(128);
39294
+ var GateCount = external_exports.number().int().min(0);
39295
+ var GateEventSchema = external_exports.discriminatedUnion("kind", [
39296
+ external_exports.strictObject({
39297
+ ...GateBase,
39298
+ kind: external_exports.literal("ready"),
39299
+ mode: external_exports.enum(["notify", "poll"]),
39300
+ growthMb: GateCount,
39301
+ windowMs: GateCount
39302
+ }),
39303
+ external_exports.strictObject({ ...GateBase, kind: external_exports.literal("off"), reason: external_exports.string().max(200) }),
39304
+ external_exports.strictObject({
39305
+ ...GateBase,
39306
+ kind: external_exports.literal("trip"),
39307
+ grewMb: GateCount,
39308
+ ms: GateCount,
39309
+ jobMb: GateCount,
39310
+ held: external_exports.array(external_exports.strictObject({ pid: external_exports.number().int(), name: GateName })).max(64)
39311
+ }),
39312
+ external_exports.strictObject({ ...GateBase, kind: external_exports.literal("hold"), pid: external_exports.number().int(), name: GateName }),
39313
+ external_exports.strictObject({
39314
+ ...GateBase,
39315
+ kind: external_exports.literal("release"),
39316
+ pid: external_exports.number().int(),
39317
+ name: GateName,
39318
+ heldMs: GateCount,
39319
+ forced: external_exports.boolean()
39320
+ })
39321
+ ]);
39322
+ function gateLine(e) {
39323
+ const cut = (s) => s.slice(0, 64);
39324
+ switch (e.kind) {
39325
+ case "ready":
39326
+ return "gate: on (" + e.mode + "): new compilers wait when the agents' programs grow by more than " + e.growthMb + " MB within " + e.windowMs / 1e3 + " s";
39327
+ case "off":
39328
+ return "gate: off (" + e.reason + "): builds are only slowed down by the dial";
39329
+ case "trip":
39330
+ return "gate: the agents' programs grew " + e.grewMb + " MB in " + (e.ms / 1e3).toFixed(1) + " s (now " + e.jobMb + " MB); holding " + e.held.length + ": " + e.held.slice(0, 8).map((p) => cut(p.name) + " (" + p.pid + ")").join(", ") + (e.held.length > 8 ? " \u2026" : "");
39331
+ case "hold":
39332
+ return "gate: " + cut(e.name) + " (" + e.pid + ") waits at start while a build burst goes through in turn";
39333
+ case "release":
39334
+ return "gate: " + cut(e.name) + " (" + e.pid + ") goes on after " + (e.heldMs / 1e3).toFixed(1) + " s" + (e.forced ? " (the longest it may wait)" : "");
39335
+ }
39336
+ }
39198
39337
  var STEP_TEXT = [
39199
39338
  "builds back to normal",
39200
39339
  "build programs at lower priority",
@@ -39244,6 +39383,7 @@ var GovernorCore = class {
39244
39383
  commitPercent: d.commitPercent,
39245
39384
  step: d.state.step,
39246
39385
  hostStep: nowMs < d.state.hostUntil ? d.state.hostStep : 0,
39386
+ held: sample.gateHeld ?? 0,
39247
39387
  diskQueue: null,
39248
39388
  top: [...sample.procs].sort((a, b) => b.mb - a.mb).slice(0, 5).map((p) => ({ name: p.name.slice(0, 64), mb: Math.round(p.mb) }))
39249
39389
  };
@@ -39265,6 +39405,7 @@ var GovernorCore = class {
39265
39405
  commitPercent: Math.max(0, Math.min(100, d.commitPercent)),
39266
39406
  lowered: Object.keys(d.state.lowered).length,
39267
39407
  paused: [...this.held].slice(0, 8).map(([pid, name]) => ({ name: name.slice(0, 64), pid, seconds: HOLD_MS / 1e3 })),
39408
+ held: sample.gateHeld ?? 0,
39268
39409
  cpuPercent: d.state.cpuRate,
39269
39410
  pcLow: nowMs < d.state.hostUntil,
39270
39411
  lastAction: this.lastAction.slice(0, 300),
@@ -39277,6 +39418,10 @@ var GovernorCore = class {
39277
39418
  this.deps.saveStatus(status);
39278
39419
  }
39279
39420
  }
39421
+ /** An event from the memory gate in memgov: goes to the memory log. */
39422
+ onGate(event) {
39423
+ this.note(gateLine(event));
39424
+ }
39280
39425
  apply(action) {
39281
39426
  const port = this.deps.port;
39282
39427
  try {
@@ -39337,26 +39482,34 @@ var SampleSchema = external_exports.object({
39337
39482
  mb: external_exports.number(),
39338
39483
  prio: external_exports.enum(["idle", "below", "normal"]).catch("normal")
39339
39484
  })
39340
- ).max(1024)
39485
+ ).max(1024),
39486
+ gateHeld: external_exports.number().int().min(0).max(1024).optional().catch(void 0)
39341
39487
  });
39342
39488
  var MemGovProcess = class {
39343
- constructor(onSample) {
39489
+ constructor(args, onSample, onGate) {
39490
+ this.args = args;
39344
39491
  this.onSample = onSample;
39492
+ this.onGate = onGate;
39345
39493
  }
39494
+ args;
39346
39495
  onSample;
39496
+ onGate;
39347
39497
  child = null;
39348
39498
  start() {
39349
39499
  try {
39350
- const child = spawn12(ensureCompiled("memgov"), ["serve", "--interval", "2"], {
39500
+ const child = spawn12(ensureCompiled("memgov"), [...this.args], {
39351
39501
  stdio: ["pipe", "pipe", "ignore"],
39352
39502
  windowsHide: true
39353
39503
  });
39354
39504
  this.child = child;
39355
39505
  createInterface({ input: child.stdout }).on("line", (line) => {
39356
39506
  try {
39357
- this.onSample(SampleSchema.parse(JSON.parse(line)));
39507
+ const raw = JSON.parse(line);
39508
+ if (typeof raw === "object" && raw !== null && raw.event === "gate")
39509
+ this.onGate(GateEventSchema.parse(raw));
39510
+ else this.onSample(SampleSchema.parse(raw));
39358
39511
  } catch (e) {
39359
- warn("governor: bad sample: " + errMsg(e));
39512
+ warn("governor: bad line from memgov: " + errMsg(e));
39360
39513
  }
39361
39514
  });
39362
39515
  child.stdin.on("error", () => void 0);
@@ -39409,12 +39562,16 @@ var Governor = class {
39409
39562
  describe() {
39410
39563
  const profile = session.loadProfile === "gentle" ? "gentle" : "normal";
39411
39564
  const first = DIAL_STEPS[profile][0];
39412
- return session.pressureResponse === false ? "off (sandbox.pressureResponse)" : profile + " profile: slows builds down from " + first.availablePercent + "% free RAM or " + first.commitPercent + "% commit, in steps";
39565
+ return session.pressureResponse === false ? "off (sandbox.pressureResponse)" : profile + " profile: slows builds down from " + first.availablePercent + "% free RAM or " + first.commitPercent + "% commit, in steps; holds new compilers when memory grows faster than " + BURST_GATE[profile].growthMb + " MB in " + BURST_GATE[profile].windowMs / 1e3 + " s";
39413
39566
  }
39414
39567
  start() {
39415
39568
  if (session.pressureResponse === false) return;
39416
39569
  const profile = session.loadProfile === "gentle" ? "gentle" : "normal";
39417
- const proc = new MemGovProcess((s) => this.core?.onSample(s));
39570
+ const proc = new MemGovProcess(
39571
+ memgovArgs(profile),
39572
+ (s) => this.core?.onSample(s),
39573
+ (e) => this.core?.onGate(e)
39574
+ );
39418
39575
  this.core = new GovernorCore({
39419
39576
  port: proc,
39420
39577
  profile,
@@ -39457,6 +39614,7 @@ function buildHeartbeat(snapshot2, sessionCount, uptimeSeconds, freeBytes2, tota
39457
39614
  hostStep: clamp(snapshot2?.hostStep ?? 0, 0, 4),
39458
39615
  topProcesses: (snapshot2?.top ?? []).slice(0, 5).map((p) => ({ name: p.name.slice(0, 64), privateMb: p.mb })),
39459
39616
  ...snapshot2?.diskQueue !== null && snapshot2?.diskQueue !== void 0 ? { diskQueueReported: snapshot2.diskQueue } : {},
39617
+ ...snapshot2 && snapshot2.held > 0 ? { held: Math.min(1024, snapshot2.held) } : {},
39460
39618
  phase: ending ? "ending" : sessionCount > 0 ? "working" : "ready"
39461
39619
  };
39462
39620
  }