windows-development-sandbox 1.28.0 → 1.29.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 (92) hide show
  1. package/README.md +5 -4
  2. package/dist/app/features/sandbox/sandbox.run.js +1 -1
  3. package/dist/bin/{chunk-UZ5S3EMB.js → chunk-O4EL32TM.js} +1 -1
  4. package/dist/bin/{chunk-CB44HTTN.js → chunk-QYRRWGFY.js} +1442 -1280
  5. package/dist/bin/sandbox.js +10772 -3
  6. package/dist/cli/commands/launch/launch.js +1 -1
  7. package/dist/cli/commands/launch/running.js +2 -2
  8. package/dist/cli/index.js +1 -1
  9. package/dist/cli/menu/pages/agents-page.js +1 -1
  10. package/dist/cli/services/sandbox-service.js +1 -1
  11. package/dist/core/schema/sandbox.js +1 -1
  12. package/dist/guest/apps/app-installer.js +1 -1
  13. package/dist/guest/apps/store-check.d.ts +2 -0
  14. package/dist/guest/apps/store-check.js +3 -1
  15. package/dist/guest/boot/guide-lines.d.ts +2 -0
  16. package/dist/guest/boot/guide-lines.js +7 -0
  17. package/dist/guest/boot/guide.js +2 -1
  18. package/dist/guest/toolchains/native/unreal.js +3 -22
  19. package/dist/host/accounts/deliver.js +16 -2
  20. package/dist/host/apps/store-package.d.ts +2 -0
  21. package/dist/host/apps/store-package.js +16 -15
  22. package/dist/host/channel/attach.js +1 -1
  23. package/dist/host/channel/channel.js +3 -3
  24. package/dist/host/guard/commands.js +1 -0
  25. package/dist/host/health/path-copies.d.ts +1 -1
  26. package/dist/host/health/path-copies.js +2 -2
  27. package/dist/host/install/index.d.ts +1 -1
  28. package/dist/host/install/index.js +1 -1
  29. package/dist/host/install/shims.d.ts +8 -1
  30. package/dist/host/install/shims.js +39 -1
  31. package/dist/host/launch/generate.js +4 -0
  32. package/dist/host/launch/index.d.ts +1 -0
  33. package/dist/host/launch/index.js +1 -0
  34. package/dist/host/launch/pc-logs.d.ts +6 -0
  35. package/dist/host/launch/pc-logs.js +129 -0
  36. package/dist/host/sandbox/platform.js +1 -1
  37. package/dist/host/sandbox/window.js +1 -1
  38. package/dist/runtime/agent.js +1 -1
  39. package/dist/runtime/daemon.js +72 -90
  40. package/dist/runtime/db.js +1 -1
  41. package/dist/runtime/main.js +35 -57
  42. package/dist/runtime/netproxy.js +1 -1
  43. package/dist/runtime/tool.js +1 -1
  44. package/dist/runtime/view.js +1 -1
  45. package/dist/selftest/cli.test.js +5 -5
  46. package/dist/selftest/config.test.js +1 -1
  47. package/dist/selftest/credentials.test.js +1 -1
  48. package/dist/selftest/engine.test.js +1 -1
  49. package/dist/selftest/generate.test.js +66 -1
  50. package/dist/selftest/hcs.test.js +1 -1
  51. package/dist/selftest/health.test.js +21 -10
  52. package/dist/selftest/heavy-load.test.js +1 -5
  53. package/dist/selftest/host-sim.test.js +13 -14
  54. package/dist/selftest/install.test.js +6 -2
  55. package/dist/selftest/store-app.test.js +62 -4
  56. package/dist/selftest/support/web-preview.js +2 -2
  57. package/dist/shared/accounts/kinds.d.ts +5 -0
  58. package/dist/shared/accounts/kinds.js +8 -0
  59. package/dist/shared/apps/index.d.ts +1 -1
  60. package/dist/shared/apps/index.js +1 -1
  61. package/dist/shared/apps/selection.d.ts +1 -0
  62. package/dist/shared/apps/selection.js +4 -3
  63. package/dist/shared/apps/store-package.d.ts +13 -0
  64. package/dist/shared/apps/store-package.js +24 -0
  65. package/dist/shared/memory/index.d.ts +0 -1
  66. package/dist/shared/memory/index.js +0 -1
  67. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  68. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  69. package/native/csharp/HostTools/Window.cs +1 -1
  70. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  71. package/native/scripts/get-store-package.ps1 +24 -13
  72. package/package.json +4 -14
  73. package/sandbox/scripts/install-store-package.ps1 +11 -4
  74. package/schemas/config.schema.json +1 -1
  75. package/schemas/sandbox-config.schema.json +1 -1
  76. package/dist/bin/agent-sandbox.d.ts +0 -2
  77. package/dist/bin/agent-sandbox.js +0 -9
  78. package/dist/bin/chunk-ZMZI3625.js +0 -10778
  79. package/dist/bin/claude-host.d.ts +0 -2
  80. package/dist/bin/claude-host.js +0 -11
  81. package/dist/bin/claude-sbx.d.ts +0 -2
  82. package/dist/bin/claude-sbx.js +0 -9
  83. package/dist/bin/codex-sbx.d.ts +0 -2
  84. package/dist/bin/codex-sbx.js +0 -9
  85. package/dist/bin/gemini-sbx.d.ts +0 -2
  86. package/dist/bin/gemini-sbx.js +0 -9
  87. package/dist/bin/sbx-hide.d.ts +0 -2
  88. package/dist/bin/sbx-hide.js +0 -9
  89. package/dist/bin/sbx-show.d.ts +0 -2
  90. package/dist/bin/sbx-show.js +0 -9
  91. package/dist/shared/memory/unreal-build.d.ts +0 -2
  92. package/dist/shared/memory/unreal-build.js +0 -21
