windows-development-sandbox 1.18.2 → 1.18.3

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 (72) hide show
  1. package/config.example.json +1 -1
  2. package/dist/app/features/defs.d.ts +8 -0
  3. package/dist/app/features/sandbox/sandbox.def.d.ts +12 -0
  4. package/dist/app/features/sandbox/sandbox.def.js +17 -2
  5. package/dist/app/features/sandbox/sandbox.run.d.ts +4 -0
  6. package/dist/app/features/sandbox/sandbox.run.js +4 -0
  7. package/dist/bin/agent-sandbox.js +2 -2
  8. package/dist/bin/{chunk-IIHL7W6B.js → chunk-3G53NNZP.js} +1 -1
  9. package/dist/bin/{chunk-MQIA7GNB.js → chunk-FQWYCF6L.js} +981 -866
  10. package/dist/bin/{chunk-M4OM632I.js → chunk-M6XUQK53.js} +1 -1
  11. package/dist/bin/claude-host.js +2 -2
  12. package/dist/bin/claude-sbx.js +2 -2
  13. package/dist/bin/codex-sbx.js +2 -2
  14. package/dist/bin/gemini-sbx.js +2 -2
  15. package/dist/bin/sandbox.js +2 -2
  16. package/dist/bin/sbx-hide.js +2 -2
  17. package/dist/bin/sbx-show.js +2 -2
  18. package/dist/cli/commands/repair/checks/sandbox.d.ts +3 -1
  19. package/dist/cli/commands/repair/checks/sandbox.js +24 -0
  20. package/dist/core/config/defaults.js +1 -1
  21. package/dist/core/schema/sandbox.js +1 -1
  22. package/dist/core/schema/toolchains.js +2 -2
  23. package/dist/guest/daemon/daemon.js +1 -1
  24. package/dist/guest/daemon/heartbeat.d.ts +6 -0
  25. package/dist/guest/daemon/heartbeat.js +40 -0
  26. package/dist/guest/entries/daemon.js +2 -0
  27. package/dist/guest/governor/core.d.ts +19 -1
  28. package/dist/guest/governor/core.js +23 -1
  29. package/dist/guest/governor/decide.d.ts +14 -0
  30. package/dist/guest/governor/decide.js +52 -7
  31. package/dist/guest/governor/governor.d.ts +4 -1
  32. package/dist/guest/governor/governor.js +9 -2
  33. package/dist/guest/toolchains/native/rust-extras.d.ts +7 -0
  34. package/dist/guest/toolchains/native/rust-extras.js +26 -3
  35. package/dist/guest/toolchains/plan.js +6 -4
  36. package/dist/host/channel/channel.d.ts +1 -1
  37. package/dist/host/channel/channel.js +10 -2
  38. package/dist/host/launch/wsb-file.d.ts +6 -1
  39. package/dist/host/launch/wsb-file.js +9 -5
  40. package/dist/host/life/crash-report.d.ts +2 -0
  41. package/dist/host/life/crash-report.js +4 -0
  42. package/dist/host/life/index.d.ts +1 -0
  43. package/dist/host/life/index.js +1 -0
  44. package/dist/host/life/last-end.d.ts +21 -0
  45. package/dist/host/life/last-end.js +70 -0
  46. package/dist/host/life/watchdog.d.ts +2 -0
  47. package/dist/host/life/watchdog.js +8 -2
  48. package/dist/runtime/agent.js +26 -3
  49. package/dist/runtime/daemon.js +367 -234
  50. package/dist/runtime/db.js +26 -3
  51. package/dist/runtime/main.js +57 -11
  52. package/dist/runtime/netproxy.js +26 -3
  53. package/dist/runtime/tool.js +26 -3
  54. package/dist/runtime/view.js +26 -3
  55. package/dist/selftest/heavy-load.test.js +95 -14
  56. package/dist/selftest/live-terminal.test.js +4 -0
  57. package/dist/shared/channel/run-dir.d.ts +4 -1
  58. package/dist/shared/channel/run-dir.js +7 -1
  59. package/dist/shared/channel/schema.d.ts +23 -0
  60. package/dist/shared/channel/schema.js +21 -0
  61. package/dist/shared/memory/policy.d.ts +10 -0
  62. package/dist/shared/memory/policy.js +17 -1
  63. package/dist/sync/watcher.js +1 -1
  64. package/dist/web/index.html +1 -1
  65. package/dist/web/{main-CWIF4VQI.js → main-6XYO5FZC.js} +43 -43
  66. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  67. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  68. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  69. package/package.json +1 -1
  70. package/sandbox/csharp/MemGov.cs +58 -0
  71. package/schemas/config.schema.json +3 -3
  72. package/schemas/sandbox-config.schema.json +3 -3
@@ -25997,7 +25997,7 @@ var DEFAULTS = {
25997
25997
  components: [],
25998
25998
  targets: [],
25999
25999
  crates: [],
26000
- targetDir: "separate",
26000
+ targetDir: "auto",
26001
26001
  linker: "lld",
26002
26002
  linkSlots: "auto"
26003
26003
  },
@@ -46130,7 +46130,7 @@ var SandboxSchema = external_exports.strictObject({
46130
46130
  '(alpha) What runs the sandbox. "windows-sandbox" = Windows Sandbox (the default: desktop, Windows Terminal, GPU, clipboard, audio, printers). "hcs" = a headless Hyper-V isolated Server Core container for agents (no desktop, GPU or clipboard); it needs Windows features and an image on this PC (sandbox engine check hcs lists what is missing), and starting it is not enabled yet in this alpha. sandbox engine shows what each engine can do. Yours only: a project file (.sandbox-config.json) cannot set it.'
46131
46131
  ),
46132
46132
  memoryMb: external_exports.union([external_exports.literal("auto"), external_exports.number().int().min(2048)]).describe(
46133
- 'Sandbox RAM in MB (>= 2048), or "auto": free RAM minus reserveHostMb, capped at maxMemoryMb. Never more than this PC can keep in RAM for it (its RAM minus the larger of reserveHostMb and 25 %, 2 GB more with vGPU): a bigger value is lowered.'
46133
+ 'Sandbox RAM in MB (>= 2048), or "auto": free RAM minus reserveHostMb, capped at maxMemoryMb. Never more than this PC can keep in RAM for it (its RAM minus the larger of reserveHostMb and 25 %, 4 GB more with vGPU): a bigger value is lowered.'
46134
46134
  ),
46135
46135
  maxMemoryMb: external_exports.number().int().positive().describe('Upper limit for memoryMb "auto".'),
46136
46136
  maxMemoryPercent: external_exports.number().int().min(10).max(100).describe(`memoryMb "auto" never takes more than this share of this PC's total RAM (percent).`),
@@ -46292,13 +46292,13 @@ var ToolchainsSchema = external_exports.strictObject({
46292
46292
  '["sccache", "cargo-nextest", "cargo-deny"]'
46293
46293
  ),
46294
46294
  targetDir: external_exports.string().min(1).describe(
46295
- `"shared": cargo's default target\\ (shared with builds on this PC, so builds on either side invalidate the other), "separate": <workspace>\\target\\sandbox (kept, no cross-talk), "local": on the sandbox's own disk (fastest, lost every boot), or a guest path.`
46295
+ `"auto" (default): on the sandbox's own disk when C: has 25 GB free at boot, else "separate" (builds then write through the mapped workspace, which can overload this PC's disk). "shared": cargo's default target\\ (shared with builds on this PC, so builds on either side invalidate the other), "separate": <workspace>\\target\\sandbox (kept, no cross-talk), "local": on the sandbox's own disk (fastest, lost every boot), or a guest path.`
46296
46296
  ),
46297
46297
  linker: external_exports.enum(["lld", "gnu"]).describe(
46298
46298
  `GNU host: "lld" links with Rust's own lld through MinGW's gcc (much less memory than GNU ld), "gnu" with GNU ld.`
46299
46299
  ),
46300
46300
  linkSlots: external_exports.union([external_exports.literal("auto"), external_exports.number().int().min(1).max(32)]).describe(
46301
- 'GNU host: how many programs may link at the same time in the whole sandbox (other links wait). "auto" = one per 3 GB of sandbox memory after 6 GB, 2 to 8 (the memory governor slows links down too when memory runs short).'
46301
+ 'GNU host: how many programs may link at the same time in the whole sandbox (other links wait). "auto" = one per 6 GB of sandbox memory, 1 to 4 (the memory governor slows links down too when memory runs short).'
46302
46302
  )
46303
46303
  }),
