windows-development-sandbox 1.18.3 → 1.18.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/features/defs.d.ts +18 -0
- package/dist/app/features/defs.js +2 -1
- package/dist/app/features/sandbox/sandbox.def.d.ts +22 -0
- package/dist/app/features/sandbox/sandbox.def.js +41 -14
- package/dist/app/features/sandbox/sandbox.run.d.ts +20 -0
- package/dist/app/features/sandbox/sandbox.run.js +41 -1
- package/dist/app/registry.js +2 -1
- package/dist/bin/agent-sandbox.js +2 -2
- package/dist/bin/{chunk-FQWYCF6L.js → chunk-FRPLQLSY.js} +3453 -3145
- package/dist/bin/{chunk-M6XUQK53.js → chunk-MY7NLQQY.js} +1 -1
- package/dist/bin/{chunk-3G53NNZP.js → chunk-N4GXYTLV.js} +1 -1
- package/dist/bin/claude-host.js +2 -2
- package/dist/bin/claude-sbx.js +2 -2
- package/dist/bin/codex-sbx.js +2 -2
- package/dist/bin/gemini-sbx.js +2 -2
- package/dist/bin/sandbox.js +2 -2
- package/dist/bin/sbx-hide.js +2 -2
- package/dist/bin/sbx-show.js +2 -2
- package/dist/cli/commands/launch/launch.js +4 -1
- package/dist/cli/commands/launch/leftover-offer.d.ts +3 -0
- package/dist/cli/commands/launch/leftover-offer.js +26 -0
- package/dist/cli/commands/repair/checks/sandbox.js +29 -2
- package/dist/guest/daemon/heartbeat.d.ts +7 -2
- package/dist/guest/daemon/heartbeat.js +34 -15
- package/dist/guest/daemon/idle-shutdown.js +2 -0
- package/dist/guest/entries/daemon.js +1 -1
- package/dist/host/health/index.d.ts +1 -0
- package/dist/host/health/index.js +1 -0
- package/dist/host/health/leftovers.d.ts +3 -0
- package/dist/host/health/leftovers.js +40 -0
- package/dist/host/health/stale.d.ts +48 -0
- package/dist/host/health/stale.js +40 -0
- package/dist/host/life/crash-report.d.ts +1 -1
- package/dist/host/life/crash-report.js +5 -3
- package/dist/host/life/last-end.d.ts +7 -0
- package/dist/host/life/last-end.js +21 -0
- package/dist/host/life/life-record.d.ts +3 -0
- package/dist/host/life/life-record.js +2 -2
- package/dist/host/life/stop-events.d.ts +3 -1
- package/dist/host/life/stop-events.js +59 -0
- package/dist/host/life/stop-warning.js +3 -0
- package/dist/host/sandbox/cleanup.d.ts +37 -0
- package/dist/host/sandbox/cleanup.js +82 -0
- package/dist/host/sandbox/index.d.ts +1 -0
- package/dist/host/sandbox/index.js +1 -0
- package/dist/host/sandbox/platform.js +1 -1
- package/dist/runtime/agent.js +3 -1
- package/dist/runtime/daemon.js +368 -351
- package/dist/runtime/db.js +3 -1
- package/dist/runtime/main.js +3 -1
- package/dist/runtime/netproxy.js +3 -1
- package/dist/runtime/tool.js +3 -1
- package/dist/runtime/view.js +3 -1
- package/dist/selftest/heavy-load.test.js +119 -2
- package/dist/shared/channel/schema.d.ts +1 -0
- package/dist/shared/channel/schema.js +2 -1
- package/dist/shared/system.d.ts +1 -0
- package/dist/shared/system.js +1 -0
- package/dist/sync/watcher.js +9 -4
- package/dist/web/index.html +1 -1
- package/dist/web/{main-6XYO5FZC.js → main-GVZWWP4I.js} +62 -62
- package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
- package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
- package/native/csharp/HostTools/Program.cs +5 -1
- package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
- package/native/scripts/stop-events.ps1 +7 -3
- package/package.json +1 -1
package/dist/runtime/db.js
CHANGED
|
@@ -5546,6 +5546,7 @@ var SYSTEM = {
|
|
|
5546
5546
|
cmd: () => system32("cmd.exe"),
|
|
5547
5547
|
powershell: () => system32("WindowsPowerShell\\v1.0\\powershell.exe"),
|
|
5548
5548
|
tasklist: () => system32("tasklist.exe"),
|
|
5549
|
+
taskkill: () => system32("taskkill.exe"),
|
|
5549
5550
|
whoami: () => system32("whoami.exe"),
|
|
5550
5551
|
icacls: () => system32("icacls.exe"),
|
|
5551
5552
|
tar: () => system32("tar.exe"),
|
|
@@ -27295,7 +27296,8 @@ var HeartbeatSchema = external_exports.object({
|
|
|
27295
27296
|
topProcesses: external_exports.array(external_exports.object({ name: Text(64), privateMb: external_exports.number().int().min(0).max(1e7) })).max(5),
|
|
27296
27297
|
/** The disk queue this PC last reported while asking the sandbox to slow down. */
|
|
27297
27298
|
diskQueueReported: external_exports.number().min(0).max(1e5).optional(),
|
|
27298
|
-
|
|
27299
|
+
/** "ending": the sandbox is shutting down in order (idle shutdown, sandbox stop, Windows shutting down). */
|
|
27300
|
+
phase: external_exports.enum(["ready", "working", "ending"])
|
|
27299
27301
|
});
|
|
27300
27302
|
var GuestResponseSchema = external_exports.object({
|
|
27301
27303
|
id: IdSchema,
|
package/dist/runtime/main.js
CHANGED
|
@@ -27425,7 +27425,8 @@ var HeartbeatSchema = external_exports.object({
|
|
|
27425
27425
|
topProcesses: external_exports.array(external_exports.object({ name: Text(64), privateMb: external_exports.number().int().min(0).max(1e7) })).max(5),
|
|
27426
27426
|
/** The disk queue this PC last reported while asking the sandbox to slow down. */
|
|
27427
27427
|
diskQueueReported: external_exports.number().min(0).max(1e5).optional(),
|
|
27428
|
-
|
|
27428
|
+
/** "ending": the sandbox is shutting down in order (idle shutdown, sandbox stop, Windows shutting down). */
|
|
27429
|
+
phase: external_exports.enum(["ready", "working", "ending"])
|
|
27429
27430
|
});
|
|
27430
27431
|
var GuestResponseSchema = external_exports.object({
|
|
27431
27432
|
id: IdSchema,
|
|
@@ -27883,6 +27884,7 @@ var SYSTEM = {
|
|
|
27883
27884
|
cmd: () => system32("cmd.exe"),
|
|
27884
27885
|
powershell: () => system32("WindowsPowerShell\\v1.0\\powershell.exe"),
|
|
27885
27886
|
tasklist: () => system32("tasklist.exe"),
|
|
27887
|
+
taskkill: () => system32("taskkill.exe"),
|
|
27886
27888
|
whoami: () => system32("whoami.exe"),
|
|
27887
27889
|
icacls: () => system32("icacls.exe"),
|
|
27888
27890
|
tar: () => system32("tar.exe"),
|
package/dist/runtime/netproxy.js
CHANGED
|
@@ -27348,7 +27348,8 @@ var HeartbeatSchema = external_exports.object({
|
|
|
27348
27348
|
topProcesses: external_exports.array(external_exports.object({ name: Text(64), privateMb: external_exports.number().int().min(0).max(1e7) })).max(5),
|
|
27349
27349
|
/** The disk queue this PC last reported while asking the sandbox to slow down. */
|
|
27350
27350
|
diskQueueReported: external_exports.number().min(0).max(1e5).optional(),
|
|
27351
|
-
|
|
27351
|
+
/** "ending": the sandbox is shutting down in order (idle shutdown, sandbox stop, Windows shutting down). */
|
|
27352
|
+
phase: external_exports.enum(["ready", "working", "ending"])
|
|
27352
27353
|
});
|
|
27353
27354
|
var GuestResponseSchema = external_exports.object({
|
|
27354
27355
|
id: IdSchema,
|
|
@@ -27681,6 +27682,7 @@ var SYSTEM = {
|
|
|
27681
27682
|
cmd: () => system32("cmd.exe"),
|
|
27682
27683
|
powershell: () => system32("WindowsPowerShell\\v1.0\\powershell.exe"),
|
|
27683
27684
|
tasklist: () => system32("tasklist.exe"),
|
|
27685
|
+
taskkill: () => system32("taskkill.exe"),
|
|
27684
27686
|
whoami: () => system32("whoami.exe"),
|
|
27685
27687
|
icacls: () => system32("icacls.exe"),
|
|
27686
27688
|
tar: () => system32("tar.exe"),
|
package/dist/runtime/tool.js
CHANGED
|
@@ -25775,6 +25775,7 @@ var SYSTEM = {
|
|
|
25775
25775
|
cmd: () => system32("cmd.exe"),
|
|
25776
25776
|
powershell: () => system32("WindowsPowerShell\\v1.0\\powershell.exe"),
|
|
25777
25777
|
tasklist: () => system32("tasklist.exe"),
|
|
25778
|
+
taskkill: () => system32("taskkill.exe"),
|
|
25778
25779
|
whoami: () => system32("whoami.exe"),
|
|
25779
25780
|
icacls: () => system32("icacls.exe"),
|
|
25780
25781
|
tar: () => system32("tar.exe"),
|
|
@@ -27902,7 +27903,8 @@ var HeartbeatSchema = external_exports.object({
|
|
|
27902
27903
|
topProcesses: external_exports.array(external_exports.object({ name: Text(64), privateMb: external_exports.number().int().min(0).max(1e7) })).max(5),
|
|
27903
27904
|
/** The disk queue this PC last reported while asking the sandbox to slow down. */
|
|
27904
27905
|
diskQueueReported: external_exports.number().min(0).max(1e5).optional(),
|
|
27905
|
-
|
|
27906
|
+
/** "ending": the sandbox is shutting down in order (idle shutdown, sandbox stop, Windows shutting down). */
|
|
27907
|
+
phase: external_exports.enum(["ready", "working", "ending"])
|
|
27906
27908
|
});
|
|
27907
27909
|
var GuestResponseSchema = external_exports.object({
|
|
27908
27910
|
id: IdSchema,
|
package/dist/runtime/view.js
CHANGED
|
@@ -5756,6 +5756,7 @@ var SYSTEM = {
|
|
|
5756
5756
|
cmd: () => system32("cmd.exe"),
|
|
5757
5757
|
powershell: () => system32("WindowsPowerShell\\v1.0\\powershell.exe"),
|
|
5758
5758
|
tasklist: () => system32("tasklist.exe"),
|
|
5759
|
+
taskkill: () => system32("taskkill.exe"),
|
|
5759
5760
|
whoami: () => system32("whoami.exe"),
|
|
5760
5761
|
icacls: () => system32("icacls.exe"),
|
|
5761
5762
|
tar: () => system32("tar.exe"),
|
|
@@ -27923,7 +27924,8 @@ var HeartbeatSchema = external_exports.object({
|
|
|
27923
27924
|
topProcesses: external_exports.array(external_exports.object({ name: Text(64), privateMb: external_exports.number().int().min(0).max(1e7) })).max(5),
|
|
27924
27925
|
/** The disk queue this PC last reported while asking the sandbox to slow down. */
|
|
27925
27926
|
diskQueueReported: external_exports.number().min(0).max(1e5).optional(),
|
|
27926
|
-
|
|
27927
|
+
/** "ending": the sandbox is shutting down in order (idle shutdown, sandbox stop, Windows shutting down). */
|
|
27928
|
+
phase: external_exports.enum(["ready", "working", "ending"])
|
|
27927
27929
|
});
|
|
27928
27930
|
var GuestResponseSchema = external_exports.object({
|
|
27929
27931
|
id: IdSchema,
|
|
@@ -10,9 +10,12 @@ import { SYSTEM } from '../shared/system.js';
|
|
|
10
10
|
import { GuestRequestSchema, PressureStatusSchema } from '../shared/channel/index.js';
|
|
11
11
|
import { compilersAllowed, DIAL_CALM_SAMPLES, DIAL_STEPS, HOST_THRESHOLDS, buildJobs, cargoJobs, linkSlotsFor, usableCpus, effectiveCpuLimit, gentleCpuLimit, maxCompilers, chooseTargetDir, } from '../shared/memory/index.js';
|
|
12
12
|
import { HeartbeatSchema } from '../shared/channel/index.js';
|
|
13
|
-
import { heartbeatLine, lastEndOf } from '../host/life/index.js';
|
|
13
|
+
import { heartbeatLine, lastEndOf, vanishedCause, stopWarning } from '../host/life/index.js';
|
|
14
14
|
import { cappedMemoryMb, decideMemory, memoryNote, residentCapMb, residentNote, resolveMemoryMb, } from '../host/launch/index.js';
|
|
15
15
|
import { vgpuWarning, GENTLE_NOTE } from '../host/launch/risk-notes.js';
|
|
16
|
+
import { judgeSandbox } from '../host/health/index.js';
|
|
17
|
+
import { cleanUpLeftovers, cleanupQuestion, killArgs } from '../host/sandbox/index.js';
|
|
18
|
+
import { parseTasklist } from '../host/sharing/index.js';
|
|
16
19
|
import { bugcheckCode, cpuPercentBetween, explainStop, findDumps, findingLines, freezeLine, hostPressure, lowReason, severeHost, pageFileRisk, parseHostMemory, crashReportText, watchdogStep, WATCHDOG_START, } from '../host/life/index.js';
|
|
17
20
|
// The pure governor files only: governor.ts loads the guest runtime (session.json), which exists only in a sandbox.
|
|
18
21
|
import { canLower, canPause, decide, INITIAL_STATE, isLinker, linkerSlots, ROTATE_MS, } from '../guest/governor/decide.js';
|
|
@@ -252,12 +255,79 @@ export const heavyLoadSuite = {
|
|
|
252
255
|
t.check(!HeartbeatSchema.safeParse({ ...beat, topProcesses: Array.from({ length: 6 }, () => beat.topProcesses[0]) })
|
|
253
256
|
.success &&
|
|
254
257
|
!HeartbeatSchema.safeParse({ ...beat, topProcesses: [{ name: 'x'.repeat(500), privateMb: 1 }] }).success, 'an oversize heartbeat is refused');
|
|
255
|
-
t.check(heartbeatLine({ ...beat, topProcesses: [{ name: 'a
|
|
258
|
+
t.check(heartbeatLine({ ...beat, topProcesses: [{ name: 'a\u0007b', privateMb: 5 }] }).includes('\u0007') === false, 'guest text in the heartbeat line goes through safeText');
|
|
256
259
|
const life = { started: '2026-11-02T20:45:00.000Z', lastSeen: '2026-11-02T21:31:30.000Z', keepAwake: false };
|
|
257
260
|
t.check((lastEndOf(life, beat, false)?.text ?? '').includes('disks were overloaded') &&
|
|
258
261
|
lastEndOf(life, beat, true) === null &&
|
|
259
262
|
lastEndOf(null, beat, false) === null &&
|
|
260
263
|
lastEndOf({ ...life, ended: { at: life.lastSeen, cause: null } }, beat, false) === null, 'last end: an unexpected end names the disks; running, no record or a normal close say nothing');
|
|
264
|
+
t.section('Windows Sandbox programs left behind by a dead sandbox');
|
|
265
|
+
const list = [
|
|
266
|
+
'"explorer.exe","1200","Console","1","90,000 K"',
|
|
267
|
+
'"WindowsSandboxClient.exe","4100","Console","1","30,000 K"',
|
|
268
|
+
'"WindowsSandboxRemoteSession.exe","4200","Console","1","40,000 K"',
|
|
269
|
+
'"vmwp.exe","4300","Services","0","50,000 K"',
|
|
270
|
+
'"WindowsSandboxServer.exe","4400","Console","2","30,000 K"',
|
|
271
|
+
].join('\n');
|
|
272
|
+
const baseFacts = {
|
|
273
|
+
rows: parseTasklist(list),
|
|
274
|
+
mySession: 1,
|
|
275
|
+
heartbeatAgeMs: null,
|
|
276
|
+
lifeEnded: false,
|
|
277
|
+
startedAgeMs: 3_600_000,
|
|
278
|
+
answers: null,
|
|
279
|
+
};
|
|
280
|
+
const judged = (patch) => judgeSandbox({ ...baseFacts, ...patch });
|
|
281
|
+
t.check(judged({ rows: parseTasklist('"explorer.exe","1200","Console","1","1 K"') }).state === 'none', 'no Windows Sandbox program: none');
|
|
282
|
+
t.check(judged({ heartbeatAgeMs: 10_000 }).state === 'running' &&
|
|
283
|
+
judged({ answers: true, lifeEnded: true }).state === 'running', 'a fresh heartbeat, or a ping answered: the sandbox is ours and running');
|
|
284
|
+
t.check(judged({ startedAgeMs: 60_000 }).state === 'starting', 'started a minute ago, no heartbeat yet: starting');
|
|
285
|
+
const leftoverJudged = judged({ lifeEnded: true });
|
|
286
|
+
t.check(leftoverJudged.state === 'leftover' &&
|
|
287
|
+
leftoverJudged.pids.join() === '4100,4200' &&
|
|
288
|
+
leftoverJudged.images.join() === 'WindowsSandboxClient.exe,WindowsSandboxRemoteSession.exe', 'the watcher saw it end but the programs remain: leftover; only this session, only the user programs (not vmwp.exe, not session 2)', leftoverJudged.pids.join());
|
|
289
|
+
t.check(judged({ heartbeatAgeMs: 600_000 }).state === 'leftover', 'a heartbeat that stopped ten minutes ago: leftover');
|
|
290
|
+
t.check(judged({ heartbeatAgeMs: 100_000 }).state === 'running', 'a heartbeat 100 seconds old is not yet "gone"');
|
|
291
|
+
t.check(judged({}).state === 'running', 'no heartbeat at all (an older sandbox) and no end: treated as running, never ended');
|
|
292
|
+
t.check(judged({ mySession: null, lifeEnded: true }).state === 'foreign' &&
|
|
293
|
+
judged({ mySession: 3, lifeEnded: true }).pids.length === 0, "another session's sandbox is never touched");
|
|
294
|
+
t.check(killArgs([4100, 4200, 0, 2, -5, 1.5]).join(' ') === '/F /PID 4100 /PID 4200' &&
|
|
295
|
+
cleanupQuestion(leftoverJudged).lines.join(' ').includes('Nothing else'), 'taskkill gets only real process ids; the question names what is closed');
|
|
296
|
+
const run = async (facts, answers, over = {}) => {
|
|
297
|
+
const did = [];
|
|
298
|
+
let n = 0;
|
|
299
|
+
let asked = 0;
|
|
300
|
+
const outcome = await cleanUpLeftovers({
|
|
301
|
+
facts: () => Promise.resolve(facts[Math.min(n++, facts.length - 1)]),
|
|
302
|
+
end: (pids) => {
|
|
303
|
+
did.push('end ' + pids.join(','));
|
|
304
|
+
return true;
|
|
305
|
+
},
|
|
306
|
+
restartService: () => {
|
|
307
|
+
did.push('service');
|
|
308
|
+
return true;
|
|
309
|
+
},
|
|
310
|
+
confirm: () => Promise.resolve(answers[asked++] ?? false),
|
|
311
|
+
say: (line) => did.push('say ' + line),
|
|
312
|
+
...over,
|
|
313
|
+
});
|
|
314
|
+
return { outcome, did };
|
|
315
|
+
};
|
|
316
|
+
const noneLeft = { ...baseFacts, rows: [] };
|
|
317
|
+
const asked = await run([{ ...baseFacts, lifeEnded: true }, noneLeft], [true]);
|
|
318
|
+
t.check(asked.outcome === 'cleaned' && asked.did[0] === 'end 4100,4200' && !asked.did.includes('service'), 'leftover, yes: they are closed and nothing else happens');
|
|
319
|
+
const declinedRun = await run([{ ...baseFacts, lifeEnded: true }], [false]);
|
|
320
|
+
t.check(declinedRun.outcome === 'declined' && declinedRun.did.length === 0, 'leftover, no: nothing is closed');
|
|
321
|
+
const live = await run([{ ...baseFacts, heartbeatAgeMs: 5000 }], [true]);
|
|
322
|
+
t.check(live.outcome === 'alive' && !live.did.some((d) => d.startsWith('end')), 'a sandbox that is alive is never closed, whatever the answer');
|
|
323
|
+
t.check((await run([noneLeft], [true])).outcome === 'nothing', 'nothing left: nothing to do');
|
|
324
|
+
const stuck = { ...baseFacts, lifeEnded: true };
|
|
325
|
+
const serviceYes = await run([stuck, stuck], [true, true]);
|
|
326
|
+
t.check(serviceYes.outcome === 'service-restarted' && serviceYes.did.includes('service'), 'programs that will not go: asks, then restarts the service (elevated)');
|
|
327
|
+
const serviceNo = await run([stuck, stuck], [true, false]);
|
|
328
|
+
t.check(serviceNo.outcome === 'declined' && !serviceNo.did.includes('service'), 'the service is only restarted when you say yes');
|
|
329
|
+
const forced = await run([stuck, noneLeft], [true], { alsoService: true });
|
|
330
|
+
t.check(forced.outcome === 'service-restarted', 'asked to restart the service as well: it does, without a second question');
|
|
261
331
|
t.section('the governor over a port (fake Windows)');
|
|
262
332
|
const calls = [];
|
|
263
333
|
const port = {
|
|
@@ -403,6 +473,53 @@ export const heavyLoadSuite = {
|
|
|
403
473
|
t.check(pageFileRisk(pc({ pageFile: 'fixed', commitLimitMb: 90000, commitAvailableMb: 80000 }), 20000) === null, 'a fixed but large one is fine');
|
|
404
474
|
t.section('the crash report');
|
|
405
475
|
const ev = (id, provider, at, process = '') => ({ id, provider, at, process });
|
|
476
|
+
t.section('a sandbox that vanishes');
|
|
477
|
+
const kindOf = (events) => explainStop(events)?.kind ?? 'none';
|
|
478
|
+
t.check(kindOf([
|
|
479
|
+
ev(4101, 'Display', '2026-11-02T21:31:00Z', 'Display driver nvlddmkm stopped responding and has successfully recovered.'),
|
|
480
|
+
]) === 'gpu' && explainStop([ev(4101, 'Display', '2026-11-02T21:31:00Z')]).text.includes('graphics driver'), 'Display 4101: the graphics driver reset (gpu)');
|
|
481
|
+
t.check(kindOf([
|
|
482
|
+
ev(1001, 'Windows Error Reporting', '2026-11-02T21:31:00Z', 'Fault bucket 1, type 5' + ' Event Name: LiveKernelEvent' + ' P1: 141' + ' P2: ffff'),
|
|
483
|
+
]) === 'gpu' &&
|
|
484
|
+
kindOf([
|
|
485
|
+
ev(1001, 'Windows Error Reporting', '2026-11-02T21:31:00Z', 'Event Name: LiveKernelEvent P1: 1a8 P2: 0'),
|
|
486
|
+
]) === 'gpu' &&
|
|
487
|
+
kindOf([
|
|
488
|
+
ev(1001, 'Windows Error Reporting', '2026-11-02T21:31:00Z', 'Event Name: LiveKernelEvent P1: 999 P2: 0'),
|
|
489
|
+
]) === 'none', 'LiveKernelEvent 117, 141 and 1a8 are GPU resets; other codes are not a cause');
|
|
490
|
+
t.check(explainStop([ev(1000, 'Application Error', '2026-11-02T21:31:00Z', 'Faulting application name: vmwp.exe')])
|
|
491
|
+
?.kind === 'hyperv' &&
|
|
492
|
+
kindOf([ev(1001, 'Windows Error Reporting', '2026-11-02T21:31:00Z', 'vmmemWindowsSandbox crashed')]) ===
|
|
493
|
+
'hyperv' &&
|
|
494
|
+
kindOf([ev(1000, 'Application Error', '2026-11-02T21:31:00Z', 'Faulting application name: notepad.exe')]) ===
|
|
495
|
+
'none', 'a crash report of vmwp.exe or vmmem is a cause; another program is not');
|
|
496
|
+
t.check(kindOf([ev(12010, 'Microsoft-Windows-Hyper-V-VmSwitch', '2026-11-02T21:31:00Z')]) === 'hyperv' &&
|
|
497
|
+
kindOf([ev(40001, 'Microsoft-Windows-Hyper-V-Worker', '2026-11-02T21:31:00Z')]) === 'none' &&
|
|
498
|
+
kindOf([ev(18590, 'Microsoft-Windows-Hyper-V-Worker', '2026-11-02T21:31:00Z', 'ErrorCode0: 0x50')]) ===
|
|
499
|
+
'sandboxCrash', 'Hyper-V worker, switch and compute errors are a cause; the sandbox crashing inside is still its own kind');
|
|
500
|
+
const started = '2026-11-02T20:45:00.000Z';
|
|
501
|
+
const endMs = Date.parse('2026-11-02T21:32:00.000Z');
|
|
502
|
+
const working = {
|
|
503
|
+
at: '2026-11-02T21:31:50.000Z',
|
|
504
|
+
uptimeS: 2800,
|
|
505
|
+
ramFreePct: 60,
|
|
506
|
+
commitPct: 30,
|
|
507
|
+
step: 0,
|
|
508
|
+
hostStep: 0,
|
|
509
|
+
topProcesses: [],
|
|
510
|
+
phase: 'working',
|
|
511
|
+
};
|
|
512
|
+
t.check(vanishedCause(working, started, false, endMs)?.kind === 'vanished', 'busy, fresh heartbeat, no word of leaving: vanished');
|
|
513
|
+
t.check(vanishedCause({ ...working, phase: 'ending' }, started, false, endMs) === null &&
|
|
514
|
+
vanishedCause(working, started, true, endMs) === null &&
|
|
515
|
+
vanishedCause(null, started, false, endMs) === null, 'an orderly end, a stop on purpose, or no heartbeat is not a vanishing');
|
|
516
|
+
t.check(vanishedCause({ ...working, phase: 'ready' }, started, false, endMs) === null &&
|
|
517
|
+
vanishedCause({ ...working, phase: 'ready', step: 2 }, started, false, endMs)?.kind === 'vanished', 'an idle sandbox (window closed by hand) is on purpose; one that was slowing builds down is not');
|
|
518
|
+
t.check(vanishedCause({ ...working, at: '2026-11-02T21:20:00.000Z' }, started, false, endMs) === null &&
|
|
519
|
+
vanishedCause({ ...working, at: '2026-11-01T21:31:50.000Z' }, started, false, endMs) === null, 'a stale heartbeat, or one from an earlier run, says nothing about this end');
|
|
520
|
+
const gone = vanishedCause(working, started, false, endMs);
|
|
521
|
+
const warning = stopWarning({ started, lastSeen: '2026-11-02T21:31:30.000Z', keepAwake: false, ended: { at: gone.at, cause: gone } }, () => null);
|
|
522
|
+
t.check((warning ?? '').includes('ended suddenly') && (warning ?? '').includes('vGPU'), 'the next launch warns about a vanished sandbox, and says what usually causes it');
|
|
406
523
|
t.check(bugcheckCode('BugcheckCode 209') === '0xD1' && bugcheckCode('BugcheckCode 0') === null, 'Kernel-Power 41 stop code');
|
|
407
524
|
const cause = explainStop([ev(41, 'Microsoft-Windows-Kernel-Power', '2026-10-15T20:53:47Z', 'BugcheckCode 0')]);
|
|
408
525
|
t.check(cause?.kind === 'crash' && cause.text.includes('freeze'), 'no stop code: a freeze, crash or power cut');
|
|
@@ -150,7 +150,8 @@ export const HeartbeatSchema = z.object({
|
|
|
150
150
|
topProcesses: z.array(z.object({ name: Text(64), privateMb: z.number().int().min(0).max(1e7) })).max(5),
|
|
151
151
|
/** The disk queue this PC last reported while asking the sandbox to slow down. */
|
|
152
152
|
diskQueueReported: z.number().min(0).max(100000).optional(),
|
|
153
|
-
|
|
153
|
+
/** "ending": the sandbox is shutting down in order (idle shutdown, sandbox stop, Windows shutting down). */
|
|
154
|
+
phase: z.enum(['ready', 'working', 'ending']),
|
|
154
155
|
});
|
|
155
156
|
/** C:\AgentData\run\responses\<id>.json: the guest's reply. */
|
|
156
157
|
export const GuestResponseSchema = z.object({
|
package/dist/shared/system.d.ts
CHANGED
package/dist/shared/system.js
CHANGED
|
@@ -9,6 +9,7 @@ export const SYSTEM = {
|
|
|
9
9
|
cmd: () => system32('cmd.exe'),
|
|
10
10
|
powershell: () => system32('WindowsPowerShell\\v1.0\\powershell.exe'),
|
|
11
11
|
tasklist: () => system32('tasklist.exe'),
|
|
12
|
+
taskkill: () => system32('taskkill.exe'),
|
|
12
13
|
whoami: () => system32('whoami.exe'),
|
|
13
14
|
icacls: () => system32('icacls.exe'),
|
|
14
15
|
tar: () => system32('tar.exe'),
|
package/dist/sync/watcher.js
CHANGED
|
@@ -13,7 +13,7 @@ import { readForwards, sandboxAddress, startPortForwards } from '../host/network
|
|
|
13
13
|
import { AuditCollector, AuditLog, readAuditSettings } from '../host/audit/index.js';
|
|
14
14
|
import { readMarkedProjects, saveDataSize, watchAndMark } from '../host/guard/index.js';
|
|
15
15
|
import { sizeWithin } from '../core/disk.js';
|
|
16
|
-
import { autoRestartAllowed, causeSince, appendHostMemory, cpuPercentBetween, KeepAwake, LifeRecord, HostMemoryStream, readHostMemoryAsync, readLoadSettings, REQUEST_SECONDS, WATCHDOG_START, watchdogStep, saveCrashReport, SessionLedger, } from '../host/life/index.js';
|
|
16
|
+
import { autoRestartAllowed, causeSince, readHeartbeat, vanishedCause, appendHostMemory, cpuPercentBetween, KeepAwake, LifeRecord, HostMemoryStream, readHostMemoryAsync, readLoadSettings, REQUEST_SECONDS, WATCHDOG_START, watchdogStep, saveCrashReport, SessionLedger, } from '../host/life/index.js';
|
|
17
17
|
import { SandboxChannel } from '../host/channel/index.js';
|
|
18
18
|
import { ProjectRegistry } from '../host/projects/registry.js';
|
|
19
19
|
import { ContainedStorage, setNameOf, storageOf } from '../host/storage/index.js';
|
|
@@ -67,7 +67,7 @@ export function watcherRunning(dataPath) {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
/** The ways the sandbox itself ends badly (the PC going down takes the watcher with it, so it never gets here). */
|
|
70
|
-
const RESTARTABLE = ['sandboxCrash', 'memory'];
|
|
70
|
+
const RESTARTABLE = ['sandboxCrash', 'memory', 'gpu', 'hyperv', 'vanished'];
|
|
71
71
|
/**
|
|
72
72
|
* The background watcher of one data folder: while the sandbox runs it syncs history back every minute, guards the
|
|
73
73
|
* projects' git folders, keeps filtered workspaces in step, forwards ports, marks downloaded files, records the audit
|
|
@@ -318,8 +318,13 @@ export class Watcher {
|
|
|
318
318
|
recordEnd() {
|
|
319
319
|
try {
|
|
320
320
|
const last = this.life.read();
|
|
321
|
-
|
|
322
|
-
const
|
|
321
|
+
// Windows' own log first; when it says nothing, a sandbox that was busy and left no word of leaving "vanished".
|
|
322
|
+
const logged = last ? causeSince(last.lastSeen, this.storageRoot) : null;
|
|
323
|
+
const cause = logged ??
|
|
324
|
+
(last
|
|
325
|
+
? vanishedCause(readHeartbeat(this.dataPath), last.started, this.ledger.read()?.closedOnPurpose === true, Date.now())
|
|
326
|
+
: null);
|
|
327
|
+
const report = last && cause ? saveCrashReport(this.storageRoot, this.dataPath, last.lastSeen, cause) : null;
|
|
323
328
|
this.life.end(cause, new Date(), report);
|
|
324
329
|
this.cause = cause;
|
|
325
330
|
// Ended with nothing wrong (its window closed, or it shut itself down idle): on purpose.
|
package/dist/web/index.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>Sandbox</title>
|
|
7
7
|
<link rel="icon" href="data:," />
|
|
8
8
|
<link rel="stylesheet" href="/main-XT7T6ODM.css" />
|
|
9
|
-
<script type="module" src="/main-
|
|
9
|
+
<script type="module" src="/main-GVZWWP4I.js"></script>
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|