@@ -0,0 +1,129 @@
1
+ // PC logs for developing the sandbox inside a sandbox: when a launch includes this very source repo, the PC's recent
2
+ // crash reports and logs are copied into <launch>\pc-logs (C:\AgentLaunch\pc-logs, read-only, gone with the sandbox).
3
+ // The gate is the repository url: a repo that only claims the package name does not pass. If one ever did, it would
4
+ // see the owner's crash diagnostics (memory numbers, Windows events, process names), which hold no secrets.
5
+ // Nothing else from <storage> is copied: no state files, logins, secrets or session ledger.
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import { z } from 'zod';
9
+ import { assertNoLinks, ensureDir } from '../../core/util/index.js';
10
+ const PACKAGE_NAME = 'windows-development-sandbox';
11
+ const MAX_PACKAGE_JSON_BYTES = 256 * 1024;
12
+ const MAX_REPORTS = 10;
13
+ const MAX_REPORT_BYTES = 64 * 1024;
14
+ const TAIL_BYTES = 256 * 1024;
15
+ const PackageSchema = z.object({
16
+ name: z.string(),
17
+ repository: z.union([z.string(), z.object({ url: z.string().optional() }).passthrough()]).optional(),
18
+ });
19
+ function repoUrl(pkg) {
20
+ const url = typeof pkg.repository === 'string' ? pkg.repository : pkg.repository?.url;
21
+ if (!url)
22
+ return null;
23
+ return url
24
+ .trim()
25
+ .toLowerCase()
26
+ .replace(/^git\+/, '')
27
+ .replace(/\.git$/, '')
28
+ .replace(/\/+$/, '');
29
+ }
30
+ /** The package.json in a folder, read with a size cap and no links; null when missing, too big or not valid. */
31
+ function readPackage(folder) {
32
+ try {
33
+ const file = path.join(folder, 'package.json');
34
+ assertNoLinks(folder, file);
35
+ const stat = fs.lstatSync(file);
36
+ if (!stat.isFile() || stat.size > MAX_PACKAGE_JSON_BYTES)
37
+ return null;
38
+ const parsed = PackageSchema.safeParse(JSON.parse(fs.readFileSync(file, 'utf8')));
39
+ return parsed.success ? parsed.data : null;
40
+ }
41
+ catch {
42
+ // No package.json, a link, or not JSON: not the sandbox's source.
43
+ return null;
44
+ }
45
+ }
46
+ /** Whether a folder is a checkout (or git worktree) of this sandbox's own source repo, judged by the installed copy. */
47
+ export function isSandboxSourceRepo(folder, packageRoot) {
48
+ const own = readPackage(packageRoot);
49
+ const ownUrl = own ? repoUrl(own) : null;
50
+ if (!ownUrl)
51
+ return false;
52
+ const pkg = readPackage(folder);
53
+ return pkg !== null && pkg.name === PACKAGE_NAME && repoUrl(pkg) === ownUrl;
54
+ }
55
+ /** Plain file, no links on the way: its first `max` bytes (head) or last `max` bytes (tail); null when unreadable. */
56
+ function readBounded(root, file, max, from) {
57
+ try {
58
+ assertNoLinks(root, file);
59
+ const stat = fs.lstatSync(file);
60
+ if (!stat.isFile())
61
+ return null;
62
+ const length = Math.min(stat.size, max);
63
+ const buffer = Buffer.alloc(length);
64
+ const fd = fs.openSync(file, 'r');
65
+ try {
66
+ fs.readSync(fd, buffer, 0, length, from === 'tail' ? stat.size - length : 0);
67
+ }
68
+ finally {
69
+ fs.closeSync(fd);
70
+ }
71
+ return buffer;
72
+ }
73
+ catch {
74
+ // Missing or unreadable: left out.
75
+ return null;
76
+ }
77
+ }
78
+ const README = [
79
+ 'These files come from the PC that started this sandbox: its recent crash reports and logs, for developing the',
80
+ 'sandbox itself. They are read-only here and gone when the sandbox closes.',
81
+ '',
82
+ 'They are not part of the repo: never copy them into the workspace and never commit them.',
83
+ '',
84
+ 'crashes\\ the newest crash reports (what Windows logged when a sandbox ended, plus the memory log)',
85
+ '*.log the last 256 KB of the PC memory log, the host watcher log and the live-folders log',
86
+ '',
87
+ ].join('\r\n');
88
+ /** Whether this launch has the sandbox's own source repo among its projects (the launch folder or a mapped project). */
89
+ export function launchHasSandboxSource(folders, packageRoot) {
90
+ return folders.some((f) => isSandboxSourceRepo(f, packageRoot));
91
+ }
92
+ /** Copies the PC's recent crash reports and log tails into <launchPath>\pc-logs. Returns the folder. */
93
+ export function stagePcLogs(storageRoot, dataPath, launchPath) {
94
+ const target = path.join(launchPath, 'pc-logs');
95
+ ensureDir(target);
96
+ fs.writeFileSync(path.join(target, 'README.txt'), README, 'utf8');
97
+ const crashes = path.join(storageRoot, 'logs', 'crashes');
98
+ let names = [];
99
+ try {
100
+ assertNoLinks(storageRoot, crashes);
101
+ names = fs
102
+ .readdirSync(crashes)
103
+ .filter((f) => /^crash-[\w.-]+\.txt$/.test(f))
104
+ .sort()
105
+ .slice(-MAX_REPORTS);
106
+ }
107
+ catch {
108
+ // No reports folder yet: none to copy.
109
+ }
110
+ for (const name of names) {
111
+ const text = readBounded(storageRoot, path.join(crashes, name), MAX_REPORT_BYTES, 'head');
112
+ if (!text)
113
+ continue;
114
+ ensureDir(path.join(target, 'crashes'));
115
+ fs.writeFileSync(path.join(target, 'crashes', name), text);
116
+ }
117
+ const data = path.basename(dataPath);
118
+ const tails = [
119
+ [path.join(storageRoot, 'logs', 'host-memory-' + data + '.log'), 'host-memory.log'],
120
+ [path.join(storageRoot, 'logs', 'host-sync-' + data + '.log'), 'host-sync.log'],
121
+ [path.join(storageRoot, 'state', 'live-folders.log'), 'live-folders.log'],
122
+ ];
123
+ for (const [source, name] of tails) {
124
+ const text = readBounded(storageRoot, source, TAIL_BYTES, 'tail');
125
+ if (text)
126
+ fs.writeFileSync(path.join(target, name), text);
127
+ }
128
+ return target;
129
+ }
@@ -63,7 +63,7 @@ export function assertCanLaunch(owner) {
63
63
  ? other + ' (Windows allows one per PC).'
64
64
  : 'A Windows Sandbox is already running (Windows allows one at a time).', other
65
65
  ? 'Use it with sandbox attach if it is shared with you, or start yours after it stops.'
66
- : 'Attach to it with sandbox claude -t, show it with sbx-show, or shut it down with sandbox stop.');
66
+ : 'Attach to it with sandbox claude -t, show it with sandbox show, or shut it down with sandbox stop.');
67
67
  }