46304
46304
  msvc: external_exports.strictObject({
@@ -48552,7 +48552,7 @@ function hostClaude(storageRoot, personal, args) {
48552
48552
 
48553
48553
  // src/host/sandbox/doctor.ts
48554
48554
  import os6 from "node:os";
48555
- import path35 from "node:path";
48555
+ import path36 from "node:path";
48556
48556
 
48557
48557
  // src/host/storage/cleanup.ts
48558
48558
  var import_lodash4 = __toESM(require_lodash(), 1);
@@ -52293,8 +52293,8 @@ function stopWarning(life, find, crashReport = () => null) {
52293
52293
  }
52294
52294
 
52295
52295
  // src/host/life/crash-report.ts
52296
- import fs25 from "node:fs";
52297
- import path32 from "node:path";
52296
+ import fs26 from "node:fs";
52297
+ import path33 from "node:path";
52298
52298
 
52299
52299
  // src/host/launch/risk-notes.ts
52300
52300
  function loadProfileOf(config3) {
@@ -52445,12 +52445,542 @@ function takePageFileHint(storageRoot, now2 = /* @__PURE__ */ new Date()) {
52445
52445
  return true;
52446
52446
  }
52447
52447
 
52448
+ // src/shared/channel/schema.ts
52449
+ var IdSchema = external_exports.string().regex(/^[A-Za-z0-9_-]{1,64}$/, "invalid id");
52450
+ var Text = (max = 4096) => external_exports.string().max(max);
52451
+ var TimeSchema = Text(64);
52452
+ var AttachEndpointSchema = external_exports.strictObject({
52453
+ addresses: external_exports.array(Text(64)).max(16),
52454
+ port: external_exports.number().int().min(1024).max(65535),
52455
+ token: Text(256)
52456
+ });
52457
+ var BootProgressSchema = external_exports.strictObject({
52458
+ step: external_exports.number().int().min(1).max(32),
52459
+ steps: external_exports.number().int().min(1).max(32),
52460
+ title: Text(120),
52461
+ /** Being set up now; percent while it downloads (when the size is known). */
52462
+ active: external_exports.array(external_exports.strictObject({ name: Text(64), percent: external_exports.number().int().min(0).max(100).optional() })).max(8),
52463
+ done: external_exports.number().int().min(0).max(1e3),
52464
+ total: external_exports.number().int().min(0).max(1e3)
52465
+ });
52466
+ var SandboxStatusSchema = external_exports.object({
52467
+ launchId: Text(64),
52468
+ phase: external_exports.enum(["booting", "provisioning", "ready", "failed"]),
52469
+ message: Text(),
52470
+ updatedAt: TimeSchema,
52471
+ workspaceHost: Text(),
52472
+ guestWorkspace: Text(),
52473
+ headless: external_exports.boolean(),
52474
+ /** The sandbox's IPv4 addresses (for network.forwardPorts; the host only uses ones on its Hyper-V network). */
52475
+ addresses: external_exports.array(external_exports.string().max(45)).max(8).optional(),
52476
+ /** A malformed progress is dropped, never the status (the phase still counts). */
52477
+ progress: BootProgressSchema.optional().catch(void 0)
52478
+ });
52479
+ var VersionSchema = external_exports.strictObject({ spec: Text(128), source: Text(256) });
52480
+ var DatabasePlanSchema = external_exports.strictObject({
52481
+ database: external_exports.enum(["postgres", "mysql", "sqlserver", "mongodb", "redis", "dynamodb"]),
52482
+ reasons: external_exports.array(Text(1024)).max(32),
52483
+ version: Text(64),
52484
+ port: external_exports.number().int().min(1024).max(65535),
52485
+ adminPort: external_exports.number().int().min(1024).max(65535).optional()
52486
+ });
52487
+ var ProjectSpecSchema = external_exports.strictObject({
52488
+ name: IdSchema,
52489
+ /** Inside the sandbox: C:\Projects\<name>. */
52490
+ workspace: Text(1024),
52491
+ workspaceHost: Text(1024),
52492
+ toolchains: ToolchainsSchema,
52493
+ versions: external_exports.strictObject({ node: VersionSchema, nodeExtra: external_exports.array(Text(128)).max(16), python: VersionSchema }),
52494
+ databases: external_exports.array(DatabasePlanSchema).max(8),
52495
+ /** Agents this project may start that the sandbox did not boot with (its .sandbox-config.json "harness"). */
52496
+ harnesses: external_exports.record(Text(64), HarnessSchema).refine((h) => Object.keys(h).length <= 8, "at most 8 agents"),
52497
+ /** Its hooks were staged in C:\AgentLaunch\projects\<name>\hooks. */
52498
+ hooks: external_exports.boolean()
52499
+ });
52500
+ var ShellChoiceSchema = external_exports.enum(["auto", "pwsh", "powershell", "cmd"]);
52501
+ var GuestRequestSchema = external_exports.discriminatedUnion("kind", [
52502
+ external_exports.strictObject({
52503
+ kind: external_exports.literal("session"),
52504
+ id: IdSchema,
52505
+ harness: Text(64),
52506
+ args: external_exports.array(Text()).max(256),
52507
+ cwd: Text(),
52508
+ project: ProjectSpecSchema.optional(),
52509
+ harnessSpec: HarnessSchema.optional(),
52510
+ /** A plain shell instead of an agent (harness is then "shell" and args are ignored). */
52511
+ shell: ShellChoiceSchema.optional(),
52512
+ createdAt: TimeSchema
52513
+ }),
52514
+ /** End a running session (an agent or a shell) and everything it started. */
52515
+ external_exports.strictObject({ kind: external_exports.literal("close"), id: IdSchema, session: IdSchema, createdAt: TimeSchema }),
52516
+ /** Provision a registered project without starting an agent (sandbox start / exec / db from its folder). */
52517
+ external_exports.strictObject({ kind: external_exports.literal("project"), id: IdSchema, project: ProjectSpecSchema, createdAt: TimeSchema }),
52518
+ external_exports.strictObject({
52519
+ kind: external_exports.literal("exec"),
52520
+ id: IdSchema,
52521
+ command: external_exports.array(Text()).min(1).max(256),
52522
+ cwd: Text(),
52523
+ timeoutSeconds: external_exports.number().positive().max(86400),
52524
+ createdAt: TimeSchema
52525
+ }),
52526
+ /** cwd: only the sessions in this folder or below (one project's); omitted = every session. */
52527
+ external_exports.strictObject({
52528
+ kind: external_exports.literal("restart"),
52529
+ id: IdSchema,
52530
+ harness: Text(64).nullable(),
52531
+ cwd: Text().optional(),
52532
+ createdAt: TimeSchema
52533
+ }),
52534
+ external_exports.strictObject({ kind: external_exports.literal("ping"), id: IdSchema, createdAt: TimeSchema }),
52535
+ /**
52536
+ * The PC's own memory or CPU is running low (the host watchdog): the sandbox slows its heaviest builds down for
52537
+ * `seconds` (it never stops them) so Windows does not have to close it. Repeated while the PC stays low.
52538
+ */
52539
+ external_exports.strictObject({
52540
+ kind: external_exports.literal("pressure"),
52541
+ id: IdSchema,
52542
+ reason: Text(300),
52543
+ seconds: external_exports.number().int().min(10).max(300),
52544
+ /** "severe": the PC is nearly out of memory; builds go to the governor's last step at once. */
52545
+ level: external_exports.enum(["slow", "severe"]).optional(),
52546
+ /** The disk queue the PC reported (shown in the heartbeat). */
52547
+ diskQueue: external_exports.number().min(0).max(1e5).optional(),
52548
+ createdAt: TimeSchema
52549
+ })
52550
+ ]);
52551
+ var PressureStatusSchema = external_exports.object({
52552
+ level: external_exports.enum(["ok", "throttled", "suspending"]),
52553
+ /** The governor's dial, 0 (nothing wrong) to 4 (one compiler at a time). */
52554
+ step: external_exports.number().int().min(0).max(4).optional(),
52555
+ profile: external_exports.enum(["normal", "gentle"]),
52556
+ /** Share of the sandbox's RAM still free and of its commit limit in use (percent). */
52557
+ ramFreePercent: external_exports.number().int().min(0).max(100),
52558
+ commitPercent: external_exports.number().int().min(0).max(100),
52559
+ /** Programs running at lower priority because of it. */
52560
+ lowered: external_exports.number().int().min(0).max(1024),
52561
+ /** Compilers waiting their turn right now (name, process id, seconds until they may go on). */
52562
+ 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),
52563
+ /** The CPU share the compilers get together (percent; 100 = no cap). */
52564
+ cpuPercent: external_exports.number().int().min(0).max(100).optional(),
52565
+ /** The PC itself asked for slowing down (its memory or CPU is low). */
52566
+ pcLow: external_exports.boolean(),
52567
+ lastAction: Text(300),
52568
+ updatedAt: TimeSchema
52569
+ });
52570
+ var HeartbeatSchema = external_exports.object({
52571
+ at: TimeSchema,
52572
+ /** Seconds since the sandbox booted. */
52573
+ uptimeS: external_exports.number().int().min(0).max(1e9),
52574
+ ramFreePct: external_exports.number().min(0).max(100),
52575
+ commitPct: external_exports.number().min(0).max(100),
52576
+ /** The governor's dial now, and the step this PC asked for. */
52577
+ step: external_exports.number().int().min(0).max(4),
52578
+ hostStep: external_exports.number().int().min(0).max(4),
52579
+ /** The biggest programs in the agents' job. */
52580
+ topProcesses: external_exports.array(external_exports.object({ name: Text(64), privateMb: external_exports.number().int().min(0).max(1e7) })).max(5),
52581
+ /** The disk queue this PC last reported while asking the sandbox to slow down. */
52582
+ diskQueueReported: external_exports.number().min(0).max(1e5).optional(),
52583
+ phase: external_exports.enum(["ready", "working"])
52584
+ });
52585
+ var GuestResponseSchema = external_exports.object({
52586
+ id: IdSchema,
52587
+ ok: external_exports.boolean(),
52588
+ message: Text(65536),
52589
+ exitCode: external_exports.number().int().optional(),
52590
+ stdout: Text(4 * 1024 * 1024).optional(),
52591
+ stderr: Text(4 * 1024 * 1024).optional(),
52592
+ finishedAt: TimeSchema
52593
+ });
52594
+ var SessionStatusSchema = external_exports.object({
52595
+ id: IdSchema,
52596
+ launchId: Text(64),
52597
+ harness: Text(64),
52598
+ /** A plain shell (harness is then the shell: pwsh, powershell or cmd); absent: an agent. */
52599
+ kind: external_exports.enum(["agent", "shell"]).optional(),
52600
+ cwd: Text(),
52601
+ /** The project it runs in: its hub name, or the launch folder's for the first project. */
52602
+ project: Text(64).optional(),
52603
+ phase: external_exports.enum(["starting", "running", "exited", "failed"]),
52604
+ message: Text(),
52605
+ updatedAt: TimeSchema,
52606
+ startedAt: TimeSchema,
52607
+ /** The agent's own conversation this session is in (Claude: the file name of its .jsonl), once known. */
52608
+ conversation: Text(128).optional(),
52609
+ exitCode: external_exports.number().int().optional(),
52610
+ attach: AttachEndpointSchema.optional()
52611
+ });
52612
+ var ProjectStatusSchema = external_exports.object({
52613
+ name: IdSchema,
52614
+ workspace: Text(1024),
52615
+ phase: external_exports.enum(["provisioning", "ready", "failed"]),
52616
+ message: Text(),
52617
+ updatedAt: TimeSchema,
52618
+ databases: external_exports.array(external_exports.object({ database: Text(32), port: external_exports.number().int() })).max(16),
52619
+ summary: external_exports.array(Text(512)).max(64)
52620
+ });
52621
+ var VpnStatusSchema = external_exports.object({
52622
+ state: external_exports.enum(["off", "starting", "up", "failed"]),
52623
+ message: Text(2e3),
52624
+ killSwitch: external_exports.boolean(),
52625
+ /** A direct connection was tried after the kill switch was applied: false = it still got out. */
52626
+ killSwitchHeld: external_exports.boolean().optional(),
52627
+ /** The server's name, e.g. uk2201.nordvpn.com. */
52628
+ server: Text(128).optional(),
52629
+ /** What the sandbox's traffic looks like from outside, once the tunnel is up. */
52630
+ publicIp: Text(64).optional(),
52631
+ updatedAt: TimeSchema
52632
+ });
52633
+
52634
+ // src/shared/channel/run-dir.ts
52635
+ import fs25 from "node:fs";
52636
+ import path32 from "node:path";
52637
+ function safeId(id) {
52638
+ return IdSchema.parse(id);
52639
+ }
52640
+ function now() {
52641
+ return (/* @__PURE__ */ new Date()).toISOString();
52642
+ }
52643
+ var scan = (schema, dir) => {
52644
+ if (!fs25.existsSync(dir)) return { valid: [], invalid: [] };
52645
+ const valid = [];
52646
+ const invalid = [];
52647
+ for (const name2 of fs25.readdirSync(dir)) {
52648
+ if (name2.endsWith(".tmp")) continue;
52649
+ const message = /^[A-Za-z0-9_-]{1,64}\.json$/.test(name2) ? readValid(schema, path32.join(dir, name2)) : null;
52650
+ if (message) valid.push(message);
52651
+ else invalid.push(path32.join(dir, name2));
52652
+ }
52653
+ return { valid, invalid };
52654
+ };
52655
+ var RunDir = class {
52656
+ root;
52657
+ sandboxFile;
52658
+ vpnFile;
52659
+ /** What the memory governor in the sandbox is doing (pressure.json). */
52660
+ pressureFile;
52661
+ /** The daemon's heartbeat every 10 seconds (heartbeat.json): what the sandbox did last. */
52662
+ heartbeatFile;
52663
+ /** What the sandbox's programs may really use (budget.json): processors, memory, build jobs, link slots. */
52664
+ budgetFile;
52665
+ requestsDir;
52666
+ responsesDir;
52667
+ sessionsDir;
52668
+ projectsDir;
52669
+ /** audit.processes: programs the sandbox started, in small .jsonl files the host takes in and deletes. */
52670
+ auditDir;
52671
+ /**
52672
+ * Each running session's process tree, recorded by its ptyhost (guest only: the daemon ends what a ptyhost that was
52673
+ * itself ended left running).
52674
+ */
52675
+ treesDir;
52676
+ /** One-shot secrets the host hands over (see secrets.ts). Next to run, not in it: shared-use users can change run. */
52677
+ secretsDir;
52678
+ /** Live folders (live.ts): signed host requests, and the daemon's replies. */
52679
+ liveDir;
52680
+ liveRepliesDir;
52681
+ constructor(dataDir) {
52682
+ this.root = path32.join(dataDir, "run");
52683
+ this.sandboxFile = path32.join(this.root, "sandbox.json");
52684
+ this.vpnFile = path32.join(this.root, "vpn.json");
52685
+ this.pressureFile = path32.join(this.root, "pressure.json");
52686
+ this.heartbeatFile = path32.join(this.root, "heartbeat.json");
52687
+ this.budgetFile = path32.join(this.root, "budget.json");
52688
+ this.requestsDir = path32.join(this.root, "requests");
52689
+ this.responsesDir = path32.join(this.root, "responses");
52690
+ this.sessionsDir = path32.join(this.root, "sessions");
52691
+ this.projectsDir = path32.join(this.root, "projects");
52692
+ this.auditDir = path32.join(this.root, "audit");
52693
+ this.treesDir = path32.join(this.root, "trees");
52694
+ this.secretsDir = path32.join(dataDir, "secrets");
52695
+ this.liveDir = path32.join(this.root, "live");
52696
+ this.liveRepliesDir = path32.join(this.root, "live-replies");
52697
+ }
52698
+ liveFile(id) {
52699
+ return path32.join(this.liveDir, safeId(id) + ".json");
52700
+ }
52701
+ liveReplyFile(id) {
52702
+ return path32.join(this.liveRepliesDir, safeId(id) + ".json");
52703
+ }
52704
+ requestFile(id) {
52705
+ return path32.join(this.requestsDir, safeId(id) + ".json");
52706
+ }
52707
+ responseFile(id) {
52708
+ return path32.join(this.responsesDir, safeId(id) + ".json");
52709
+ }
52710
+ sessionFile(id) {
52711
+ return path32.join(this.sessionsDir, safeId(id) + ".json");
52712
+ }
52713
+ treeFile(id) {
52714
+ return path32.join(this.treesDir, safeId(id) + ".json");
52715
+ }
52716
+ projectFile(name2) {
52717
+ return path32.join(this.projectsDir, safeId(name2) + ".json");
52718
+ }
52719
+ secretFile(id) {
52720
+ return path32.join(this.secretsDir, safeId(id) + ".json");
52721
+ }
52722
+ readSandboxStatus() {
52723
+ return readValid(SandboxStatusSchema, this.sandboxFile);
52724
+ }
52725
+ readVpnStatus() {
52726
+ return readValid(VpnStatusSchema, this.vpnFile);
52727
+ }
52728
+ readPressure() {
52729
+ return readValid(PressureStatusSchema, this.pressureFile);
52730
+ }
52731
+ readHeartbeat() {
52732
+ return readValid(HeartbeatSchema, this.heartbeatFile);
52733
+ }
52734
+ readSession(id) {
52735
+ return readValid(SessionStatusSchema, this.sessionFile(id));
52736
+ }
52737
+ readResponse(id) {
52738
+ return readValid(GuestResponseSchema, this.responseFile(id));
52739
+ }
52740
+ listSessions() {
52741
+ return scan(SessionStatusSchema, this.sessionsDir).valid.sort((a, b) => a.startedAt.localeCompare(b.startedAt));
52742
+ }
52743
+ readProjectStatus(name2) {
52744
+ return readValid(ProjectStatusSchema, this.projectFile(name2));
52745
+ }
52746
+ listProjectStatus() {
52747
+ return scan(ProjectStatusSchema, this.projectsDir).valid.slice(0, 64);
52748
+ }
52749
+ scanRequests() {
52750
+ return scan(GuestRequestSchema, this.requestsDir);
52751
+ }
52752
+ };
52753
+
52754
+ // src/shared/accounts/payloads.ts
52755
+ var KB = 1024;
52756
+ var base643 = (maxBytes) => external_exports.string().min(4).max(maxBytes).regex(/^[A-Za-z0-9+/]+={0,2}$/);
52757
+ var secretText = (max) => external_exports.string().min(1).max(max).regex(/^[^\r\n]+$/);
52758
+ var EpicPayloadSchema = external_exports.strictObject({
52759
+ data: external_exports.string().min(16).max(16384).regex(/^[A-Za-z0-9+/=_-]+$/)
52760
+ });
52761
+ var SteamPayloadSchema = external_exports.strictObject({
52762
+ account: external_exports.string().regex(/^[A-Za-z0-9_]{1,64}$/),
52763
+ configVdf: external_exports.string().min(1).max(256 * KB),
52764
+ sentries: external_exports.array(external_exports.strictObject({ name: external_exports.string().regex(/^ssfn\d{1,40}$/), base64: base643(64 * KB) })).max(4),
52765
+ /** Reserved for the Steam client's own login (very likely not portable between Windows users). */
52766
+ client: base643(256 * KB).optional()
52767
+ });
52768
+ var UnityPayloadSchema = external_exports.strictObject({
52769
+ ulf: external_exports.string().min(1).max(128 * KB).optional(),
52770
+ email: external_exports.string().email().max(256).optional(),
52771
+ password: secretText(1024).optional(),
52772
+ serial: secretText(64).optional()
52773
+ });
52774
+ var CfxPayloadSchema = external_exports.strictObject({
52775
+ licenseKey: external_exports.string().max(256).regex(/^cfxk_[A-Za-z0-9_]+$/),
52776
+ steamWebApiKey: external_exports.string().regex(/^[A-F0-9]{32}$/).optional()
52777
+ });
52778
+ var KeystorePayloadSchema = external_exports.strictObject({
52779
+ fileName: external_exports.string().regex(/^[A-Za-z0-9._-]{1,100}\.(jks|keystore|p12)$/i),
52780
+ jksBase64: base643(256 * KB),
52781
+ storePassword: secretText(1024),
52782
+ keyAlias: secretText(256),
52783
+ keyPassword: secretText(1024)
52784
+ });
52785
+ var PlayPayloadSchema = external_exports.strictObject({
52786
+ serviceAccountJson: external_exports.string().min(2).max(64 * KB).refine((text) => {
52787
+ try {
52788
+ const parsed = JSON.parse(text);
52789
+ return typeof parsed === "object" && parsed !== null && parsed.type === "service_account";
52790
+ } catch {
52791
+ return false;
52792
+ }
52793
+ }, 'not a Google service account key (its "type" is not "service_account")')
52794
+ });
52795
+ var ExpoPayloadSchema = external_exports.strictObject({
52796
+ token: external_exports.string().regex(/^[A-Za-z0-9_-]{8,512}$/)
52797
+ });
52798
+ var AppstorePayloadSchema = external_exports.strictObject({
52799
+ issuerId: external_exports.string().uuid(),
52800
+ keyId: external_exports.string().regex(/^[A-Z0-9]{10}$/),
52801
+ p8: external_exports.string().max(8 * KB).regex(/^-----BEGIN PRIVATE KEY-----\r?\n[A-Za-z0-9+/=\r\n]+-----END PRIVATE KEY-----\s*$/)
52802
+ });
52803
+ var PAYLOADS = {
52804
+ epic: EpicPayloadSchema,
52805
+ steam: SteamPayloadSchema,
52806
+ unity: UnityPayloadSchema,
52807
+ cfx: CfxPayloadSchema,
52808
+ keystore: KeystorePayloadSchema,
52809
+ play: PlayPayloadSchema,
52810
+ expo: ExpoPayloadSchema,
52811
+ appstore: AppstorePayloadSchema
52812
+ };
52813
+ var ServiceAccountsSchema = external_exports.strictObject({
52814
+ epic: EpicPayloadSchema.optional(),
52815
+ steam: SteamPayloadSchema.optional(),
52816
+ unity: UnityPayloadSchema.optional(),
52817
+ cfx: CfxPayloadSchema.optional(),
52818
+ keystore: KeystorePayloadSchema.optional(),
52819
+ play: PlayPayloadSchema.optional(),
52820
+ expo: ExpoPayloadSchema.optional(),
52821
+ appstore: AppstorePayloadSchema.optional()
52822
+ });
52823
+ var LaunchIdSchema = external_exports.string().regex(/^[A-Za-z0-9-]{1,64}$/);
52824
+ function handoffSchema(kind) {
52825
+ return external_exports.strictObject({
52826
+ kind: external_exports.literal(kind),
52827
+ launchId: LaunchIdSchema,
52828
+ payload: PAYLOADS[kind]
52829
+ });
52830
+ }
52831
+
52832
+ // src/shared/github/token.ts
52833
+ var GITHUB_TOKEN_KINDS = ["fine-grained", "classic"];
52834
+ var MIN_LENGTH = 20;
52835
+ var MAX_LENGTH = 255;
52836
+ var PRINTABLE = /^[\x21-\x7e]+$/;
52837
+ var GithubTokenSchema = external_exports.string().min(MIN_LENGTH).max(MAX_LENGTH).regex(PRINTABLE);
52838
+ var OTHER_TOKENS = {
52839
+ gho_: "This is an OAuth token (from gh auth login or gh auth token), which can reach everything your account can.",
52840
+ ghu_: "This is a GitHub App user token, not a personal access token.",
52841
+ ghs_: "This is a GitHub App installation token, not a personal access token.",
52842
+ ghr_: "This is a refresh token, not a personal access token."
52843
+ };
52844
+ var CREATE_FINE_GRAINED = "Create a fine-grained token at https://github.com/settings/personal-access-tokens/new with only the repositories and permissions the agent needs, then run: sandbox github login";
52845
+ function githubTokenKind(token) {
52846
+ if (token.startsWith("github_pat_")) return "fine-grained";
52847
+ if (token.startsWith("ghp_")) return "classic";
52848
+ return null;
52849
+ }
52850
+ function githubTokenProblem(token) {
52851
+ if (!token) return { message: "No token was typed.", fix: CREATE_FINE_GRAINED };
52852
+ if (!PRINTABLE.test(token))
52853
+ return {
52854
+ message: "The token may not contain spaces, line breaks or other special characters.",
52855
+ fix: "Copy the token again from GitHub (only the token itself) and run: sandbox github login"
52856
+ };
52857
+ if (token.length < MIN_LENGTH || token.length > MAX_LENGTH)
52858
+ return {
52859
+ message: "That is not the length of a GitHub token.",
52860
+ fix: "Copy the whole token again from GitHub and run: sandbox github login"
52861
+ };
52862
+ const other = Object.keys(OTHER_TOKENS).find((p) => token.startsWith(p));
52863
+ if (other) return { message: OTHER_TOKENS[other], fix: CREATE_FINE_GRAINED };
52864
+ if (!githubTokenKind(token))
52865
+ return {
52866
+ message: "That is not a GitHub personal access token (they start with github_pat_ or ghp_).",
52867
+ fix: CREATE_FINE_GRAINED
52868
+ };
52869
+ return null;
52870
+ }
52871
+
52872
+ // src/shared/channel/secrets.ts
52873
+ var Environment = external_exports.record(external_exports.string().max(256), external_exports.string().max(65536));
52874
+ var SecretsFileSchema = external_exports.strictObject({
52875
+ /** The agent the sandbox booted with: its harness environment. */
52876
+ harnessEnvironment: Environment.optional(),
52877
+ /** harnessSpec of a session request (an agent the sandbox did not boot with). */
52878
+ harnessSpecEnvironment: Environment.optional(),
52879
+ /** project.harnesses of a request, by agent name. */
52880
+ projectHarnessEnvironment: external_exports.record(external_exports.string().max(64), Environment).optional(),
52881
+ /** The login of this PC's Claude (claudeProfile.copyCredentials): the text of .credentials.json. */
52882
+ claudeCredentials: external_exports.string().max(1024 * 1024).optional(),
52883
+ /** VPN service credentials (vpn.provider's login), only for the launch's own sandbox. */
52884
+ vpn: external_exports.strictObject({ username: external_exports.string().min(1).max(256), password: external_exports.string().min(1).max(1024) }).optional(),
52885
+ /** The checked WireGuard config of vpn.provider wireguard-file (holds the private key). */
52886
+ wireguardConfig: external_exports.string().min(1).max(32 * 1024).optional(),
52887
+ /** The GitHub token you saved for sandboxes (github.shareToken), only with git access "full". */
52888
+ githubToken: GithubTokenSchema.optional(),
52889
+ /** Your saved service sign-ins (accounts.<kind>.share): each goes to its tool's own files at boot. */
52890
+ serviceAccounts: ServiceAccountsSchema.optional(),
52891
+ /** projects.liveFolders "hcs": this launch's key for signed live-folder requests (live.ts). */
52892
+ liveFoldersKey: external_exports.string().regex(/^[0-9a-f]{64}$/).optional()
52893
+ });
52894
+
52895
+ // src/shared/channel/live.ts
52896
+ import crypto3 from "node:crypto";
52897
+ var LIVE_SHARE_NAME = /^agentsbx-[0-9a-f]{8,32}$/;
52898
+ var LIVE_FOLDER_NAME = /^[a-z0-9][a-z0-9_-]{0,63}$/;
52899
+ var LIVE_KEY = /^[0-9a-f]{64}$/;
52900
+ var LIVE_TOKEN = /^[0-9a-f]{32}$/;
52901
+ var LIVE_MAX_AGE_MS = 2 * 60 * 1e3;
52902
+ var MAX_PAYLOAD = 4096;
52903
+ var LivePayloadSchema = external_exports.strictObject({
52904
+ v: external_exports.literal(1),
52905
+ /** canary: show the share, read token.txt, hide it again and reply with the token; map / unmap: the real folder. */
52906
+ kind: external_exports.enum(["canary", "map", "unmap"]),
52907
+ id: IdSchema,
52908
+ launchId: external_exports.string().min(1).max(64),
52909
+ share: external_exports.string().regex(LIVE_SHARE_NAME).optional(),
52910
+ folder: external_exports.string().regex(LIVE_FOLDER_NAME),
52911
+ readOnly: external_exports.boolean(),
52912
+ createdAt: external_exports.string().max(64)
52913
+ });
52914
+ var LiveEnvelopeSchema = external_exports.strictObject({
52915
+ payload: external_exports.string().min(2).max(MAX_PAYLOAD),
52916
+ mac: external_exports.string().regex(/^[0-9a-f]{64}$/)
52917
+ });
52918
+ var LiveReplySchema = external_exports.strictObject({
52919
+ id: IdSchema,
52920
+ ok: external_exports.boolean(),
52921
+ message: external_exports.string().max(1e3),
52922
+ token: external_exports.string().regex(LIVE_TOKEN).optional(),
52923
+ finishedAt: external_exports.string().max(64)
52924
+ });
52925
+ function mac3(key3, payload) {
52926
+ return crypto3.createHmac("sha256", Buffer.from(key3, "hex")).update(payload, "utf8").digest("hex");
52927
+ }
52928
+ function signLive(key3, payload) {
52929
+ if (!LIVE_KEY.test(key3)) throw new Error("not a live-folder key");
52930
+ const text = JSON.stringify(LivePayloadSchema.parse(payload));
52931
+ return { payload: text, mac: mac3(key3, text) };
52932
+ }
52933
+ function newShareName() {
52934
+ return "agentsbx-" + crypto3.randomBytes(8).toString("hex");
52935
+ }
52936
+ function newLiveKey() {
52937
+ return crypto3.randomBytes(32).toString("hex");
52938
+ }
52939
+
52940
+ // src/host/life/last-end.ts
52941
+ function readHeartbeat(dataPath) {
52942
+ try {
52943
+ const root = hostDataRoot(dataPath);
52944
+ const file2 = new RunDir(root).heartbeatFile;
52945
+ assertNoLinks(root, file2);
52946
+ return readValid(HeartbeatSchema, file2);
52947
+ } catch {
52948
+ return null;
52949
+ }
52950
+ }
52951
+ var clock = (iso) => {
52952
+ const at = new Date(iso);
52953
+ return Number.isNaN(at.getTime()) ? iso : at.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit", hour12: false });
52954
+ };
52955
+ function heartbeatLine(hb) {
52956
+ const top = hb.topProcesses.map((p) => safeText(p.name) + " " + p.privateMb + " MB").join(", ");
52957
+ return "last seen inside at " + clock(hb.at) + ": up " + Math.round(hb.uptimeS / 60) + " min, " + hb.ramFreePct + "% RAM free, " + hb.commitPct + "% commit used, builds at step " + hb.step + (hb.hostStep > 0 ? " (this PC asked for step " + hb.hostStep + ")" : "") + (hb.diskQueueReported !== void 0 ? ", this PC disk queue " + hb.diskQueueReported : "") + (top ? "; biggest: " + top : "");
52958
+ }
52959
+ function guessReason(hb) {
52960
+ if (hb && hb.diskQueueReported !== void 0 && hb.hostStep > 0)
52961
+ return "this PC's disks were overloaded (queue " + hb.diskQueueReported + ")";
52962
+ if (hb && (hb.ramFreePct < 8 || hb.commitPct > 92)) return "the sandbox was running out of memory";
52963
+ return "Windows stopped it (no reason was logged)";
52964
+ }
52965
+ function lastEndOf(life, heartbeat, running) {
52966
+ if (!life || running) return null;
52967
+ if (life.ended && !life.ended.cause) return null;
52968
+ const hb = heartbeat && Date.parse(heartbeat.at) >= Date.parse(life.started) - 6e4 ? heartbeat : null;
52969
+ const at = life.ended?.at ?? life.lastSeen;
52970
+ const why = life.ended?.cause ? safeText(life.ended.cause.text) : guessReason(hb);
52971
+ return { at, text: "ended unexpectedly " + clock(at) + ": " + why };
52972
+ }
52973
+ function readLastEnd(lifeRead, dataPath, running) {
52974
+ const life = lifeRead();
52975
+ return lastEndOf(life, life && !running ? readHeartbeat(dataPath) : null, running);
52976
+ }
52977
+
52448
52978
  // src/host/life/crash-report.ts
52449
52979
  var KEEP_REPORTS = 20;
52450
52980
  var MEMORY_TAIL_BYTES = 16 * 1024;
52451
52981
  var FREEZE_GAP_MS = 5 * 60 * 1e3;
52452
52982
  function crashDir(storageRoot) {
52453
- return path32.join(storageRoot, "logs", "crashes");
52983
+ return path33.join(storageRoot, "logs", "crashes");
52454
52984
  }
52455
52985
  function freezeLine(cause, lastSeen) {
52456
52986
  if (cause.kind !== "crash") return null;
@@ -52493,11 +53023,11 @@ function findDumps(dirs, since) {
52493
53023
  const found = [];
52494
53024
  for (const dir of dirs) {
52495
53025
  try {
52496
- for (const name2 of fs25.readdirSync(dir)) {
53026
+ for (const name2 of fs26.readdirSync(dir)) {
52497
53027
  if (!/\.dmp$/i.test(name2)) continue;
52498
- const stat = fs25.lstatSync(path32.join(dir, name2));
53028
+ const stat = fs26.lstatSync(path33.join(dir, name2));
52499
53029
  if (stat.isFile() && stat.mtime >= since)
52500
- found.push({ name: path32.join(dir, name2), at: stat.mtime.toISOString() });
53030
+ found.push({ name: path33.join(dir, name2), at: stat.mtime.toISOString() });
52501
53031
  }
52502
53032
  } catch {
52503
53033
  }
@@ -52506,30 +53036,30 @@ function findDumps(dirs, since) {
52506
53036
  }
52507
53037
  function launchHadVgpu(storageRoot, lastSeen) {
52508
53038
  try {
52509
- const root = path32.join(storageRoot, "launches");
53039
+ const root = path33.join(storageRoot, "launches");
52510
53040
  const limit = Date.parse(lastSeen) + 60 * 1e3;
52511
- const newest = fs25.readdirSync(root).map((name2) => ({ name: name2, at: fs25.lstatSync(path32.join(root, name2)).mtimeMs })).filter((l) => l.at <= limit).sort((a, b) => b.at - a.at)[0];
53041
+ const newest = fs26.readdirSync(root).map((name2) => ({ name: name2, at: fs26.lstatSync(path33.join(root, name2)).mtimeMs })).filter((l) => l.at <= limit).sort((a, b) => b.at - a.at)[0];
52512
53042
  if (!newest) return false;
52513
- const dir = path32.join(root, newest.name);
52514
- const wsb = fs25.readdirSync(dir).find((f) => f.endsWith(".wsb"));
52515
- return wsb ? /<VGpu>\s*Enable\s*<\/VGpu>/i.test(fs25.readFileSync(path32.join(dir, wsb), "utf8").slice(0, 65536)) : false;
53043
+ const dir = path33.join(root, newest.name);
53044
+ const wsb = fs26.readdirSync(dir).find((f) => f.endsWith(".wsb"));
53045
+ return wsb ? /<VGpu>\s*Enable\s*<\/VGpu>/i.test(fs26.readFileSync(path33.join(dir, wsb), "utf8").slice(0, 65536)) : false;
52516
53046
  } catch {
52517
53047
  return false;
52518
53048
  }
52519
53049
  }
52520
53050
  function hostMemoryTail(storageRoot, dataPath) {
52521
53051
  try {
52522
- const file2 = path32.join(storageRoot, "logs", "host-memory-" + path32.basename(dataPath) + ".log");
52523
- const stat = fs25.lstatSync(file2);
53052
+ const file2 = path33.join(storageRoot, "logs", "host-memory-" + path33.basename(dataPath) + ".log");
53053
+ const stat = fs26.lstatSync(file2);
52524
53054
  if (!stat.isFile()) return "";
52525
53055
  const length2 = Math.min(stat.size, MEMORY_TAIL_BYTES);
52526
- const fd = fs25.openSync(file2, "r");
53056
+ const fd = fs26.openSync(file2, "r");
52527
53057
  try {
52528
53058
  const buffer = Buffer.alloc(length2);
52529
- fs25.readSync(fd, buffer, 0, length2, stat.size - length2);
53059
+ fs26.readSync(fd, buffer, 0, length2, stat.size - length2);
52530
53060
  return buffer.toString("utf8");
52531
53061
  } finally {
52532
- fs25.closeSync(fd);
53062
+ fs26.closeSync(fd);
52533
53063
  }
52534
53064
  } catch {
52535
53065
  return "";
@@ -52571,6 +53101,7 @@ function crashReportText(cause, events, memoryLog, lastSeen, extra = {}) {
52571
53101
  ...hostLines.length ? hostLines : [" (none: the background sync was not running)"],
52572
53102
  ...extra.hostMemoryNow ? [" after the restart: " + extra.hostMemoryNow] : [],
52573
53103
  "",
53104
+ ...extra.lastInside ? [safeText(extra.lastInside), ""] : [],
52574
53105
  "The sandbox's memory log, last lines (C:\\AgentData\\logs\\memory-*.log inside it):",
52575
53106
  ...memoryLines,
52576
53107
  ""
@@ -52579,10 +53110,10 @@ function crashReportText(cause, events, memoryLog, lastSeen, extra = {}) {
52579
53110
  function newestCrashReport(storageRoot) {
52580
53111
  try {
52581
53112
  const dir = crashDir(storageRoot);
52582
- const newest = fs25.readdirSync(dir).filter((f) => /^crash-.*\.txt$/.test(f)).sort().pop();
53113
+ const newest = fs26.readdirSync(dir).filter((f) => /^crash-.*\.txt$/.test(f)).sort().pop();
52583
53114
  if (!newest) return null;
52584
- const file2 = path32.join(dir, newest);
52585
- return { file: file2, text: safeText(fs25.readFileSync(file2, "utf8").slice(0, 64 * 1024)) };
53115
+ const file2 = path33.join(dir, newest);
53116
+ return { file: file2, text: safeText(fs26.readFileSync(file2, "utf8").slice(0, 64 * 1024)) };
52586
53117
  } catch {
52587
53118
  return null;
52588
53119
  }
@@ -52591,19 +53122,19 @@ function memoryLogTail(dataPath) {
52591
53122
  try {
52592
53123
  const logs = hostDataFolder(dataPath, "logs");
52593
53124
  assertNoLinks(hostDataRoot(dataPath), logs);
52594
- const newest = fs25.readdirSync(logs).filter((f) => /^memory-\d{8}-\d{6}\.log$/.test(f)).sort().pop();
53125
+ const newest = fs26.readdirSync(logs).filter((f) => /^memory-\d{8}-\d{6}\.log$/.test(f)).sort().pop();
52595
53126
  if (!newest) return "";
52596
- const file2 = path32.join(logs, newest);
52597
- if (!fs25.lstatSync(file2).isFile()) return "";
52598
- const size = fs25.statSync(file2).size;
52599
- const fd = fs25.openSync(file2, "r");
53127
+ const file2 = path33.join(logs, newest);
53128
+ if (!fs26.lstatSync(file2).isFile()) return "";
53129
+ const size = fs26.statSync(file2).size;
53130
+ const fd = fs26.openSync(file2, "r");
52600
53131
  try {
52601
53132
  const length2 = Math.min(size, MEMORY_TAIL_BYTES);
52602
53133
  const buffer = Buffer.alloc(length2);
52603
- fs25.readSync(fd, buffer, 0, length2, size - length2);
53134
+ fs26.readSync(fd, buffer, 0, length2, size - length2);
52604
53135
  return buffer.toString("utf8");
52605
53136
  } finally {
52606
- fs25.closeSync(fd);
53137
+ fs26.closeSync(fd);
52607
53138
  }
52608
53139
  } catch {
52609
53140
  return "";
@@ -52614,22 +53145,24 @@ function saveCrashReport(storageRoot, dataPath, lastSeen) {
52614
53145
  const cause = explainStop(events);
52615
53146
  if (!cause || !["sandboxCrash", "crash", "memory"].includes(cause.kind)) return null;
52616
53147
  const dir = crashDir(storageRoot);
52617
- fs25.mkdirSync(dir, { recursive: true });
52618
- const file2 = path32.join(dir, "crash-" + cause.at.replace(/[:.]/g, "-") + ".txt");
53148
+ fs26.mkdirSync(dir, { recursive: true });
53149
+ const file2 = path33.join(dir, "crash-" + cause.at.replace(/[:.]/g, "-") + ".txt");
52619
53150
  const since = new Date(Date.parse(lastSeen) - 10 * 60 * 1e3);
52620
53151
  const windir = process.env.SystemRoot ?? "C:\\Windows";
52621
53152
  const memoryNow = readHostMemory(storageRoot);
52622
- fs25.writeFileSync(
53153
+ const heartbeat = readHeartbeat(dataPath);
53154
+ fs26.writeFileSync(
52623
53155
  file2,
52624
53156
  crashReportText(cause, events, memoryLogTail(dataPath), lastSeen, {
52625
53157
  hostMemoryLog: hostMemoryTail(storageRoot, dataPath),
52626
53158
  vgpu: launchHadVgpu(storageRoot, lastSeen),
52627
- dumps: findDumps([path32.join(windir, "Minidump"), windir], since),
53159
+ ...heartbeat ? { lastInside: heartbeatLine(heartbeat) } : {},
53160
+ dumps: findDumps([path33.join(windir, "Minidump"), windir], since),
52628
53161
  ...memoryNow ? { hostMemoryNow: hostMemoryLine(memoryNow) } : {}
52629
53162
  })
52630
53163
  );
52631
- for (const old of fs25.readdirSync(dir).filter((f) => f.startsWith("crash-")).sort().slice(0, -KEEP_REPORTS))
52632
- fs25.rmSync(path32.join(dir, old), { force: true });
53164
+ for (const old of fs26.readdirSync(dir).filter((f) => f.startsWith("crash-")).sort().slice(0, -KEEP_REPORTS))
53165
+ fs26.rmSync(path33.join(dir, old), { force: true });
52633
53166
  return file2;
52634
53167
  }
52635
53168
 
@@ -52728,25 +53261,25 @@ function updateRestartPending() {
52728
53261
  var UPDATE_RESTART_WARNING = "Windows is waiting to restart for updates and may do so on its own (often overnight), which closes the sandbox. Restart first, or pause updates: Settings > Windows Update > Pause updates.";
52729
53262
 
52730
53263
  // src/host/life/session-ledger.ts
52731
- import path33 from "node:path";
53264
+ import path34 from "node:path";
52732
53265
  var MAX_SESSIONS = 50;
52733
53266
  var MAX_TEXT = 1024;
52734
53267
  var AUTO_RESTARTS = 3;
52735
53268
  var AUTO_RESTART_WINDOW_MS = 6 * 60 * 60 * 1e3;
52736
- var Text = external_exports.string().max(MAX_TEXT);
53269
+ var Text2 = external_exports.string().max(MAX_TEXT);
52737
53270
  var LedgerSchema = external_exports.object({
52738
53271
  savedAt: external_exports.string().max(40),
52739
53272
  launch: external_exports.object({
52740
- primary: Text,
52741
- harness: Text.nullable(),
52742
- projects: external_exports.array(Text).max(MAX_SESSIONS),
53273
+ primary: Text2,
53274
+ harness: Text2.nullable(),
53275
+ projects: external_exports.array(Text2).max(MAX_SESSIONS),
52743
53276
  headless: external_exports.boolean(),
52744
- configPath: Text.nullable()
53277
+ configPath: Text2.nullable()
52745
53278
  }),
52746
53279
  sessions: external_exports.array(
52747
53280
  external_exports.object({
52748
- harness: Text,
52749
- project: Text,
53281
+ harness: Text2,
53282
+ project: Text2,
52750
53283
  conversation: external_exports.string().max(128).optional(),
52751
53284
  startedAt: external_exports.string().max(40)
52752
53285
  })
@@ -52778,7 +53311,7 @@ function recent(restarts, now2) {
52778
53311
  });
52779
53312
  }
52780
53313
  function describeLast(sessions) {
52781
- const names = sessions.map((s) => s.harness + " in " + (path33.win32.basename(s.project) || s.project));
53314
+ const names = sessions.map((s) => s.harness + " in " + (path34.win32.basename(s.project) || s.project));
52782
53315
  return names.join(", ") + " (" + sessions.length + " session" + (sessions.length === 1 ? "" : "s") + ")";
52783
53316
  }
52784
53317
  var SessionLedger = class {
@@ -52862,8 +53395,8 @@ var SessionLedger = class {
52862
53395
  var key = (s) => [s.harness, s.project.toLowerCase(), s.startedAt].join("|");
52863
53396
 
52864
53397
  // src/host/life/watchdog.ts
52865
- import fs26 from "node:fs";
52866
- import path34 from "node:path";
53398
+ import fs27 from "node:fs";
53399
+ import path35 from "node:path";
52867
53400
 
52868
53401
  // src/shared/memory/policy.ts
52869
53402
  var HOST_THRESHOLDS = {
@@ -52895,6 +53428,7 @@ function effectiveCpuLimit(cpuLimit, profile) {
52895
53428
  if (profile === "gentle") return Math.min(gentleCpuLimit(cpuLimit), cpuLimit === "none" ? 100 : cpuLimit);
52896
53429
  return cpuLimit === "none" ? null : cpuLimit;
52897
53430
  }
53431
+ var LOCAL_TARGET_MIN_BYTES = 25 * 1024 ** 3;
52898
53432
 
52899
53433
  // src/host/life/watchdog.ts
52900
53434
  var WATCHDOG_START = {
@@ -52903,6 +53437,7 @@ var WATCHDOG_START = {
52903
53437
  calm: 0,
52904
53438
  cpuHighSeconds: 0,
52905
53439
  diskHighSeconds: 0,
53440
+ diskSevereSeconds: 0,
52906
53441
  lastSentAt: 0
52907
53442
  };
52908
53443
  var RESEND_MS = 3e4;
@@ -52930,8 +53465,10 @@ function watchdogStep(prev, input2) {
52930
53465
  reason = "this PC's CPU has been above " + t.cpuPercent + "% for " + cpuHigh + " seconds";
52931
53466
  const diskQueue = input2.memory?.diskQueue;
52932
53467
  const diskHigh = diskQueue !== void 0 && diskQueue >= t.diskQueue ? prev.diskHighSeconds + input2.intervalSeconds : 0;
53468
+ const diskDouble = diskQueue !== void 0 && diskQueue >= 2 * t.diskQueue ? prev.diskSevereSeconds + input2.intervalSeconds : 0;
53469
+ const diskSevere = diskDouble >= t.diskSeconds || diskHigh >= 3 * t.diskSeconds;
52933
53470
  if (!reason && diskHigh >= t.diskSeconds)
52934
- reason = "this PC's disks have had a queue of " + diskQueue + " for " + diskHigh + " seconds";
53471
+ reason = diskSevere ? "this PC's disks are overloaded (queue " + diskQueue + " for " + diskHigh + " seconds)" : "this PC's disks have had a queue of " + diskQueue + " for " + diskHigh + " seconds";
52935
53472
  const calmNow = input2.memory ? lowReason(input2.memory, input2.reserveHostMb, t, 1.25) === "" : true;
52936
53473
  let low = prev.low;
52937
53474
  let calm = prev.calm;
@@ -52945,7 +53482,7 @@ function watchdogStep(prev, input2) {
52945
53482
  calm = 0;
52946
53483
  }
52947
53484
  }
52948
- const severe = low && input2.memory !== null && severeHost(input2.memory);
53485
+ const severe = low && (input2.memory !== null && severeHost(input2.memory) || diskSevere);
52949
53486
  const send = low && (input2.nowMs - prev.lastSentAt >= RESEND_MS || severe && !prev.severe);
52950
53487
  return {
52951
53488
  state: {
@@ -52954,6 +53491,7 @@ function watchdogStep(prev, input2) {
52954
53491
  calm,
52955
53492
  cpuHighSeconds: cpuHigh,
52956
53493
  diskHighSeconds: diskHigh,
53494
+ diskSevereSeconds: diskDouble,
52957
53495
  lastSentAt: send ? input2.nowMs : prev.lastSentAt
52958
53496
  },
52959
53497
  send,
@@ -52978,12 +53516,12 @@ function cpuPercentBetween(a, b) {
52978
53516
  var MAX_LOG_BYTES = 512 * 1024;
52979
53517
  function appendHostMemory(file2, memory, cpuPercent) {
52980
53518
  try {
52981
- fs26.mkdirSync(path34.dirname(file2), { recursive: true });
53519
+ fs27.mkdirSync(path35.dirname(file2), { recursive: true });
52982
53520
  try {
52983
- if (fs26.statSync(file2).size > MAX_LOG_BYTES) fs26.renameSync(file2, file2 + ".old");
53521
+ if (fs27.statSync(file2).size > MAX_LOG_BYTES) fs27.renameSync(file2, file2 + ".old");
52984
53522
  } catch {
52985
53523
  }
52986
- fs26.appendFileSync(file2, hostMemoryLine(memory) + (cpuPercent === null ? "" : " | CPU " + cpuPercent + "%") + "\n");
53524
+ fs27.appendFileSync(file2, hostMemoryLine(memory) + (cpuPercent === null ? "" : " | CPU " + cpuPercent + "%") + "\n");
52987
53525
  } catch {
52988
53526
  }
52989
53527
  }
@@ -52992,16 +53530,16 @@ var LoadSettingsSchema = external_exports.object({
52992
53530
  reserveHostMb: external_exports.number().int().nonnegative()
52993
53531
  });
52994
53532
  function loadFile(storageRoot, dataPath) {
52995
- return path34.join(storageRoot, "run", "load-" + path34.basename(dataPath) + ".json");
53533
+ return path35.join(storageRoot, "run", "load-" + path35.basename(dataPath) + ".json");
52996
53534
  }
52997
53535
  function writeLoadSettings(storageRoot, dataPath, settings) {
52998
53536
  const file2 = loadFile(storageRoot, dataPath);
52999
- fs26.mkdirSync(path34.dirname(file2), { recursive: true });
53000
- fs26.writeFileSync(file2, JSON.stringify(settings));
53537
+ fs27.mkdirSync(path35.dirname(file2), { recursive: true });
53538
+ fs27.writeFileSync(file2, JSON.stringify(settings));
53001
53539
  }
53002
53540
  function readLoadSettings(storageRoot, dataPath) {
53003
53541
  try {
53004
- return LoadSettingsSchema.parse(JSON.parse(fs26.readFileSync(loadFile(storageRoot, dataPath), "utf8")));
53542
+ return LoadSettingsSchema.parse(JSON.parse(fs27.readFileSync(loadFile(storageRoot, dataPath), "utf8")));
53005
53543
  } catch {
53006
53544
  return { profile: "normal", reserveHostMb: 8192 };
53007
53545
  }
@@ -53032,7 +53570,7 @@ function doctorRows(config3, harnessName, storageRoot, configPath) {
53032
53570
  ["Host nvm versions", probe.nvmDir ?? "(no NVM_HOME; versions will be downloaded)"],
53033
53571
  [
53034
53572
  "Host " + harnessName,
53035
- probe.harnessExe ? path35.join(probe.harnessExe.dir, probe.harnessExe.file) : "(none; will npm install)"
53573
+ probe.harnessExe ? path36.join(probe.harnessExe.dir, probe.harnessExe.file) : "(none; will npm install)"
53036
53574
  ],
53037
53575
  ["Host Git for Windows", probe.gitDir ?? "(none with bash; PortableGit will be downloaded)"],
53038
53576
  ["Storage root", storageRoot + (dirExists(storageRoot) ? "" : " (will be created)")]
@@ -53040,10 +53578,10 @@ function doctorRows(config3, harnessName, storageRoot, configPath) {
53040
53578
  }
53041
53579
 
53042
53580
  // src/host/sandbox/window.ts
53043
- import path36 from "node:path";
53581
+ import path37 from "node:path";
53044
53582
  import { spawn as spawn7, spawnSync as spawnSync11 } from "node:child_process";
53045
53583
  function stateFile(storageRoot) {
53046
- return path36.join(storageRoot, "run", "hidden-windows.json");
53584
+ return path37.join(storageRoot, "run", "hidden-windows.json");
53047
53585
  }
53048
53586
  function run(file2, args) {
53049
53587
  return new Promise((resolve, reject) => {
@@ -53081,19 +53619,19 @@ function showSandboxWindow(storageRoot) {
53081
53619
  }
53082
53620
 
53083
53621
  // src/host/sandbox/wsb-cli.ts
53084
- import fs27 from "node:fs";
53085
- import path37 from "node:path";
53622
+ import fs28 from "node:fs";
53623
+ import path38 from "node:path";
53086
53624
  import { spawnSync as spawnSync12 } from "node:child_process";
53087
53625
  var GUID = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i;
53088
53626
  var MAX_OUTPUT = 1024 * 1024;
53089
53627
  function wsbCandidates(env = process.env) {
53090
53628
  return [
53091
- env.LOCALAPPDATA ? path37.join(env.LOCALAPPDATA, "Microsoft", "WindowsApps", "wsb.exe") : "",
53092
- path37.join(env.SystemRoot ?? env.WINDIR ?? "C:\\Windows", "System32", "wsb.exe")
53629
+ env.LOCALAPPDATA ? path38.join(env.LOCALAPPDATA, "Microsoft", "WindowsApps", "wsb.exe") : "",
53630
+ path38.join(env.SystemRoot ?? env.WINDIR ?? "C:\\Windows", "System32", "wsb.exe")
53093
53631
  ].filter(Boolean);
53094
53632
  }
53095
- function findWsb(env = process.env, exists2 = fs27.existsSync) {
53096
- return process.platform === "win32" || exists2 !== fs27.existsSync ? wsbCandidates(env).find((p) => exists2(p)) ?? null : null;
53633
+ function findWsb(env = process.env, exists2 = fs28.existsSync) {
53634
+ return process.platform === "win32" || exists2 !== fs28.existsSync ? wsbCandidates(env).find((p) => exists2(p)) ?? null : null;
53097
53635
  }
53098
53636
  function parseVersion(out) {
53099
53637
  return /\d+\.\d+(?:\.\d+){0,2}/.exec(out)?.[0] ?? null;
@@ -53177,17 +53715,17 @@ function shareFolder(wsb, id, hostPath, sandboxPath, readOnly = false) {
53177
53715
  }
53178
53716
 
53179
53717
  // src/host/sharing/machine-paths.ts
53180
- import path38 from "node:path";
53718
+ import path39 from "node:path";
53181
53719
  function usersDir(env = process.env) {
53182
- return path38.join(machineRoot(env), "users");
53720
+ return path39.join(machineRoot(env), "users");
53183
53721
  }
53184
53722
  function activeDir(env = process.env) {
53185
- return path38.join(machineRoot(env), "active");
53723
+ return path39.join(machineRoot(env), "active");
53186
53724
  }
53187
53725
 
53188
53726
  // src/host/sharing/active-record.ts
53189
- import fs28 from "node:fs";
53190
- import path39 from "node:path";
53727
+ import fs29 from "node:fs";
53728
+ import path40 from "node:path";
53191
53729
  var SID = /^S-1-[0-9-]{3,180}$/;
53192
53730
  var TEXT = external_exports.string().max(1024);
53193
53731
  var MAX_RECORD_BYTES = 64 * 1024;
@@ -53215,17 +53753,17 @@ var ActiveRecords = class {
53215
53753
  readAll() {
53216
53754
  let names;
53217
53755
  try {
53218
- names = fs28.readdirSync(this.dir);
53756
+ names = fs29.readdirSync(this.dir);
53219
53757
  } catch {
53220
53758
  return [];
53221
53759
  }
53222
53760
  const records2 = [];
53223
53761
  for (const name2 of names) {
53224
53762
  if (!name2.toLowerCase().endsWith(".json")) continue;
53225
- const file2 = path39.join(this.dir, name2);
53763
+ const file2 = path40.join(this.dir, name2);
53226
53764
  let stat;
53227
53765
  try {
53228
- stat = fs28.lstatSync(file2);
53766
+ stat = fs29.lstatSync(file2);
53229
53767
  } catch {
53230
53768
  continue;
53231
53769
  }
@@ -53241,7 +53779,7 @@ var ActiveRecords = class {
53241
53779
  }
53242
53780
  fileOf(sid) {
53243
53781
  if (!SID.test(sid)) throw new Error("Not a Windows SID: " + sid);
53244
- return path39.join(this.dir, sid + ".json");
53782
+ return path40.join(this.dir, sid + ".json");
53245
53783
  }
53246
53784
  };
53247
53785
 
@@ -53328,13 +53866,13 @@ function runningOwner(records2 = new ActiveRecords(activeDir())) {
53328
53866
  }
53329
53867
 
53330
53868
  // src/host/sharing/borrow.ts
53331
- import path40 from "node:path";
53869
+ import path41 from "node:path";
53332
53870
  function sharedDataPath(record2, machine3 = machineRoot()) {
53333
- const users = path40.join(machine3, "users");
53334
- const dataPath = path40.resolve(record2.dataPath);
53335
- const parts = path40.relative(users, dataPath).split(path40.sep);
53871
+ const users = path41.join(machine3, "users");
53872
+ const dataPath = path41.resolve(record2.dataPath);
53873
+ const parts = path41.relative(users, dataPath).split(path41.sep);
53336
53874
  const inside = parts.length === 3 && parts[1].toLowerCase() === "data" && parts.every((p) => p && p !== "..");
53337
- if (!inside || path40.isAbsolute(path40.relative(users, dataPath)))
53875
+ if (!inside || path41.isAbsolute(path41.relative(users, dataPath)))
53338
53876
  fail(
53339
53877
  "The record of " + ownerName(record2.user) + "'s sandbox points outside the shared folder, so it is not used.",
53340
53878
  "Ask them to restart their sandbox."
@@ -53347,475 +53885,6 @@ function sharedDataPath(record2, machine3 = machineRoot()) {
53347
53885
  import fs33 from "node:fs";
53348
53886
  import path49 from "node:path";
53349
53887
 
53350
- // src/shared/channel/schema.ts
53351
- var IdSchema = external_exports.string().regex(/^[A-Za-z0-9_-]{1,64}$/, "invalid id");
53352
- var Text2 = (max = 4096) => external_exports.string().max(max);
53353
- var TimeSchema = Text2(64);
53354
- var AttachEndpointSchema = external_exports.strictObject({
53355
- addresses: external_exports.array(Text2(64)).max(16),
53356
- port: external_exports.number().int().min(1024).max(65535),
53357
- token: Text2(256)
53358
- });
53359
- var BootProgressSchema = external_exports.strictObject({
53360
- step: external_exports.number().int().min(1).max(32),
53361
- steps: external_exports.number().int().min(1).max(32),
53362
- title: Text2(120),
53363
- /** Being set up now; percent while it downloads (when the size is known). */
53364
- active: external_exports.array(external_exports.strictObject({ name: Text2(64), percent: external_exports.number().int().min(0).max(100).optional() })).max(8),
53365
- done: external_exports.number().int().min(0).max(1e3),
53366
- total: external_exports.number().int().min(0).max(1e3)
53367
- });
53368
- var SandboxStatusSchema = external_exports.object({
53369
- launchId: Text2(64),
53370
- phase: external_exports.enum(["booting", "provisioning", "ready", "failed"]),
53371
- message: Text2(),
53372
- updatedAt: TimeSchema,
53373
- workspaceHost: Text2(),
53374
- guestWorkspace: Text2(),
53375
- headless: external_exports.boolean(),
53376
- /** The sandbox's IPv4 addresses (for network.forwardPorts; the host only uses ones on its Hyper-V network). */
53377
- addresses: external_exports.array(external_exports.string().max(45)).max(8).optional(),
53378
- /** A malformed progress is dropped, never the status (the phase still counts). */
53379
- progress: BootProgressSchema.optional().catch(void 0)
53380
- });
53381
- var VersionSchema = external_exports.strictObject({ spec: Text2(128), source: Text2(256) });
53382
- var DatabasePlanSchema = external_exports.strictObject({
53383
- database: external_exports.enum(["postgres", "mysql", "sqlserver", "mongodb", "redis", "dynamodb"]),
53384
- reasons: external_exports.array(Text2(1024)).max(32),
53385
- version: Text2(64),
53386
- port: external_exports.number().int().min(1024).max(65535),
53387
- adminPort: external_exports.number().int().min(1024).max(65535).optional()
53388
- });
53389
- var ProjectSpecSchema = external_exports.strictObject({
53390
- name: IdSchema,
53391
- /** Inside the sandbox: C:\Projects\<name>. */
53392
- workspace: Text2(1024),
53393
- workspaceHost: Text2(1024),
53394
- toolchains: ToolchainsSchema,
53395
- versions: external_exports.strictObject({ node: VersionSchema, nodeExtra: external_exports.array(Text2(128)).max(16), python: VersionSchema }),
53396
- databases: external_exports.array(DatabasePlanSchema).max(8),
53397
- /** Agents this project may start that the sandbox did not boot with (its .sandbox-config.json "harness"). */
53398
- harnesses: external_exports.record(Text2(64), HarnessSchema).refine((h) => Object.keys(h).length <= 8, "at most 8 agents"),
53399
- /** Its hooks were staged in C:\AgentLaunch\projects\<name>\hooks. */
53400
- hooks: external_exports.boolean()
53401
- });
53402
- var ShellChoiceSchema = external_exports.enum(["auto", "pwsh", "powershell", "cmd"]);
53403
- var GuestRequestSchema = external_exports.discriminatedUnion("kind", [
53404
- external_exports.strictObject({
53405
- kind: external_exports.literal("session"),
53406
- id: IdSchema,
53407
- harness: Text2(64),
53408
- args: external_exports.array(Text2()).max(256),
53409
- cwd: Text2(),
53410
- project: ProjectSpecSchema.optional(),
53411
- harnessSpec: HarnessSchema.optional(),
53412
- /** A plain shell instead of an agent (harness is then "shell" and args are ignored). */
53413
- shell: ShellChoiceSchema.optional(),
53414
- createdAt: TimeSchema
53415
- }),
53416
- /** End a running session (an agent or a shell) and everything it started. */
53417
- external_exports.strictObject({ kind: external_exports.literal("close"), id: IdSchema, session: IdSchema, createdAt: TimeSchema }),
53418
- /** Provision a registered project without starting an agent (sandbox start / exec / db from its folder). */
53419
- external_exports.strictObject({ kind: external_exports.literal("project"), id: IdSchema, project: ProjectSpecSchema, createdAt: TimeSchema }),
53420
- external_exports.strictObject({
53421
- kind: external_exports.literal("exec"),
53422
- id: IdSchema,
53423
- command: external_exports.array(Text2()).min(1).max(256),
53424
- cwd: Text2(),
53425
- timeoutSeconds: external_exports.number().positive().max(86400),
53426
- createdAt: TimeSchema
53427
- }),
53428
- /** cwd: only the sessions in this folder or below (one project's); omitted = every session. */
53429
- external_exports.strictObject({
53430
- kind: external_exports.literal("restart"),
53431
- id: IdSchema,
53432
- harness: Text2(64).nullable(),
53433
- cwd: Text2().optional(),
53434
- createdAt: TimeSchema
53435
- }),
53436
- external_exports.strictObject({ kind: external_exports.literal("ping"), id: IdSchema, createdAt: TimeSchema }),
53437
- /**
53438
- * The PC's own memory or CPU is running low (the host watchdog): the sandbox slows its heaviest builds down for
53439
- * `seconds` (it never stops them) so Windows does not have to close it. Repeated while the PC stays low.
53440
- */
53441
- external_exports.strictObject({
53442
- kind: external_exports.literal("pressure"),
53443
- id: IdSchema,
53444
- reason: Text2(300),
53445
- seconds: external_exports.number().int().min(10).max(300),
53446
- /** "severe": the PC is nearly out of memory; builds go to the governor's last step at once. */
53447
- level: external_exports.enum(["slow", "severe"]).optional(),
53448
- createdAt: TimeSchema
53449
- })
53450
- ]);
53451
- var PressureStatusSchema = external_exports.object({
53452
- level: external_exports.enum(["ok", "throttled", "suspending"]),
53453
- /** The governor's dial, 0 (nothing wrong) to 4 (one compiler at a time). */
53454
- step: external_exports.number().int().min(0).max(4).optional(),
53455
- profile: external_exports.enum(["normal", "gentle"]),
53456
- /** Share of the sandbox's RAM still free and of its commit limit in use (percent). */
53457
- ramFreePercent: external_exports.number().int().min(0).max(100),
53458
- commitPercent: external_exports.number().int().min(0).max(100),
53459
- /** Programs running at lower priority because of it. */
53460
- lowered: external_exports.number().int().min(0).max(1024),
53461
- /** Compilers waiting their turn right now (name, process id, seconds until they may go on). */
53462
- paused: external_exports.array(external_exports.object({ name: Text2(64), pid: external_exports.number().int(), seconds: external_exports.number().int().min(0).max(60) })).max(8),
53463
- /** The CPU share the compilers get together (percent; 100 = no cap). */
53464
- cpuPercent: external_exports.number().int().min(0).max(100).optional(),
53465
- /** The PC itself asked for slowing down (its memory or CPU is low). */
53466
- pcLow: external_exports.boolean(),
53467
- lastAction: Text2(300),
53468
- updatedAt: TimeSchema
53469
- });
53470
- var GuestResponseSchema = external_exports.object({
53471
- id: IdSchema,
53472
- ok: external_exports.boolean(),
53473
- message: Text2(65536),
53474
- exitCode: external_exports.number().int().optional(),
53475
- stdout: Text2(4 * 1024 * 1024).optional(),
53476
- stderr: Text2(4 * 1024 * 1024).optional(),
53477
- finishedAt: TimeSchema
53478
- });
53479
- var SessionStatusSchema = external_exports.object({
53480
- id: IdSchema,
53481
- launchId: Text2(64),
53482
- harness: Text2(64),
53483
- /** A plain shell (harness is then the shell: pwsh, powershell or cmd); absent: an agent. */
53484
- kind: external_exports.enum(["agent", "shell"]).optional(),
53485
- cwd: Text2(),
53486
- /** The project it runs in: its hub name, or the launch folder's for the first project. */
53487
- project: Text2(64).optional(),
53488
- phase: external_exports.enum(["starting", "running", "exited", "failed"]),
53489
- message: Text2(),
53490
- updatedAt: TimeSchema,
53491
- startedAt: TimeSchema,
53492
- /** The agent's own conversation this session is in (Claude: the file name of its .jsonl), once known. */
53493
- conversation: Text2(128).optional(),
53494
- exitCode: external_exports.number().int().optional(),
53495
- attach: AttachEndpointSchema.optional()
53496
- });
53497
- var ProjectStatusSchema = external_exports.object({
53498
- name: IdSchema,
53499
- workspace: Text2(1024),
53500
- phase: external_exports.enum(["provisioning", "ready", "failed"]),
53501
- message: Text2(),
53502
- updatedAt: TimeSchema,
53503
- databases: external_exports.array(external_exports.object({ database: Text2(32), port: external_exports.number().int() })).max(16),
53504
- summary: external_exports.array(Text2(512)).max(64)
53505
- });
53506
- var VpnStatusSchema = external_exports.object({
53507
- state: external_exports.enum(["off", "starting", "up", "failed"]),
53508
- message: Text2(2e3),
53509
- killSwitch: external_exports.boolean(),
53510
- /** A direct connection was tried after the kill switch was applied: false = it still got out. */
53511
- killSwitchHeld: external_exports.boolean().optional(),
53512
- /** The server's name, e.g. uk2201.nordvpn.com. */
53513
- server: Text2(128).optional(),
53514
- /** What the sandbox's traffic looks like from outside, once the tunnel is up. */
53515
- publicIp: Text2(64).optional(),
53516
- updatedAt: TimeSchema
53517
- });
53518
-
53519
- // src/shared/channel/run-dir.ts
53520
- import fs29 from "node:fs";
53521
- import path41 from "node:path";
53522
- function safeId(id) {
53523
- return IdSchema.parse(id);
53524
- }
53525
- function now() {
53526
- return (/* @__PURE__ */ new Date()).toISOString();
53527
- }
53528
- var scan = (schema, dir) => {
53529
- if (!fs29.existsSync(dir)) return { valid: [], invalid: [] };
53530
- const valid = [];
53531
- const invalid = [];
53532
- for (const name2 of fs29.readdirSync(dir)) {
53533
- if (name2.endsWith(".tmp")) continue;
53534
- const message = /^[A-Za-z0-9_-]{1,64}\.json$/.test(name2) ? readValid(schema, path41.join(dir, name2)) : null;
53535
- if (message) valid.push(message);
53536
- else invalid.push(path41.join(dir, name2));
53537
- }
53538
- return { valid, invalid };
53539
- };
53540
- var RunDir = class {
53541
- root;
53542
- sandboxFile;
53543
- vpnFile;
53544
- /** What the memory governor in the sandbox is doing (pressure.json). */
53545
- pressureFile;
53546
- /** What the sandbox's programs may really use (budget.json): processors, memory, build jobs, link slots. */
53547
- budgetFile;
53548
- requestsDir;
53549
- responsesDir;
53550
- sessionsDir;
53551
- projectsDir;
53552
- /** audit.processes: programs the sandbox started, in small .jsonl files the host takes in and deletes. */
53553
- auditDir;
53554
- /**
53555
- * Each running session's process tree, recorded by its ptyhost (guest only: the daemon ends what a ptyhost that was
53556
- * itself ended left running).
53557
- */
53558
- treesDir;
53559
- /** One-shot secrets the host hands over (see secrets.ts). Next to run, not in it: shared-use users can change run. */
53560
- secretsDir;
53561
- /** Live folders (live.ts): signed host requests, and the daemon's replies. */
53562
- liveDir;
53563
- liveRepliesDir;
53564
- constructor(dataDir) {
53565
- this.root = path41.join(dataDir, "run");
53566
- this.sandboxFile = path41.join(this.root, "sandbox.json");
53567
- this.vpnFile = path41.join(this.root, "vpn.json");
53568
- this.pressureFile = path41.join(this.root, "pressure.json");
53569
- this.budgetFile = path41.join(this.root, "budget.json");
53570
- this.requestsDir = path41.join(this.root, "requests");
53571
- this.responsesDir = path41.join(this.root, "responses");
53572
- this.sessionsDir = path41.join(this.root, "sessions");
53573
- this.projectsDir = path41.join(this.root, "projects");
53574
- this.auditDir = path41.join(this.root, "audit");
53575
- this.treesDir = path41.join(this.root, "trees");
53576
- this.secretsDir = path41.join(dataDir, "secrets");
53577
- this.liveDir = path41.join(this.root, "live");
53578
- this.liveRepliesDir = path41.join(this.root, "live-replies");
53579
- }
53580
- liveFile(id) {
53581
- return path41.join(this.liveDir, safeId(id) + ".json");
53582
- }
53583
- liveReplyFile(id) {
53584
- return path41.join(this.liveRepliesDir, safeId(id) + ".json");
53585
- }
53586
- requestFile(id) {
53587
- return path41.join(this.requestsDir, safeId(id) + ".json");
53588
- }
53589
- responseFile(id) {
53590
- return path41.join(this.responsesDir, safeId(id) + ".json");
53591
- }
53592
- sessionFile(id) {
53593
- return path41.join(this.sessionsDir, safeId(id) + ".json");
53594
- }
53595
- treeFile(id) {
53596
- return path41.join(this.treesDir, safeId(id) + ".json");
53597
- }
53598
- projectFile(name2) {
53599
- return path41.join(this.projectsDir, safeId(name2) + ".json");
53600
- }
53601
- secretFile(id) {
53602
- return path41.join(this.secretsDir, safeId(id) + ".json");
53603
- }
53604
- readSandboxStatus() {
53605
- return readValid(SandboxStatusSchema, this.sandboxFile);
53606
- }
53607
- readVpnStatus() {
53608
- return readValid(VpnStatusSchema, this.vpnFile);
53609
- }
53610
- readPressure() {
53611
- return readValid(PressureStatusSchema, this.pressureFile);
53612
- }
53613
- readSession(id) {
53614
- return readValid(SessionStatusSchema, this.sessionFile(id));
53615
- }
53616
- readResponse(id) {
53617
- return readValid(GuestResponseSchema, this.responseFile(id));
53618
- }
53619
- listSessions() {
53620
- return scan(SessionStatusSchema, this.sessionsDir).valid.sort((a, b) => a.startedAt.localeCompare(b.startedAt));
53621
- }
53622
- readProjectStatus(name2) {
53623
- return readValid(ProjectStatusSchema, this.projectFile(name2));
53624
- }
53625
- listProjectStatus() {
53626
- return scan(ProjectStatusSchema, this.projectsDir).valid.slice(0, 64);
53627
- }
53628
- scanRequests() {
53629
- return scan(GuestRequestSchema, this.requestsDir);
53630
- }
53631
- };
53632
-
53633
- // src/shared/accounts/payloads.ts
53634
- var KB = 1024;
53635
- var base643 = (maxBytes) => external_exports.string().min(4).max(maxBytes).regex(/^[A-Za-z0-9+/]+={0,2}$/);
53636
- var secretText = (max) => external_exports.string().min(1).max(max).regex(/^[^\r\n]+$/);
53637
- var EpicPayloadSchema = external_exports.strictObject({
53638
- data: external_exports.string().min(16).max(16384).regex(/^[A-Za-z0-9+/=_-]+$/)
53639
- });
53640
- var SteamPayloadSchema = external_exports.strictObject({
53641
- account: external_exports.string().regex(/^[A-Za-z0-9_]{1,64}$/),
53642
- configVdf: external_exports.string().min(1).max(256 * KB),
53643
- sentries: external_exports.array(external_exports.strictObject({ name: external_exports.string().regex(/^ssfn\d{1,40}$/), base64: base643(64 * KB) })).max(4),
53644
- /** Reserved for the Steam client's own login (very likely not portable between Windows users). */
53645
- client: base643(256 * KB).optional()
53646
- });
53647
- var UnityPayloadSchema = external_exports.strictObject({
53648
- ulf: external_exports.string().min(1).max(128 * KB).optional(),
53649
- email: external_exports.string().email().max(256).optional(),
53650
- password: secretText(1024).optional(),
53651
- serial: secretText(64).optional()
53652
- });
53653
- var CfxPayloadSchema = external_exports.strictObject({
53654
- licenseKey: external_exports.string().max(256).regex(/^cfxk_[A-Za-z0-9_]+$/),
53655
- steamWebApiKey: external_exports.string().regex(/^[A-F0-9]{32}$/).optional()
53656
- });
53657
- var KeystorePayloadSchema = external_exports.strictObject({
53658
- fileName: external_exports.string().regex(/^[A-Za-z0-9._-]{1,100}\.(jks|keystore|p12)$/i),
53659
- jksBase64: base643(256 * KB),
53660
- storePassword: secretText(1024),
53661
- keyAlias: secretText(256),
53662
- keyPassword: secretText(1024)
53663
- });
53664
- var PlayPayloadSchema = external_exports.strictObject({
53665
- serviceAccountJson: external_exports.string().min(2).max(64 * KB).refine((text) => {
53666
- try {
53667
- const parsed = JSON.parse(text);
53668
- return typeof parsed === "object" && parsed !== null && parsed.type === "service_account";
53669
- } catch {
53670
- return false;
53671
- }
53672
- }, 'not a Google service account key (its "type" is not "service_account")')
53673
- });
53674
- var ExpoPayloadSchema = external_exports.strictObject({
53675
- token: external_exports.string().regex(/^[A-Za-z0-9_-]{8,512}$/)
53676
- });
53677
- var AppstorePayloadSchema = external_exports.strictObject({
53678
- issuerId: external_exports.string().uuid(),
53679
- keyId: external_exports.string().regex(/^[A-Z0-9]{10}$/),
53680
- p8: external_exports.string().max(8 * KB).regex(/^-----BEGIN PRIVATE KEY-----\r?\n[A-Za-z0-9+/=\r\n]+-----END PRIVATE KEY-----\s*$/)
53681
- });
53682
- var PAYLOADS = {
53683
- epic: EpicPayloadSchema,
53684
- steam: SteamPayloadSchema,
53685
- unity: UnityPayloadSchema,
53686
- cfx: CfxPayloadSchema,
53687
- keystore: KeystorePayloadSchema,
53688
- play: PlayPayloadSchema,
53689
- expo: ExpoPayloadSchema,
53690
- appstore: AppstorePayloadSchema
53691
- };
53692
- var ServiceAccountsSchema = external_exports.strictObject({
53693
- epic: EpicPayloadSchema.optional(),
53694
- steam: SteamPayloadSchema.optional(),
53695
- unity: UnityPayloadSchema.optional(),
53696
- cfx: CfxPayloadSchema.optional(),
53697
- keystore: KeystorePayloadSchema.optional(),
53698
- play: PlayPayloadSchema.optional(),
53699
- expo: ExpoPayloadSchema.optional(),
53700
- appstore: AppstorePayloadSchema.optional()
53701
- });
53702
- var LaunchIdSchema = external_exports.string().regex(/^[A-Za-z0-9-]{1,64}$/);
53703
- function handoffSchema(kind) {
53704
- return external_exports.strictObject({
53705
- kind: external_exports.literal(kind),
53706
- launchId: LaunchIdSchema,
53707
- payload: PAYLOADS[kind]
53708
- });
53709
- }
53710
-
53711
- // src/shared/github/token.ts
53712
- var GITHUB_TOKEN_KINDS = ["fine-grained", "classic"];
53713
- var MIN_LENGTH = 20;
53714
- var MAX_LENGTH = 255;
53715
- var PRINTABLE = /^[\x21-\x7e]+$/;
53716
- var GithubTokenSchema = external_exports.string().min(MIN_LENGTH).max(MAX_LENGTH).regex(PRINTABLE);
53717
- var OTHER_TOKENS = {
53718
- gho_: "This is an OAuth token (from gh auth login or gh auth token), which can reach everything your account can.",
53719
- ghu_: "This is a GitHub App user token, not a personal access token.",
53720
- ghs_: "This is a GitHub App installation token, not a personal access token.",
53721
- ghr_: "This is a refresh token, not a personal access token."
53722
- };
53723
- var CREATE_FINE_GRAINED = "Create a fine-grained token at https://github.com/settings/personal-access-tokens/new with only the repositories and permissions the agent needs, then run: sandbox github login";
53724
- function githubTokenKind(token) {
53725
- if (token.startsWith("github_pat_")) return "fine-grained";
53726
- if (token.startsWith("ghp_")) return "classic";
53727
- return null;
53728
- }
53729
- function githubTokenProblem(token) {
53730
- if (!token) return { message: "No token was typed.", fix: CREATE_FINE_GRAINED };
53731
- if (!PRINTABLE.test(token))
53732
- return {
53733
- message: "The token may not contain spaces, line breaks or other special characters.",
53734
- fix: "Copy the token again from GitHub (only the token itself) and run: sandbox github login"
53735
- };
53736
- if (token.length < MIN_LENGTH || token.length > MAX_LENGTH)
53737
- return {
53738
- message: "That is not the length of a GitHub token.",
53739
- fix: "Copy the whole token again from GitHub and run: sandbox github login"
53740
- };
53741
- const other = Object.keys(OTHER_TOKENS).find((p) => token.startsWith(p));
53742
- if (other) return { message: OTHER_TOKENS[other], fix: CREATE_FINE_GRAINED };
53743
- if (!githubTokenKind(token))
53744
- return {
53745
- message: "That is not a GitHub personal access token (they start with github_pat_ or ghp_).",
53746
- fix: CREATE_FINE_GRAINED
53747
- };
53748
- return null;
53749
- }
53750
-
53751
- // src/shared/channel/secrets.ts
53752
- var Environment = external_exports.record(external_exports.string().max(256), external_exports.string().max(65536));
53753
- var SecretsFileSchema = external_exports.strictObject({
53754
- /** The agent the sandbox booted with: its harness environment. */
53755
- harnessEnvironment: Environment.optional(),
53756
- /** harnessSpec of a session request (an agent the sandbox did not boot with). */
53757
- harnessSpecEnvironment: Environment.optional(),
53758
- /** project.harnesses of a request, by agent name. */
53759
- projectHarnessEnvironment: external_exports.record(external_exports.string().max(64), Environment).optional(),
53760
- /** The login of this PC's Claude (claudeProfile.copyCredentials): the text of .credentials.json. */
53761
- claudeCredentials: external_exports.string().max(1024 * 1024).optional(),
53762
- /** VPN service credentials (vpn.provider's login), only for the launch's own sandbox. */
53763
- vpn: external_exports.strictObject({ username: external_exports.string().min(1).max(256), password: external_exports.string().min(1).max(1024) }).optional(),
53764
- /** The checked WireGuard config of vpn.provider wireguard-file (holds the private key). */
53765
- wireguardConfig: external_exports.string().min(1).max(32 * 1024).optional(),
53766
- /** The GitHub token you saved for sandboxes (github.shareToken), only with git access "full". */
53767
- githubToken: GithubTokenSchema.optional(),
53768
- /** Your saved service sign-ins (accounts.<kind>.share): each goes to its tool's own files at boot. */
53769
- serviceAccounts: ServiceAccountsSchema.optional(),
53770
- /** projects.liveFolders "hcs": this launch's key for signed live-folder requests (live.ts). */
53771
- liveFoldersKey: external_exports.string().regex(/^[0-9a-f]{64}$/).optional()
53772
- });
53773
-
53774
- // src/shared/channel/live.ts
53775
- import crypto3 from "node:crypto";
53776
- var LIVE_SHARE_NAME = /^agentsbx-[0-9a-f]{8,32}$/;
53777
- var LIVE_FOLDER_NAME = /^[a-z0-9][a-z0-9_-]{0,63}$/;
53778
- var LIVE_KEY = /^[0-9a-f]{64}$/;
53779
- var LIVE_TOKEN = /^[0-9a-f]{32}$/;
53780
- var LIVE_MAX_AGE_MS = 2 * 60 * 1e3;
53781
- var MAX_PAYLOAD = 4096;
53782
- var LivePayloadSchema = external_exports.strictObject({
53783
- v: external_exports.literal(1),
53784
- /** canary: show the share, read token.txt, hide it again and reply with the token; map / unmap: the real folder. */
53785
- kind: external_exports.enum(["canary", "map", "unmap"]),
53786
- id: IdSchema,
53787
- launchId: external_exports.string().min(1).max(64),
53788
- share: external_exports.string().regex(LIVE_SHARE_NAME).optional(),
53789
- folder: external_exports.string().regex(LIVE_FOLDER_NAME),
53790
- readOnly: external_exports.boolean(),
53791
- createdAt: external_exports.string().max(64)
53792
- });
53793
- var LiveEnvelopeSchema = external_exports.strictObject({
53794
- payload: external_exports.string().min(2).max(MAX_PAYLOAD),
53795
- mac: external_exports.string().regex(/^[0-9a-f]{64}$/)
53796
- });
53797
- var LiveReplySchema = external_exports.strictObject({
53798
- id: IdSchema,
53799
- ok: external_exports.boolean(),
53800
- message: external_exports.string().max(1e3),
53801
- token: external_exports.string().regex(LIVE_TOKEN).optional(),
53802
- finishedAt: external_exports.string().max(64)
53803
- });
53804
- function mac3(key3, payload) {
53805
- return crypto3.createHmac("sha256", Buffer.from(key3, "hex")).update(payload, "utf8").digest("hex");
53806
- }
53807
- function signLive(key3, payload) {
53808
- if (!LIVE_KEY.test(key3)) throw new Error("not a live-folder key");
53809
- const text = JSON.stringify(LivePayloadSchema.parse(payload));
53810
- return { payload: text, mac: mac3(key3, text) };
53811
- }
53812
- function newShareName() {
53813
- return "agentsbx-" + crypto3.randomBytes(8).toString("hex");
53814
- }
53815
- function newLiveKey() {
53816
- return crypto3.randomBytes(32).toString("hex");
53817
- }
53818
-
53819
53888
  // src/host/install/layout.ts
53820
53889
  import fs30 from "node:fs";
53821
53890
  import path42 from "node:path";
@@ -58473,7 +58542,7 @@ var GUEST = {
58473
58542
  var MB = 1048576;
58474
58543
  var MIN_AUTO_MEMORY_MB = 4096;
58475
58544
  var roundDown = (mb) => Math.floor(mb / 1024) * 1024;
58476
- var VGPU_RESERVE_MB = 2048;
58545
+ var VGPU_RESERVE_MB = 4096;
58477
58546
  function residentCapMb(sandbox, totalMb) {
58478
58547
  const keep = Math.max(sandbox.reserveHostMb, Math.floor(totalMb / 4)) + (sandbox.vGpu ? VGPU_RESERVE_MB : 0);
58479
58548
  return roundDown(totalMb - keep);
@@ -58488,8 +58557,9 @@ function residentNote(asked, given) {
58488
58557
  }
58489
58558
  function decideMemory(sandbox, freeMb, totalMb, commitAvailableMb) {
58490
58559
  const shareMb = Math.floor(totalMb * sandbox.maxMemoryPercent / 100);
58491
- const byFree = freeMb - sandbox.reserveHostMb;
58492
- const byCommit = commitAvailableMb === null ? Infinity : commitAvailableMb - sandbox.reserveHostMb;
58560
+ const reserveMb = sandbox.reserveHostMb + (sandbox.vGpu ? VGPU_RESERVE_MB : 0);
58561
+ const byFree = freeMb - reserveMb;
58562
+ const byCommit = commitAvailableMb === null ? Infinity : commitAvailableMb - reserveMb;
58493
58563
  const candidates = [
58494
58564
  ["max", sandbox.maxMemoryMb],
58495
58565
  ["share", shareMb],
@@ -62343,8 +62413,16 @@ var SandboxChannel = class {
62343
62413
  return this.waitForResponse(requestId, 3e4);
62344
62414
  }
62345
62415
  /** The PC is low on memory or CPU: the sandbox slows its builds down for `seconds`. Never waits for an answer. */
62346
- pressure(reason, seconds, level = "slow") {
62347
- this.send({ kind: "pressure", id: newId(), reason, seconds, level, createdAt: now() });
62416
+ pressure(reason, seconds, level = "slow", diskQueue) {
62417
+ this.send({
62418
+ kind: "pressure",
62419
+ id: newId(),
62420
+ reason,
62421
+ seconds,
62422
+ level,
62423
+ ...diskQueue === void 0 ? {} : { diskQueue },
62424
+ createdAt: now()
62425
+ });
62348
62426
  }
62349
62427
  async ping() {
62350
62428
  const requestId = newId();
@@ -64083,7 +64161,12 @@ var Watcher = class {
64083
64161
  step.state.low ? "this PC is low (" + step.reason + "): asking the sandbox to slow its builds down" : "this PC has recovered: no longer asking the sandbox to slow down"
64084
64162
  );
64085
64163
  if (step.send)
64086
- new SandboxChannel(this.dataPath).pressure(step.reason, REQUEST_SECONDS, step.severe ? "severe" : "slow");
64164
+ new SandboxChannel(this.dataPath).pressure(
64165
+ step.reason,
64166
+ REQUEST_SECONDS,
64167
+ step.severe ? "severe" : "slow",
64168
+ memory?.diskQueue
64169
+ );
64087
64170
  } catch (e) {
64088
64171
  this.note("host watchdog: " + errMsg(e));
64089
64172
  } finally {
@@ -76784,6 +76867,16 @@ async function ensureConpty(dataPath, refresh) {
76784
76867
 
76785
76868
  // src/cli/commands/repair/checks/sandbox.ts
76786
76869
  var LAUNCH_DAYS = 7;
76870
+ var PAGE_FILE_MAX_SHARE = 0.4;
76871
+ function pageFileFinding(pageFileMb, driveBytes) {
76872
+ if (pageFileMb <= 0 || driveBytes <= 0 || pageFileMb * 1024 * 1024 <= PAGE_FILE_MAX_SHARE * driveBytes) return null;
76873
+ return {
76874
+ status: "warn",
76875
+ detail: "the paging file is " + pageFileMb + " MB on a " + humanBytes(driveBytes) + " drive",
76876
+ means: "sandbox.pageFileMb takes more than 40% of the drive, so builds and installs have little room left and the disk fills up (a full disk can freeze the sandbox).",
76877
+ fix: "Run: sandbox config set sandbox.pageFileMb 8192 (or 0 to let Windows decide)."
76878
+ };
76879
+ }
76787
76880
  var hiddenWindowsFile = (env) => path169.join(env.storageRoot, "run", "hidden-windows.json");
76788
76881
  var SANDBOX_CHECKS = [
76789
76882
  {
@@ -76797,6 +76890,16 @@ var SANDBOX_CHECKS = [
76797
76890
  fix: "Run: sandbox config set sandbox.vGpu false"
76798
76891
  } : ok("off")
76799
76892
  },
76893
+ {
76894
+ id: "sandbox-pagefile",
76895
+ group: "Sandbox",
76896
+ name: "Paging file size",
76897
+ run: (env) => {
76898
+ const mb = env.ctx.loaded.config.sandbox.pageFileMb;
76899
+ const drive = diskUsage(env.storageRoot).find((d) => d.drive.toUpperCase() === "C:\\");
76900
+ return pageFileFinding(mb, drive?.totalBytes ?? 0) ?? ok(mb > 0 ? mb + " MB" : "Windows' own");
76901
+ }
76902
+ },
76800
76903
  {
76801
76904
  id: "sandbox-exe",
76802
76905
  group: "Sandbox",
@@ -77679,311 +77782,6 @@ var enableSandboxRun = implement(enableSandbox, async (ctx, _input, io) => {
77679
77782
  });
77680
77783
  var lastHealthRun = implement(lastHealth, (ctx) => ({ last: readLast(ctx.storageRoot) }));
77681
77784
 
77682
- // src/app/features/sandbox/sandbox.def.ts
77683
- var ProjectSchema = external_exports.object({
77684
- name: external_exports.string().describe("The project"),
77685
- host: external_exports.string().describe("Its folder on this PC"),
77686
- guest: external_exports.string().describe("Its folder in the sandbox"),
77687
- mechanism: external_exports.string().describe("How it got into the sandbox (launch, root, boot, wsb or hcs)"),
77688
- here: external_exports.boolean().describe("The project of the current folder"),
77689
- setup: external_exports.object({ phase: external_exports.string(), message: external_exports.string() }).describe("How far its setup is"),
77690
- databases: external_exports.array(external_exports.object({ database: external_exports.string(), port: external_exports.number(), state: external_exports.string() })),
77691
- sessions: external_exports.array(external_exports.object({ id: external_exports.string(), harness: external_exports.string(), viewers: external_exports.string(), started: external_exports.string() })),
77692
- repoGuard: external_exports.array(external_exports.string()).describe("Repo guard findings in its folder")
77693
- });
77694
- var StateSchema2 = external_exports.object({
77695
- running: external_exports.boolean().describe("A Windows Sandbox is running"),
77696
- phase: external_exports.string().describe("What it is doing (ready, provisioning ...)"),
77697
- message: external_exports.string().optional().describe("Its last message"),
77698
- headless: external_exports.boolean().optional().describe("Its window is hidden"),
77699
- startedAt: external_exports.number().nullable().describe("When it started (ms), if known"),
77700
- daemon: external_exports.string().describe("The sandbox helper's answer"),
77701
- account: external_exports.string().nullable().describe("The Claude login it uses"),
77702
- syncWatcher: external_exports.boolean().describe("The background history sync runs")
77703
- });
77704
- var SandboxStatusSchema2 = external_exports.object({
77705
- /** Present when the running sandbox is another user's. */
77706
- shared: external_exports.object({
77707
- line: external_exports.string().describe('"Running, started by alice (shared with you)"'),
77708
- limited: external_exports.boolean().describe("Not shared with you: nothing more is shown"),
77709
- startedBy: external_exports.string().nullable().describe("Whose it is")
77710
- }).optional(),
77711
- sandbox: StateSchema2,
77712
- here: external_exports.object({ folder: external_exports.string(), project: external_exports.string().nullable(), guest: external_exports.string().nullable() }),
77713
- roots: external_exports.array(external_exports.object({ host: external_exports.string(), guest: external_exports.string() })),
77714
- projects: external_exports.array(ProjectSchema),
77715
- otherSessions: external_exports.array(external_exports.object({ id: external_exports.string(), harness: external_exports.string(), cwd: external_exports.string() })),
77716
- repoGuard: external_exports.object({ guarded: external_exports.number(), findings: external_exports.array(external_exports.string()) })
77717
- });
77718
- var MessageSchema5 = external_exports.object({ message: external_exports.string().describe("What happened") });
77719
- function uptime(s) {
77720
- return s.running && s.startedAt ? ago(s.startedAt).replace(" ago", "") : null;
77721
- }
77722
- var sandboxStatus = defineQuery({
77723
- id: "sandbox.status",
77724
- title: "Sandbox status",
77725
- summary: "show what the sandbox is doing",
77726
- input: external_exports.object({}),
77727
- output: SandboxStatusSchema2,
77728
- live: { everyMs: 2500 },
77729
- cli: {
77730
- name: "status",
77731
- group: "Sessions",
77732
- json: true,
77733
- // The --json shape `sandbox status` has always printed.
77734
- shapeJson: (o) => {
77735
- if (o.shared?.limited) return { sandbox: { running: true, shared: false, startedBy: o.shared.startedBy } };
77736
- const s = o.sandbox;
77737
- const borrowed = o.shared?.startedBy ? { startedBy: o.shared.startedBy, shared: true } : {};
77738
- return {
77739
- sandbox: {
77740
- ...borrowed,
77741
- running: s.running,
77742
- phase: s.phase,
77743
- message: s.message,
77744
- headless: s.headless,
77745
- uptime: uptime(s),
77746
- daemon: s.daemon,
77747
- account: s.account,
77748
- syncWatcher: s.syncWatcher
77749
- },
77750
- here: o.here,
77751
- roots: o.roots,
77752
- projects: o.projects,
77753
- otherSessions: o.otherSessions,
77754
- repoGuard: o.repoGuard
77755
- };
77756
- }
77757
- }
77758
- });
77759
- var showWindow = defineOperation({
77760
- id: "sandbox.showWindow",
77761
- access: "control",
77762
- title: "Show the window",
77763
- summary: "show the sandbox window",
77764
- input: external_exports.object({}),
77765
- output: MessageSchema5,
77766
- text: (o) => o.message,
77767
- cli: { name: "show", group: "Sessions" }
77768
- });
77769
- var hideWindow = defineOperation({
77770
- id: "sandbox.hideWindow",
77771
- access: "control",
77772
- title: "Hide the window",
77773
- summary: "hide the sandbox window (it keeps running)",
77774
- input: external_exports.object({}),
77775
- output: MessageSchema5,
77776
- text: (o) => o.message,
77777
- cli: { name: "hide", group: "Sessions" }
77778
- });
77779
- var stopSandbox = defineOperation({
77780
- id: "sandbox.stop",
77781
- access: "control",
77782
- title: "Stop the sandbox\u2026",
77783
- summary: "shut the sandbox down (every project's sessions end)",
77784
- input: external_exports.object({}),
77785
- output: external_exports.object({ stopped: external_exports.boolean().describe("It was asked to shut down"), message: external_exports.string() }),
77786
- danger: true,
77787
- text: (o) => o.message,
77788
- cli: { name: "stop", group: "Agents", yes: "do not ask when other projects' sessions would end" }
77789
- });
77790
- var MECHANISM_TEXT = {
77791
- launch: "started from here",
77792
- root: "in a project root",
77793
- boot: "mapped at start",
77794
- wsb: "wsb share",
77795
- hcs: "live folder through HCS, alpha"
77796
- };
77797
- function projectItem2(p) {
77798
- return {
77799
- id: "project:" + p.name,
77800
- label: p.name,
77801
- icon: "project",
77802
- hint: p.host + " -> " + p.guest + " (" + (MECHANISM_TEXT[p.mechanism] ?? "wsb share") + ")",
77803
- marked: p.here,
77804
- details: [
77805
- "Setup: " + p.setup.phase + (p.setup.phase === "ready" ? "" : " - " + p.setup.message),
77806
- ...p.databases.length ? ["Databases: " + p.databases.map((d) => d.database + " :" + d.port + " " + d.state).join(", ")] : [],
77807
- ...p.sessions.map((s) => "Session: " + s.harness + " " + s.id + ", " + s.viewers + ", started " + ago(s.started)),
77808
- ...p.repoGuard.map((f) => "Guard: " + f)
77809
- ]
77810
- };
77811
- }
77812
- function facts(o) {
77813
- const s = o.sandbox;
77814
- const sessions = o.projects.reduce((n, p) => n + p.sessions.length, 0) + o.otherSessions.length;
77815
- const fact = (id, label2, value, extra = {}) => ({
77816
- id,
77817
- label: label2,
77818
- value,
77819
- ...extra
77820
- });
77821
- return [
77822
- ...o.shared?.startedBy ? [fact("owner", "Started by", o.shared.startedBy)] : [],
77823
- // One line for what it is doing: "Ready", "Installing Rust ...", or "not running".
77824
- fact("state", "State", s.running ? s.message || s.phase : "not running", {
77825
- tone: !s.running ? "muted" : s.phase === "ready" ? "ok" : "warn"
77826
- }),
77827
- ...s.running ? [
77828
- fact("window", "Window", s.headless ? "hidden (sandbox show opens it)" : "shown"),
77829
- fact("uptime", "Uptime", uptime(s) ?? "-"),
77830
- // The session count is its own fact: the helper only says whether it answers.
77831
- fact("helper", "Helper", s.daemon === "-" ? "-" : s.daemon.startsWith("pong") ? "Responding" : s.daemon),
77832
- fact("sessions", "Sessions", String(sessions))
77833
- ] : [],
77834
- fact("account", "Account", s.account ?? "none selected (sandbox account use)"),
77835
- fact("sync", "Background sync", s.syncWatcher ? "on" : "off")
77836
- ];
77837
- }
77838
- function projectsSection(o) {
77839
- const roots = o.roots.map((r) => ({
77840
- id: "root:" + r.host,
77841
- label: "root",
77842
- icon: "folder",
77843
- hint: r.host + " -> " + r.guest + " (every project in it opens without a restart)"
77844
- }));
77845
- const here = o.projects.some((p) => p.here);
77846
- return {
77847
- id: "projects",
77848
- title: "Projects",
77849
- items: [...o.projects.map(projectItem2), ...roots],
77850
- notes: [
77851
- here ? { text: "* = the project of this folder", tone: "muted" } : { text: o.here.folder + " is not in this sandbox (start it here: sandbox claude)", tone: "muted" }
77852
- ]
77853
- };
77854
- }
77855
- function guardSection(o) {
77856
- const findings = o.repoGuard.findings;
77857
- return {
77858
- id: "guard",
77859
- title: "Repo guard",
77860
- summary: "Watching " + plural2(o.repoGuard.guarded, "folder") + ". " + (findings.length ? plural2(findings.length, "change") + " from the sandbox were blocked:" : "Nothing from the sandbox needed blocking."),
77861
- items: findings.slice(-10).map((f, i) => ({ id: "guard:" + i, label: f, tone: "warn" }))
77862
- };
77863
- }
77864
- function sandboxPage(o) {
77865
- const notice = o.shared ? {
77866
- text: o.shared.line + (o.shared.limited ? "" : ". Its window is on their desktop; use its agents here."),
77867
- tone: "accent"
77868
- } : void 0;
77869
- if (o.shared?.limited) return { title: "Sandbox", notice, sections: [] };
77870
- const running = o.sandbox.running;
77871
- const status = {
77872
- id: "status",
77873
- title: "Status",
77874
- facts: facts(o),
77875
- // Only the window action that changes something: show a hidden window, hide a shown one.
77876
- actions: running ? [
77877
- o.sandbox.headless ? action(showWindow, {}, { key: "w", verb: "show", importance: 3 }) : action(hideWindow, {}, { key: "h", verb: "hide", importance: 3 }),
77878
- action(stopSandbox, {}, { key: "x", verb: "stop", importance: 1 })
77879
- ] : []
77880
- };
77881
- const others = {
77882
- id: "other-sessions",
77883
- title: "Other sessions",
77884
- items: o.otherSessions.map((s) => ({
77885
- id: "session:" + s.id,
77886
- label: "session " + s.id,
77887
- hint: s.harness + " " + s.cwd
77888
- }))
77889
- };
77890
- return {
77891
- title: "Sandbox",
77892
- notice,
77893
- sections: [
77894
- status,
77895
- ...running ? [projectsSection(o)] : [],
77896
- ...others.items?.length ? [others] : [],
77897
- guardSection(o)
77898
- ]
77899
- };
77900
- }
77901
- var sandboxView = defineView({
77902
- id: "sandbox",
77903
- title: "Sandbox",
77904
- summary: "Status, projects and controls of the running sandbox.",
77905
- queries: { status: sandboxStatus },
77906
- operations: [showWindow, hideWindow, stopSandbox],
77907
- build: ({ status }) => sandboxPage(status)
77908
- });
77909
-
77910
- // src/app/features/sandbox/sandbox.run.ts
77911
- import path173 from "node:path";
77912
- function limitedStatus(view, folder) {
77913
- const owner = view.owner;
77914
- return {
77915
- shared: { line: view.line ?? "", limited: true, startedBy: owner?.kind === "other" ? owner.user : null },
77916
- sandbox: {
77917
- running: true,
77918
- phase: "unknown",
77919
- startedAt: null,
77920
- daemon: "-",
77921
- account: null,
77922
- syncWatcher: false
77923
- },
77924
- here: { folder, project: null, guest: null },
77925
- roots: [],
77926
- projects: [],
77927
- otherSessions: [],
77928
- repoGuard: { guarded: 0, findings: [] }
77929
- };
77930
- }
77931
- var sandboxStatusRun = implement(sandboxStatus, async (asked) => {
77932
- const view = lookAtSandbox(asked);
77933
- if (view.limited) return limitedStatus(view, hereDir(asked));
77934
- const report = await new SandboxService(view.ctx).status();
77935
- return {
77936
- ...view.line ? { shared: { line: view.line, limited: false, startedBy: view.ctx.borrowed?.user ?? null } } : {},
77937
- ...report,
77938
- sandbox: { ...report.sandbox, daemon: safeText(report.sandbox.daemon) }
77939
- };
77940
- });
77941
- var showWindowRun = implement(showWindow, async (ctx, _input, io) => {
77942
- requireOnPc("show");
77943
- requireRunning();
77944
- await useSharedSandbox(ctx, { kind: "owner-only", what: "show the window", window: true }, typedConfirm(io));
77945
- new SandboxService(ctx).showWindow();
77946
- return { message: "Sandbox window shown." };
77947
- });
77948
- var hideWindowRun = implement(hideWindow, async (ctx, _input, io) => {
77949
- requireOnPc("hide");
77950
- requireRunning();
77951
- await useSharedSandbox(ctx, { kind: "owner-only", what: "hide the window", window: true }, typedConfirm(io));
77952
- io.progress("Hiding the window");
77953
- await new SandboxService(ctx).hideWindow();
77954
- return { message: "Sandbox window hidden. Bring it back with: sbx-show" };
77955
- });
77956
- var stopSandboxRun = implement(stopSandbox, async (asked, _input, io) => {
77957
- requireOnPc("stop");
77958
- if (!new SandboxService(asked).isRunning()) return { stopped: false, message: "No Windows Sandbox is running." };
77959
- const ctx = runningOwner().kind === "mine" ? asked : await useSharedSandbox(
77960
- asked,
77961
- (shared) => ({ kind: "stop", sessions: new SandboxChannel(shared.dataPath).currentSessions().length }),
77962
- typedConfirm(io)
77963
- );
77964
- const service = new SandboxService(ctx);
77965
- const { sandbox, running, others } = service.stopPlan();
77966
- if (running.length)
77967
- io.log(
77968
- "Stopping ends " + running.length + " session(s): " + running.map((s) => safeText(s.harness) + " in " + safeText(s.project ?? path173.win32.basename(s.cwd))).join(", ") + "."
77969
- );
77970
- const yes = await io.confirm({
77971
- title: others.length ? "Stop the whole sandbox?" : "Stop the sandbox?",
77972
- lines: others.length ? [others.length + " of them belong to other projects."] : [],
77973
- yes: "Stop",
77974
- danger: true,
77975
- implied: others.length === 0,
77976
- noTerminal: {
77977
- message: others.length + " of these sessions belong to other projects, so the sandbox was not stopped.",
77978
- fix: "Stop it anyway with: sandbox stop --yes"
77979
- }
77980
- });
77981
- if (!yes) cancelled();
77982
- io.progress("Asking the sandbox to shut down");
77983
- await service.shutdown(sandbox);
77984
- return { stopped: true, message: "Sandbox is shutting down; history and claude-mem sync back when it closes." };
77985
- });
77986
-
77987
77785
  // src/app/features/logs/logs.def.ts
77988
77786
  var CRASH = "@crash";
77989
77787
  var MAX_SHOWN = 1e3;
@@ -78083,6 +77881,323 @@ var logsView = defineView({
78083
77881
  build: ({ files, tail }) => logsPage(files, tail)
78084
77882
  });
78085
77883
 
77884
+ // src/app/features/sandbox/sandbox.def.ts
77885
+ var ProjectSchema = external_exports.object({
77886
+ name: external_exports.string().describe("The project"),
77887
+ host: external_exports.string().describe("Its folder on this PC"),
77888
+ guest: external_exports.string().describe("Its folder in the sandbox"),
77889
+ mechanism: external_exports.string().describe("How it got into the sandbox (launch, root, boot, wsb or hcs)"),
77890
+ here: external_exports.boolean().describe("The project of the current folder"),
77891
+ setup: external_exports.object({ phase: external_exports.string(), message: external_exports.string() }).describe("How far its setup is"),
77892
+ databases: external_exports.array(external_exports.object({ database: external_exports.string(), port: external_exports.number(), state: external_exports.string() })),
77893
+ sessions: external_exports.array(external_exports.object({ id: external_exports.string(), harness: external_exports.string(), viewers: external_exports.string(), started: external_exports.string() })),
77894
+ repoGuard: external_exports.array(external_exports.string()).describe("Repo guard findings in its folder")
77895
+ });
77896
+ var StateSchema2 = external_exports.object({
77897
+ running: external_exports.boolean().describe("A Windows Sandbox is running"),
77898
+ phase: external_exports.string().describe("What it is doing (ready, provisioning ...)"),
77899
+ message: external_exports.string().optional().describe("Its last message"),
77900
+ headless: external_exports.boolean().optional().describe("Its window is hidden"),
77901
+ startedAt: external_exports.number().nullable().describe("When it started (ms), if known"),
77902
+ daemon: external_exports.string().describe("The sandbox helper's answer"),
77903
+ account: external_exports.string().nullable().describe("The Claude login it uses"),
77904
+ syncWatcher: external_exports.boolean().describe("The background history sync runs")
77905
+ });
77906
+ var SandboxStatusSchema2 = external_exports.object({
77907
+ /** Present when the running sandbox is another user's. */
77908
+ shared: external_exports.object({
77909
+ line: external_exports.string().describe('"Running, started by alice (shared with you)"'),
77910
+ limited: external_exports.boolean().describe("Not shared with you: nothing more is shown"),
77911
+ startedBy: external_exports.string().nullable().describe("Whose it is")
77912
+ }).optional(),
77913
+ sandbox: StateSchema2,
77914
+ here: external_exports.object({ folder: external_exports.string(), project: external_exports.string().nullable(), guest: external_exports.string().nullable() }),
77915
+ roots: external_exports.array(external_exports.object({ host: external_exports.string(), guest: external_exports.string() })),
77916
+ projects: external_exports.array(ProjectSchema),
77917
+ otherSessions: external_exports.array(external_exports.object({ id: external_exports.string(), harness: external_exports.string(), cwd: external_exports.string() })),
77918
+ repoGuard: external_exports.object({ guarded: external_exports.number(), findings: external_exports.array(external_exports.string()) }),
77919
+ /** How the last sandbox ended when nobody closed it (it is not running now). */
77920
+ lastEnd: external_exports.object({ text: external_exports.string().describe("What happened"), at: external_exports.string().describe("When (ISO time)") }).optional().describe("The last sandbox ended unexpectedly")
77921
+ });
77922
+ var MessageSchema5 = external_exports.object({ message: external_exports.string().describe("What happened") });
77923
+ function uptime(s) {
77924
+ return s.running && s.startedAt ? ago(s.startedAt).replace(" ago", "") : null;
77925
+ }
77926
+ var sandboxStatus = defineQuery({
77927
+ id: "sandbox.status",
77928
+ title: "Sandbox status",
77929
+ summary: "show what the sandbox is doing",
77930
+ input: external_exports.object({}),
77931
+ output: SandboxStatusSchema2,
77932
+ live: { everyMs: 2500 },
77933
+ cli: {
77934
+ name: "status",
77935
+ group: "Sessions",
77936
+ json: true,
77937
+ // The --json shape `sandbox status` has always printed.
77938
+ shapeJson: (o) => {
77939
+ if (o.shared?.limited) return { sandbox: { running: true, shared: false, startedBy: o.shared.startedBy } };
77940
+ const s = o.sandbox;
77941
+ const borrowed = o.shared?.startedBy ? { startedBy: o.shared.startedBy, shared: true } : {};
77942
+ return {
77943
+ sandbox: {
77944
+ ...borrowed,
77945
+ running: s.running,
77946
+ phase: s.phase,
77947
+ message: s.message,
77948
+ headless: s.headless,
77949
+ uptime: uptime(s),
77950
+ daemon: s.daemon,
77951
+ account: s.account,
77952
+ syncWatcher: s.syncWatcher
77953
+ },
77954
+ here: o.here,
77955
+ roots: o.roots,
77956
+ projects: o.projects,
77957
+ otherSessions: o.otherSessions,
77958
+ repoGuard: o.repoGuard,
77959
+ ...o.lastEnd ? { lastEnd: o.lastEnd } : {}
77960
+ };
77961
+ }
77962
+ }
77963
+ });
77964
+ var showWindow = defineOperation({
77965
+ id: "sandbox.showWindow",
77966
+ access: "control",
77967
+ title: "Show the window",
77968
+ summary: "show the sandbox window",
77969
+ input: external_exports.object({}),
77970
+ output: MessageSchema5,
77971
+ text: (o) => o.message,
77972
+ cli: { name: "show", group: "Sessions" }
77973
+ });
77974
+ var hideWindow = defineOperation({
77975
+ id: "sandbox.hideWindow",
77976
+ access: "control",
77977
+ title: "Hide the window",
77978
+ summary: "hide the sandbox window (it keeps running)",
77979
+ input: external_exports.object({}),
77980
+ output: MessageSchema5,
77981
+ text: (o) => o.message,
77982
+ cli: { name: "hide", group: "Sessions" }
77983
+ });
77984
+ var stopSandbox = defineOperation({
77985
+ id: "sandbox.stop",
77986
+ access: "control",
77987
+ title: "Stop the sandbox\u2026",
77988
+ summary: "shut the sandbox down (every project's sessions end)",
77989
+ input: external_exports.object({}),
77990
+ output: external_exports.object({ stopped: external_exports.boolean().describe("It was asked to shut down"), message: external_exports.string() }),
77991
+ danger: true,
77992
+ text: (o) => o.message,
77993
+ cli: { name: "stop", group: "Agents", yes: "do not ask when other projects' sessions would end" }
77994
+ });
77995
+ var MECHANISM_TEXT = {
77996
+ launch: "started from here",
77997
+ root: "in a project root",
77998
+ boot: "mapped at start",
77999
+ wsb: "wsb share",
78000
+ hcs: "live folder through HCS, alpha"
78001
+ };
78002
+ function projectItem2(p) {
78003
+ return {
78004
+ id: "project:" + p.name,
78005
+ label: p.name,
78006
+ icon: "project",
78007
+ hint: p.host + " -> " + p.guest + " (" + (MECHANISM_TEXT[p.mechanism] ?? "wsb share") + ")",
78008
+ marked: p.here,
78009
+ details: [
78010
+ "Setup: " + p.setup.phase + (p.setup.phase === "ready" ? "" : " - " + p.setup.message),
78011
+ ...p.databases.length ? ["Databases: " + p.databases.map((d) => d.database + " :" + d.port + " " + d.state).join(", ")] : [],
78012
+ ...p.sessions.map((s) => "Session: " + s.harness + " " + s.id + ", " + s.viewers + ", started " + ago(s.started)),
78013
+ ...p.repoGuard.map((f) => "Guard: " + f)
78014
+ ]
78015
+ };
78016
+ }
78017
+ function facts(o) {
78018
+ const s = o.sandbox;
78019
+ const sessions = o.projects.reduce((n, p) => n + p.sessions.length, 0) + o.otherSessions.length;
78020
+ const fact = (id, label2, value, extra = {}) => ({
78021
+ id,
78022
+ label: label2,
78023
+ value,
78024
+ ...extra
78025
+ });
78026
+ return [
78027
+ ...o.shared?.startedBy ? [fact("owner", "Started by", o.shared.startedBy)] : [],
78028
+ // One line for what it is doing: "Ready", "Installing Rust ...", or "not running".
78029
+ fact("state", "State", s.running ? s.message || s.phase : "not running", {
78030
+ tone: !s.running ? "muted" : s.phase === "ready" ? "ok" : "warn"
78031
+ }),
78032
+ ...s.running ? [
78033
+ fact("window", "Window", s.headless ? "hidden (sandbox show opens it)" : "shown"),
78034
+ fact("uptime", "Uptime", uptime(s) ?? "-"),
78035
+ // The session count is its own fact: the helper only says whether it answers.
78036
+ fact("helper", "Helper", s.daemon === "-" ? "-" : s.daemon.startsWith("pong") ? "Responding" : s.daemon),
78037
+ fact("sessions", "Sessions", String(sessions))
78038
+ ] : [],
78039
+ ...o.lastEnd ? [fact("lastEnd", "Last sandbox", o.lastEnd.text, { tone: "warn" })] : [],
78040
+ fact("account", "Account", s.account ?? "none selected (sandbox account use)"),
78041
+ fact("sync", "Background sync", s.syncWatcher ? "on" : "off")
78042
+ ];
78043
+ }
78044
+ function projectsSection(o) {
78045
+ const roots = o.roots.map((r) => ({
78046
+ id: "root:" + r.host,
78047
+ label: "root",
78048
+ icon: "folder",
78049
+ hint: r.host + " -> " + r.guest + " (every project in it opens without a restart)"
78050
+ }));
78051
+ const here = o.projects.some((p) => p.here);
78052
+ return {
78053
+ id: "projects",
78054
+ title: "Projects",
78055
+ items: [...o.projects.map(projectItem2), ...roots],
78056
+ notes: [
78057
+ here ? { text: "* = the project of this folder", tone: "muted" } : { text: o.here.folder + " is not in this sandbox (start it here: sandbox claude)", tone: "muted" }
78058
+ ]
78059
+ };
78060
+ }
78061
+ function guardSection(o) {
78062
+ const findings = o.repoGuard.findings;
78063
+ return {
78064
+ id: "guard",
78065
+ title: "Repo guard",
78066
+ summary: "Watching " + plural2(o.repoGuard.guarded, "folder") + ". " + (findings.length ? plural2(findings.length, "change") + " from the sandbox were blocked:" : "Nothing from the sandbox needed blocking."),
78067
+ items: findings.slice(-10).map((f, i) => ({ id: "guard:" + i, label: f, tone: "warn" }))
78068
+ };
78069
+ }
78070
+ function sandboxPage(o) {
78071
+ const notice = o.shared ? {
78072
+ text: o.shared.line + (o.shared.limited ? "" : ". Its window is on their desktop; use its agents here."),
78073
+ tone: "accent"
78074
+ } : void 0;
78075
+ if (o.shared?.limited) return { title: "Sandbox", notice, sections: [] };
78076
+ const running = o.sandbox.running;
78077
+ const status = {
78078
+ id: "status",
78079
+ title: "Status",
78080
+ facts: facts(o),
78081
+ // Only the window action that changes something: show a hidden window, hide a shown one.
78082
+ actions: running ? [
78083
+ o.sandbox.headless ? action(showWindow, {}, { key: "w", verb: "show", importance: 3 }) : action(hideWindow, {}, { key: "h", verb: "hide", importance: 3 }),
78084
+ action(stopSandbox, {}, { key: "x", verb: "stop", importance: 1 })
78085
+ ] : o.lastEnd ? [
78086
+ link({ kind: "view", view: "logs", params: { name: CRASH } }, "Show crash report", {
78087
+ verb: "show",
78088
+ importance: 2
78089
+ })
78090
+ ] : []
78091
+ };
78092
+ const others = {
78093
+ id: "other-sessions",
78094
+ title: "Other sessions",
78095
+ items: o.otherSessions.map((s) => ({
78096
+ id: "session:" + s.id,
78097
+ label: "session " + s.id,
78098
+ hint: s.harness + " " + s.cwd
78099
+ }))
78100
+ };
78101
+ return {
78102
+ title: "Sandbox",
78103
+ notice,
78104
+ sections: [
78105
+ status,
78106
+ ...running ? [projectsSection(o)] : [],
78107
+ ...others.items?.length ? [others] : [],
78108
+ guardSection(o)
78109
+ ]
78110
+ };
78111
+ }
78112
+ var sandboxView = defineView({
78113
+ id: "sandbox",
78114
+ title: "Sandbox",
78115
+ summary: "Status, projects and controls of the running sandbox.",
78116
+ queries: { status: sandboxStatus },
78117
+ operations: [showWindow, hideWindow, stopSandbox],
78118
+ build: ({ status }) => sandboxPage(status)
78119
+ });
78120
+
78121
+ // src/app/features/sandbox/sandbox.run.ts
78122
+ import path173 from "node:path";
78123
+ function limitedStatus(view, folder) {
78124
+ const owner = view.owner;
78125
+ return {
78126
+ shared: { line: view.line ?? "", limited: true, startedBy: owner?.kind === "other" ? owner.user : null },
78127
+ sandbox: {
78128
+ running: true,
78129
+ phase: "unknown",
78130
+ startedAt: null,
78131
+ daemon: "-",
78132
+ account: null,
78133
+ syncWatcher: false
78134
+ },
78135
+ here: { folder, project: null, guest: null },
78136
+ roots: [],
78137
+ projects: [],
78138
+ otherSessions: [],
78139
+ repoGuard: { guarded: 0, findings: [] }
78140
+ };
78141
+ }
78142
+ var sandboxStatusRun = implement(sandboxStatus, async (asked) => {
78143
+ const view = lookAtSandbox(asked);
78144
+ if (view.limited) return limitedStatus(view, hereDir(asked));
78145
+ const report = await new SandboxService(view.ctx).status();
78146
+ const { storageRoot, dataPath } = view.ctx;
78147
+ const lastEnd = readLastEnd(() => new LifeRecord(storageRoot, dataPath).read(), dataPath, report.sandbox.running);
78148
+ return {
78149
+ ...lastEnd ? { lastEnd } : {},
78150
+ ...view.line ? { shared: { line: view.line, limited: false, startedBy: view.ctx.borrowed?.user ?? null } } : {},
78151
+ ...report,
78152
+ sandbox: { ...report.sandbox, daemon: safeText(report.sandbox.daemon) }
78153
+ };
78154
+ });
78155
+ var showWindowRun = implement(showWindow, async (ctx, _input, io) => {
78156
+ requireOnPc("show");
78157
+ requireRunning();
78158
+ await useSharedSandbox(ctx, { kind: "owner-only", what: "show the window", window: true }, typedConfirm(io));
78159
+ new SandboxService(ctx).showWindow();
78160
+ return { message: "Sandbox window shown." };
78161
+ });
78162
+ var hideWindowRun = implement(hideWindow, async (ctx, _input, io) => {
78163
+ requireOnPc("hide");
78164
+ requireRunning();
78165
+ await useSharedSandbox(ctx, { kind: "owner-only", what: "hide the window", window: true }, typedConfirm(io));
78166
+ io.progress("Hiding the window");
78167
+ await new SandboxService(ctx).hideWindow();
78168
+ return { message: "Sandbox window hidden. Bring it back with: sbx-show" };
78169
+ });
78170
+ var stopSandboxRun = implement(stopSandbox, async (asked, _input, io) => {
78171
+ requireOnPc("stop");
78172
+ if (!new SandboxService(asked).isRunning()) return { stopped: false, message: "No Windows Sandbox is running." };
78173
+ const ctx = runningOwner().kind === "mine" ? asked : await useSharedSandbox(
78174
+ asked,
78175
+ (shared) => ({ kind: "stop", sessions: new SandboxChannel(shared.dataPath).currentSessions().length }),
78176
+ typedConfirm(io)
78177
+ );
78178
+ const service = new SandboxService(ctx);
78179
+ const { sandbox, running, others } = service.stopPlan();
78180
+ if (running.length)
78181
+ io.log(
78182
+ "Stopping ends " + running.length + " session(s): " + running.map((s) => safeText(s.harness) + " in " + safeText(s.project ?? path173.win32.basename(s.cwd))).join(", ") + "."
78183
+ );
78184
+ const yes = await io.confirm({
78185
+ title: others.length ? "Stop the whole sandbox?" : "Stop the sandbox?",
78186
+ lines: others.length ? [others.length + " of them belong to other projects."] : [],
78187
+ yes: "Stop",
78188
+ danger: true,
78189
+ implied: others.length === 0,
78190
+ noTerminal: {
78191
+ message: others.length + " of these sessions belong to other projects, so the sandbox was not stopped.",
78192
+ fix: "Stop it anyway with: sandbox stop --yes"
78193
+ }
78194
+ });
78195
+ if (!yes) cancelled();
78196
+ io.progress("Asking the sandbox to shut down");
78197
+ await service.shutdown(sandbox);
78198
+ return { stopped: true, message: "Sandbox is shutting down; history and claude-mem sync back when it closes." };
78199
+ });
78200
+
78086
78201
  // src/app/features/logs/logs.run.ts
78087
78202
  function lastLines(text, n) {
78088
78203
  return text.split("\n").slice(-n).join("\n");
@@ -80442,7 +80557,7 @@ async function attachOpened(ctx, session) {
80442
80557
  nextStep("sandbox attach " + session);
80443
80558
  return 0;
80444
80559
  }
80445
- const { attach: attach2 } = await import("./chunk-M4OM632I.js");
80560
+ const { attach: attach2 } = await import("./chunk-M6XUQK53.js");
80446
80561
  return attach2({ ...ctx, options: { ...ctx.options, positionals: [session] } });
80447
80562
  }
80448
80563
  function definitionHandler(def) {
@@ -81292,13 +81407,13 @@ export {
81292
81407
  StorageHistory,
81293
81408
  planOffer,
81294
81409
  staleMountNote,
81410
+ RunDir,
81295
81411
  LifeRecord,
81296
81412
  shouldReopen,
81297
81413
  describeLast,
81298
81414
  SessionLedger,
81299
81415
  currentUserName,
81300
81416
  runningOwner,
81301
- RunDir,
81302
81417
  describeCopy,
81303
81418
  takeHiddenInstallNotice,
81304
81419
  protectPrivateFolder,
@@ -81328,14 +81443,14 @@ export {
81328
81443
  showData,
81329
81444
  cleanData,
81330
81445
  dataPage,
81331
- sandboxStatus,
81332
- sandboxPage,
81333
- listProjects,
81334
- projectsPage,
81335
81446
  CRASH,
81336
81447
  listLogs,
81337
81448
  tailLog,
81338
81449
  logsPage,
81450
+ sandboxStatus,
81451
+ sandboxPage,
81452
+ listProjects,
81453
+ projectsPage,
81339
81454
  listAudit,
81340
81455
  auditPage,
81341
81456
  auditArgv,