68
68
  }
69
69
  /** cmd.exe expands %VAR% and ^ even inside quotes, and a quote ends them: such a path could run something else. */
@@ -1,5 +1,5 @@
1
1
  // Headless mode: Windows Sandbox always opens its client window, so we hide it (ShowWindow SW_HIDE) as soon as it
2
- // appears. The VM and the logged-in session keep running; `sbx-show` brings the window back. The work is done by
2
+ // appears. The VM and the logged-in session keep running; `sandbox show` brings the window back. The work is done by
3
3
  // HostTools.exe (native\csharp\HostTools\Window.cs), else by native\scripts\*-window.ps1 with the same C#.
4
4
  import path from 'node:path';
5
5
  import { spawn, spawnSync } from 'node:child_process';
@@ -25516,7 +25516,7 @@ var SandboxSchema = external_exports.strictObject({
25516
25516
  printerRedirection: external_exports.boolean().describe("Printers from this PC."),
25517
25517
  protectedClient: external_exports.boolean().describe("Run the sandbox window in Protected Client mode (recommended)."),
25518
25518
  headless: external_exports.boolean().describe(
25519
- "Hide the sandbox window (sbx-show brings it back). A headless sandbox shuts down when its last session ends."
25519
+ 'Hide the sandbox window ("Show the window" brings it back). A headless sandbox shuts down when its last session ends.'
25520
25520
  ),
25521
25521
  keepAwake: external_exports.boolean().describe(
25522
25522
  "Keep this PC from sleeping while a sandbox runs, so agents left working overnight keep going (the screen may still turn off; closing a laptop lid still sleeps it)."
@@ -25535,7 +25535,7 @@ var SandboxSchema = external_exports.strictObject({
25535
25535
  printerRedirection: external_exports.boolean().describe("Printers from this PC."),
25536
25536
  protectedClient: external_exports.boolean().describe("Run the sandbox window in Protected Client mode (recommended)."),
25537
25537
  headless: external_exports.boolean().describe(
25538
- "Hide the sandbox window (sbx-show brings it back). A headless sandbox shuts down when its last session ends."
25538
+ 'Hide the sandbox window ("Show the window" brings it back). A headless sandbox shuts down when its last session ends.'
25539
25539
  ),
25540
25540
  keepAwake: external_exports.boolean().describe(
25541
25541
  "Keep this PC from sleeping while a sandbox runs, so agents left working overnight keep going (the screen may still turn off; closing a laptop lid still sleeps it)."
@@ -30040,6 +30040,10 @@ function toolLines(primaryName, toolSummary, projects) {
30040
30040
  "- Tools by project: each project gets the tools its own files ask for; python and rust already installed here are on every project's PATH."
30041
30041
  ];
30042
30042
  }
30043
+ function pcLogsGuideLine(launchDir2, exists2) {
30044
+ const dir = path31.win32.join(launchDir2, "pc-logs");
30045
+ return exists2(dir) ? ["- PC crash reports and logs (this project only, read-only, never commit them): `" + dir + "`"] : [];
30046
+ }
30043
30047
 
30044
30048
  // src/shared/unreal/engines.ts
30045
30049
  import crypto3 from "node:crypto";
@@ -31915,6 +31919,7 @@ function sandboxGuide(toolSummary, lan = "blocked", projects = [], addons = [])
31915
31919
  "- RW, persists: `" + stateDir + "` (agent config/history; do not edit logins) \xB7 logs: `" + path46.join(dataDir, "logs") + "`",
31916
31920
  "- RO: `" + launchDir + "` (this boot's scripts) \xB7 lost on close: everything else, incl. `C:\\nvm` (Node symlink)",
31917
31921
  ...mirrorLine(),
31922
+ ...pcLogsGuideLine(launchDir, fs28.existsSync),
31918
31923
  ...toolLines(session.project, toolSummary, projects),
31919
31924
  ...unrealLines(projects),
31920
31925
  ...unityLines(projects),
@@ -32690,9 +32695,10 @@ function fileSha256(file3) {
32690
32695
  stream.on("end", () => resolve2(hash2.digest("hex")));
32691
32696
  });
32692
32697
  }
32698
+ var MISSING_PACKAGE = 'this PC did not hand over its package (it could not fetch it: see the launch notes). Fix: on this PC run "winget download --id <product id> --source msstore", then launch again';
32693
32699
  async function checkedPackage(dir) {
32694
32700
  const manifest2 = readValid(StorePackageManifestSchema, path55.join(dir, STORE_PACKAGE_MANIFEST));
32695
- if (!manifest2) throw new Error("the package was not handed over (this PC could not fetch it: see the launch notes)");
32701
+ if (!manifest2) throw new Error(MISSING_PACKAGE);
32696
32702
  for (const f of manifest2.files) {
32697
32703
  if (await fileSha256(path55.join(dir, f.name)).catch(() => "") !== f.sha256)
32698
32704
  throw new Error(f.name + " is missing or changed");
@@ -33072,7 +33078,9 @@ var AppInstaller = class {
33072
33078
  warn(
33073
33079
  entry.name + " could not be installed and is skipped; the other apps are not affected. Reason: " + errMsg(e)
33074
33080
  );
33075
- this.env.summary.push(entry.name + ": FAILED");
33081
+ this.env.summary.push(
33082
+ "store" in entry.source ? entry.name + ": not installed - " + errMsg(e).slice(0, 300) : entry.name + ": FAILED"
33083
+ );
33076
33084
  }
33077
33085
  }
33078
33086
  /** The command line an app's shortcut starts it with (Comet: its own profile, DevTools on 127.0.0.1). */
@@ -34957,82 +34965,6 @@ import fs58 from "node:fs";
34957
34965
  import os10 from "node:os";
34958
34966
  import path83 from "node:path";
34959
34967
 
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
-
35036
34968
  // src/guest/toolchains/native/debuggers.ts
35037
34969
  import path79 from "node:path";
35038
34970
  var SDK_SETUP_URL = "https://go.microsoft.com/fwlink/?linkid=2311806";
@@ -35810,14 +35742,6 @@ async function checkVisualStudio() {
35810
35742
  fs58.rmSync(dir, { recursive: true, force: true });
35811
35743
  }
35812
35744
  }
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
- }
35821
35745
  async function part(what, work, fallback) {
35822
35746
  try {
35823
35747
  return await work();
@@ -35836,8 +35760,6 @@ async function setupUnreal(env, config2, _ctx) {
35836
35760
  return;
35837
35761
  }
35838
35762
  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)");
35841
35763
  await part("the DirectX End-User Runtime", installDirectX, void 0);
35842
35764
  const launcher = await part("the Epic Games Launcher", ensureLauncher, null);
35843
35765
  const place = placeEngines(config2.persistEngines);
@@ -35873,7 +35795,7 @@ async function setupUnreal(env, config2, _ctx) {
35873
35795
  debugger: debuggers ? path83.join(debuggers, "cdb.exe") : null
35874
35796
  }));
35875
35797
  env.summary.push(
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" : "")
35798
+ "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" : "")
35877
35799
  );
35878
35800
  }
35879
35801
 
@@ -37720,6 +37642,66 @@ import fs72 from "node:fs";
37720
37642
  import os12 from "node:os";
37721
37643
  import path104 from "node:path";
37722
37644
 
37645
+ // src/shared/memory/policy.ts
37646
+ var DIAL_STEPS = {
37647
+ normal: [
37648
+ { availablePercent: 15, commitPercent: 80 },
37649
+ { availablePercent: 10, commitPercent: 88 },
37650
+ { availablePercent: 6, commitPercent: 94, secondsToLimit: 20 },
37651
+ { availablePercent: 4, commitPercent: 97, secondsToLimit: 8 }
37652
+ ],
37653
+ gentle: [
37654
+ { availablePercent: 20, commitPercent: 75 },
37655
+ { availablePercent: 15, commitPercent: 85 },
37656
+ { availablePercent: 8, commitPercent: 92, secondsToLimit: 25 },
37657
+ { availablePercent: 5, commitPercent: 96, secondsToLimit: 10 }
37658
+ ]
37659
+ };
37660
+ var DIAL_EXIT_MARGIN = { availablePercent: 5, commitPercent: 4 };
37661
+ var DIAL_CALM_SAMPLES = 3;
37662
+ var DIAL_CPU_RATE = [100, 100, 60, 35, 15];
37663
+ function compilersAllowed(step, running) {
37664
+ if (step <= 1 || running <= 1) return running;
37665
+ if (step === 2) return Math.max(1, Math.ceil(running * 0.6));
37666
+ if (step === 3) return Math.max(1, Math.ceil(running / 3));
37667
+ return 1;
37668
+ }
37669
+ function buildJobs(cpus, memoryGb) {
37670
+ return Math.max(2, Math.min(cpus, Math.floor((memoryGb - 6) / 3)));
37671
+ }
37672
+ function linkSlotsFor(memoryGb) {
37673
+ return Math.max(2, Math.min(8, Math.floor((memoryGb - 6) / 3)));
37674
+ }
37675
+ function usableCpus(cpus, percent) {
37676
+ if (percent === null || percent >= 100) return cpus;
37677
+ return Math.max(2, Math.min(cpus, Math.floor(cpus * percent / 100)));
37678
+ }
37679
+ function cargoJobs(cpus, memoryGb, sessions2) {
37680
+ const byCpu = Math.max(2, Math.min(8, Math.floor(cpus / 4)));
37681
+ const byMemory = Math.max(1, Math.floor(memoryGb / 4));
37682
+ const jobs = Math.min(byCpu, byMemory);
37683
+ return Math.max(1, Math.min(jobs, Math.floor(buildJobs(cpus, memoryGb) / Math.max(1, sessions2))));
37684
+ }
37685
+ var LOCAL_TARGET_MIN_BYTES = 25 * 1024 ** 3;
37686
+ function chooseTargetDir(targetDir, freeBytes2) {
37687
+ if (targetDir !== "auto") return { targetDir };
37688
+ if (freeBytes2 >= LOCAL_TARGET_MIN_BYTES) return { targetDir: "local" };
37689
+ return {
37690
+ targetDir: "separate",
37691
+ 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)"
37692
+ };
37693
+ }
37694
+ function maxCompilers(cpus, profile) {
37695
+ return profile === "gentle" ? Math.min(4, Math.max(2, Math.floor(cpus / 4))) : null;
37696
+ }
37697
+ function cargoLinkerVar(host) {
37698
+ return "CARGO_TARGET_" + host.toUpperCase().replace(/[^A-Z0-9]/g, "_") + "_LINKER";
37699
+ }
37700
+ var BURST_GATE = {
37701
+ normal: { growthMb: 4096, windowMs: 3e3, releaseMs: 1500, maxHoldMs: 3e4 },
37702
+ gentle: { growthMb: 3072, windowMs: 3e3, releaseMs: 2e3, maxHoldMs: 3e4 }
37703
+ };
37704
+
37723
37705
  // src/guest/governor/decide.ts
37724
37706
  var COMPILERS = /* @__PURE__ */ new Set([
37725
37707
  "rustc",
@@ -25492,7 +25492,7 @@ var SandboxSchema = external_exports.strictObject({
25492
25492
  printerRedirection: external_exports.boolean().describe("Printers from this PC."),
25493
25493
  protectedClient: external_exports.boolean().describe("Run the sandbox window in Protected Client mode (recommended)."),
25494
25494
  headless: external_exports.boolean().describe(
25495
- "Hide the sandbox window (sbx-show brings it back). A headless sandbox shuts down when its last session ends."
25495
+ 'Hide the sandbox window ("Show the window" brings it back). A headless sandbox shuts down when its last session ends.'
25496
25496
  ),
25497
25497
  keepAwake: external_exports.boolean().describe(
25498
25498
  "Keep this PC from sleeping while a sandbox runs, so agents left working overnight keep going (the screen may still turn off; closing a laptop lid still sleeps it)."
@@ -25508,7 +25508,7 @@ var SandboxSchema = external_exports.strictObject({
25508
25508
  printerRedirection: external_exports.boolean().describe("Printers from this PC."),
25509
25509
  protectedClient: external_exports.boolean().describe("Run the sandbox window in Protected Client mode (recommended)."),
25510
25510
  headless: external_exports.boolean().describe(
25511
- "Hide the sandbox window (sbx-show brings it back). A headless sandbox shuts down when its last session ends."
25511
+ 'Hide the sandbox window ("Show the window" brings it back). A headless sandbox shuts down when its last session ends.'
25512
25512
  ),
25513
25513
  keepAwake: external_exports.boolean().describe(
25514
25514
  "Keep this PC from sleeping while a sandbox runs, so agents left working overnight keep going (the screen may still turn off; closing a laptop lid still sleeps it)."
@@ -29168,6 +29168,10 @@ function toolLines(primaryName, toolSummary, projects) {
29168
29168
  "- Tools by project: each project gets the tools its own files ask for; python and rust already installed here are on every project's PATH."
29169
29169
  ];
29170
29170
  }
29171
+ function pcLogsGuideLine(launchDir2, exists2) {
29172
+ const dir = path20.win32.join(launchDir2, "pc-logs");
29173
+ return exists2(dir) ? ["- PC crash reports and logs (this project only, read-only, never commit them): `" + dir + "`"] : [];
29174
+ }
29171
29175
 
29172
29176
  // src/shared/unreal/engines.ts
29173
29177
  import crypto3 from "node:crypto";
@@ -31194,6 +31198,7 @@ function sandboxGuide(toolSummary, lan = "blocked", projects = [], addons = [])
31194
31198
  "- RW, persists: `" + stateDir + "` (agent config/history; do not edit logins) \xB7 logs: `" + path36.join(dataDir, "logs") + "`",
31195
31199
  "- RO: `" + launchDir + "` (this boot's scripts) \xB7 lost on close: everything else, incl. `C:\\nvm` (Node symlink)",
31196
31200
  ...mirrorLine(),
31201
+ ...pcLogsGuideLine(launchDir, fs23.existsSync),
31197
31202
  ...toolLines(session.project, toolSummary, projects),
31198
31203
  ...unrealLines(projects),
31199
31204
  ...unityLines(projects),
@@ -31981,9 +31986,10 @@ function fileSha256(file3) {
31981
31986
  stream.on("end", () => resolve2(hash2.digest("hex")));
31982
31987
  });
31983
31988
  }
31989
+ var MISSING_PACKAGE = 'this PC did not hand over its package (it could not fetch it: see the launch notes). Fix: on this PC run "winget download --id <product id> --source msstore", then launch again';
31984
31990
  async function checkedPackage(dir) {
31985
31991
  const manifest2 = readValid(StorePackageManifestSchema, path45.join(dir, STORE_PACKAGE_MANIFEST));
31986
- if (!manifest2) throw new Error("the package was not handed over (this PC could not fetch it: see the launch notes)");
31992
+ if (!manifest2) throw new Error(MISSING_PACKAGE);
31987
31993
  for (const f of manifest2.files) {
31988
31994
  if (await fileSha256(path45.join(dir, f.name)).catch(() => "") !== f.sha256)
31989
31995
  throw new Error(f.name + " is missing or changed");
@@ -32363,7 +32369,9 @@ var AppInstaller = class {
32363
32369
  warn(
32364
32370
  entry.name + " could not be installed and is skipped; the other apps are not affected. Reason: " + errMsg(e)
32365
32371
  );
32366
- this.env.summary.push(entry.name + ": FAILED");
32372
+ this.env.summary.push(
32373
+ "store" in entry.source ? entry.name + ": not installed - " + errMsg(e).slice(0, 300) : entry.name + ": FAILED"
32374
+ );
32367
32375
  }
32368
32376
  }
32369
32377
  /** The command line an app's shortcut starts it with (Comet: its own profile, DevTools on 127.0.0.1). */
@@ -34243,49 +34251,6 @@ import fs52 from "node:fs";
34243
34251
  import os9 from "node:os";
34244
34252
  import path73 from "node:path";
34245
34253
 
34246
- // src/shared/memory/policy.ts
34247
- function buildJobs(cpus, memoryGb) {
34248
- return Math.max(2, Math.min(cpus, Math.floor((memoryGb - 6) / 3)));
34249
- }
34250
- function usableCpus(cpus, percent) {
34251
- if (percent === null || percent >= 100) return cpus;
34252
- return Math.max(2, Math.min(cpus, Math.floor(cpus * percent / 100)));
34253
- }
34254
- function cargoJobs(cpus, memoryGb, sessions) {
34255
- const byCpu = Math.max(2, Math.min(8, Math.floor(cpus / 4)));
34256
- const byMemory = Math.max(1, Math.floor(memoryGb / 4));
34257
- const jobs = Math.min(byCpu, byMemory);
34258
- return Math.max(1, Math.min(jobs, Math.floor(buildJobs(cpus, memoryGb) / Math.max(1, sessions))));
34259
- }
34260
- var LOCAL_TARGET_MIN_BYTES = 25 * 1024 ** 3;
34261
- function chooseTargetDir(targetDir, freeBytes2) {
34262
- if (targetDir !== "auto") return { targetDir };
34263
- if (freeBytes2 >= LOCAL_TARGET_MIN_BYTES) return { targetDir: "local" };
34264
- return {
34265
- targetDir: "separate",
34266
- 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)"
34267
- };
34268
- }
34269
- function cargoLinkerVar(host) {
34270
- return "CARGO_TARGET_" + host.toUpperCase().replace(/[^A-Z0-9]/g, "_") + "_LINKER";
34271
- }
34272
-
34273
- // src/shared/memory/unreal-build.ts
34274
- var NAMESPACE = "https://www.unrealengine.com/BuildConfiguration";
34275
- function ubtBuildConfiguration(jobs) {
34276
- const max = Math.max(1, Math.floor(jobs));
34277
- return [
34278
- '<?xml version="1.0" encoding="utf-8"?>',
34279
- "<!-- Written by the sandbox at boot: compiles at once from the build budget (run\\budget.json, buildJobs). -->",
34280
- '<Configuration xmlns="' + NAMESPACE + '">',
34281
- " <BuildConfiguration>",
34282
- " <MaxParallelActions>" + max + "</MaxParallelActions>",
34283
- " </BuildConfiguration>",
34284
- "</Configuration>",
34285
- ""
34286
- ].join("\r\n");
34287
- }
34288
-
34289
34254
  // src/guest/toolchains/native/debuggers.ts
34290
34255
  import path69 from "node:path";
34291
34256
  var SDK_SETUP_URL = "https://go.microsoft.com/fwlink/?linkid=2311806";
@@ -35015,14 +34980,6 @@ async function checkVisualStudio() {
35015
34980
  fs52.rmSync(dir, { recursive: true, force: true });
35016
34981
  }
35017
34982
  }
35018
- function capUnrealBuilds() {
35019
- const cpus = usableCpus(os9.cpus().length, session.cpuLimitPercent ?? null);
35020
- const jobs = buildJobs(cpus, os9.totalmem() / 1024 ** 3);
35021
- const appData = process.env.APPDATA ?? "C:\\Users\\WDAGUtilityAccount\\AppData\\Roaming";
35022
- const dir = mkdirp(path73.join(appData, "Unreal Engine", "UnrealBuildTool"));
35023
- fs52.writeFileSync(path73.join(dir, "BuildConfiguration.xml"), ubtBuildConfiguration(jobs), "utf8");
35024
- return jobs;
35025
- }
35026
34983
  async function part(what, work, fallback) {
35027
34984
  try {
35028
34985
  return await work();
@@ -35041,8 +34998,6 @@ async function setupUnreal(env, config3, _ctx) {
35041
34998
  return;
35042
34999
  }
35043
35000
  resetUnrealStatus();
35044
- const jobs = await part("capping parallel compiles", async () => capUnrealBuilds(), 0);
35045
- if (jobs) log("Unreal: UnrealBuildTool runs at most " + jobs + " compiles at once (the build budget)");
35046
35001
  await part("the DirectX End-User Runtime", installDirectX, void 0);
35047
35002
  const launcher = await part("the Epic Games Launcher", ensureLauncher, null);
35048
35003
  const place = placeEngines(config3.persistEngines);
@@ -35078,7 +35033,7 @@ async function setupUnreal(env, config3, _ctx) {
35078
35033
  debugger: debuggers ? path73.join(debuggers, "cdb.exe") : null
35079
35034
  }));
35080
35035
  env.summary.push(
35081
- "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" : "")
35036
+ "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" : "")
35082
35037
  );
35083
35038
  }
35084
35039
 
@@ -38405,6 +38360,29 @@ import fs67 from "node:fs";
38405
38360
  import os11 from "node:os";
38406
38361
  import path94 from "node:path";
38407
38362
 
38363
+ // src/shared/memory/policy.ts
38364
+ function buildJobs(cpus, memoryGb) {
38365
+ return Math.max(2, Math.min(cpus, Math.floor((memoryGb - 6) / 3)));
38366
+ }
38367
+ function cargoJobs(cpus, memoryGb, sessions) {
38368
+ const byCpu = Math.max(2, Math.min(8, Math.floor(cpus / 4)));
38369
+ const byMemory = Math.max(1, Math.floor(memoryGb / 4));
38370
+ const jobs = Math.min(byCpu, byMemory);
38371
+ return Math.max(1, Math.min(jobs, Math.floor(buildJobs(cpus, memoryGb) / Math.max(1, sessions))));
38372
+ }
38373
+ var LOCAL_TARGET_MIN_BYTES = 25 * 1024 ** 3;
38374
+ function chooseTargetDir(targetDir, freeBytes2) {
38375
+ if (targetDir !== "auto") return { targetDir };
38376
+ if (freeBytes2 >= LOCAL_TARGET_MIN_BYTES) return { targetDir: "local" };
38377
+ return {
38378
+ targetDir: "separate",
38379
+ 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)"
38380
+ };
38381
+ }
38382
+ function cargoLinkerVar(host) {
38383
+ return "CARGO_TARGET_" + host.toUpperCase().replace(/[^A-Z0-9]/g, "_") + "_LINKER";
38384
+ }
38385
+
38408
38386
  // src/guest/governor/decide.ts
38409
38387
  var COMPILERS = /* @__PURE__ */ new Set([
38410
38388
  "rustc",
@@ -25544,7 +25544,7 @@ var SandboxSchema = external_exports.strictObject({
25544
25544
  printerRedirection: external_exports.boolean().describe("Printers from this PC."),
25545
25545
  protectedClient: external_exports.boolean().describe("Run the sandbox window in Protected Client mode (recommended)."),
25546
25546
  headless: external_exports.boolean().describe(
25547
- "Hide the sandbox window (sbx-show brings it back). A headless sandbox shuts down when its last session ends."
25547
+ 'Hide the sandbox window ("Show the window" brings it back). A headless sandbox shuts down when its last session ends.'
25548
25548
  ),
25549
25549
  keepAwake: external_exports.boolean().describe(
25550
25550
  "Keep this PC from sleeping while a sandbox runs, so agents left working overnight keep going (the screen may still turn off; closing a laptop lid still sleeps it)."
@@ -25930,7 +25930,7 @@ var SandboxSchema = external_exports.strictObject({
25930
25930
  printerRedirection: external_exports.boolean().describe("Printers from this PC."),
25931
25931
  protectedClient: external_exports.boolean().describe("Run the sandbox window in Protected Client mode (recommended)."),
25932
25932
  headless: external_exports.boolean().describe(
25933
- "Hide the sandbox window (sbx-show brings it back). A headless sandbox shuts down when its last session ends."
25933
+ 'Hide the sandbox window ("Show the window" brings it back). A headless sandbox shuts down when its last session ends.'
25934
25934
  ),
25935
25935
  keepAwake: external_exports.boolean().describe(
25936
25936
  "Keep this PC from sleeping while a sandbox runs, so agents left working overnight keep going (the screen may still turn off; closing a laptop lid still sleeps it)."
@@ -26214,7 +26214,7 @@ var SandboxSchema = external_exports.strictObject({
26214
26214
  printerRedirection: external_exports.boolean().describe("Printers from this PC."),
26215
26215
  protectedClient: external_exports.boolean().describe("Run the sandbox window in Protected Client mode (recommended)."),
26216
26216
  headless: external_exports.boolean().describe(
26217
- "Hide the sandbox window (sbx-show brings it back). A headless sandbox shuts down when its last session ends."
26217
+ 'Hide the sandbox window ("Show the window" brings it back). A headless sandbox shuts down when its last session ends.'
26218
26218
  ),
26219
26219
  keepAwake: external_exports.boolean().describe(
26220
26220
  "Keep this PC from sleeping while a sandbox runs, so agents left working overnight keep going (the screen may still turn off; closing a laptop lid still sleeps it)."
@@ -47,17 +47,17 @@ export const cliSuite = {
47
47
  ['postgres', 'redis', 'mongodb'],
48
48
  ['--continue'],
49
49
  ], '`start` is launch without an agent; positionals, repeated lists and arguments after -- parse');
50
- t.section('parser: agent wrappers (claude-sbx ...) and agent-sandbox');
50
+ t.section('parser: agent mode (sandbox claude ...) and plain commands');
51
51
  const wrapped = parseCli(['--terminal', '--continue', '--headless', '--node', '20', '--', '--terminal'], true);
52
- t.equal([wrapped.command, wrapped.terminal, wrapped.headless, wrapped.node, wrapped.passthrough], ['launch', true, true, '20', ['--continue', '--terminal']], 'claude-sbx: sandbox flags are consumed, everything else goes to the agent');
53
- await t.throws(() => parseCli(['status', '--bogus']), 'unknown option', 'agent-sandbox: unknown options are errors');
52
+ t.equal([wrapped.command, wrapped.terminal, wrapped.headless, wrapped.node, wrapped.passthrough], ['launch', true, true, '20', ['--continue', '--terminal']], 'agent mode: sandbox flags are consumed, everything else goes to the agent');
53
+ await t.throws(() => parseCli(['status', '--bogus']), 'unknown option', 'plain commands: unknown options are errors');
54
54
  const agentFlags = parseCli(['--model', 'opus', '-p', 'fix it', '--json', '-f', 'x', '--timeout', '5', '--resume', 'abc'], true);
55
55
  t.check(agentFlags.passthrough.join('|') === '--model|opus|-p|fix it|--json|-f|x|--timeout|5|--resume|abc' &&
56
56
  !agentFlags.json &&
57
57
  !agentFlags.follow &&
58
- agentFlags.timeout === 600, 'claude-sbx: any flag that is not a launch option goes to the agent in order and sets no agent-sandbox option');
58
+ agentFlags.timeout === 600, 'agent mode: any flag that is not a launch option goes to the agent in order and sets no sandbox option');
59
59
  const reserved = parseCli(['--sbx-terminal', '--terminal', '--sbx-node=18'], true);
60
- t.equal([reserved.terminal, reserved.node, reserved.passthrough], [true, '18', []], '--sbx-<option> always means agent-sandbox');
60
+ t.equal([reserved.terminal, reserved.node, reserved.passthrough], [true, '18', []], '--sbx-<option> always means the sandbox option');
61
61
  await t.throws(() => parseCli(['--sbx-nope'], true), 'Unknown option: --sbx-nope', 'an unknown --sbx-<option> is an error');
62
62
  t.section('menu helpers');
63
63
  t.check(splitArgs('--model "big one" -c \'x y\'').join('|') === '--model|big one|-c|x y' &&
@@ -75,7 +75,7 @@ export const configSuite = {
75
75
  });
76
76
  const sharedFile = t.at('share', 'theirs.json');
77
77
  fs.writeFileSync(sharedFile, shared);
78
- const bin = (args) => f.runBin('agent-sandbox', [...args, '--config', yours], { cwd: f.workA, env });
78
+ const bin = (args) => f.runBin('sandbox', [...args, '--config', yours], { cwd: f.workA, env });
79
79
  const outFile = t.at('share', 'out.json');
80
80
  const exportRun = await bin(['config', 'export', outFile]);
81
81
  const written = readJsonc(outFile);
@@ -191,7 +191,7 @@ export const credentialsSuite = {
191
191
  ['vpn', 'status', '--json'],
192
192
  ['status', '--json'],
193
193
  ]) {
194
- const out = await f.runBin('agent-sandbox', [...args, '--config', cliConfig], { cwd: f.workA, env });
194
+ const out = await f.runBin('sandbox', [...args, '--config', cliConfig], { cwd: f.workA, env });
195
195
  t.check(!out.out.includes(API_KEY) && (args[0] !== 'config' || out.out.includes('***')), 'sandbox ' + args.join(' ') + ' never prints the planted key' + (args[0] === 'config' ? ' (it shows ***)' : ''), out.out.slice(0, 400));
196
196
  }
197
197
  },
@@ -117,7 +117,7 @@ export const engineSuite = {
117
117
  !LocalOverridesSchema.safeParse({ storage: { containedMaxGb: 8 } }).success, "a project's .sandbox-config.json cannot set sandbox.engine, storage.contained or storage.containedMaxGb");
118
118
  t.check(LocalOverridesSchema.safeParse({ sandbox: { vGpu: false } }).success, 'other sandbox settings are still allowed in a project file');
119
119
  t.section('sandbox engine');
120
- const run = await f.runBin('agent-sandbox', ['engine', '--json', '--config', t.writeJson(t.at('engine-config.json'), {})], {
120
+ const run = await f.runBin('sandbox', ['engine', '--json', '--config', t.writeJson(t.at('engine-config.json'), {})], {
121
121
  cwd: f.workA,
122
122
  env: { ...process.env, NO_COLOR: '1', AGENT_SANDBOX_HOME: t.dir('engine-home'), AGENT_SANDBOX_HOST_ONLY: '1' },
123
123
  });