diff --git a/src/lib/onboard.ts b/src/lib/onboard.ts index f4584f1266a..78b2b3feb85 100644 --- a/src/lib/onboard.ts +++ b/src/lib/onboard.ts @@ -364,6 +364,7 @@ import type { OpenShellInstallResult, } from "./onboard/openshell-install"; import { decidePolicyCarryForward } from "./onboard/policy-carryforward"; +import { resolveSandboxGpuMode, type SandboxGpuFlag, type SandboxGpuMode } from "./onboard/sandbox-gpu-mode"; import type { SelectionDrift } from "./onboard/selection-drift"; import type { ModelCatalogFetchResult, @@ -1240,9 +1241,6 @@ function shouldAllowOpenshellAboveBlueprintMax( return shouldUseOpenshellDevChannel(platform, env) && isOpenshellDevVersion(versionOutput); } -type SandboxGpuMode = "auto" | "1" | "0"; -type SandboxGpuFlag = "enable" | "disable" | null; - type SandboxGpuConfig = { mode: SandboxGpuMode; hostGpuDetected: boolean; @@ -1287,9 +1285,7 @@ function resolveSandboxGpuConfig( errors.push("NEMOCLAW_SANDBOX_GPU must be one of: auto, 1, 0."); } - let mode: SandboxGpuMode = envMode ?? "auto"; - if (options.flag === "enable") mode = "1"; - if (options.flag === "disable") mode = "0"; + let mode = resolveSandboxGpuMode({ envMode, gpu, flag: options.flag }); const device = (options.device ?? env.NEMOCLAW_SANDBOX_GPU_DEVICE ?? "").trim() || null; if (device && mode === "0") { @@ -10035,6 +10031,8 @@ async function onboard(opts: OnboardOptions = {}): Promise { ? " GPU passthrough requested; passing --gpu to OpenShell gateway and sandbox creation." : " NVIDIA GPU detected; enabling OpenShell GPU passthrough. Use --no-gpu to opt out.", ); + } else if (gpu?.platform === "jetson") { + note(" GPU sandbox passthrough disabled by default on Jetson."); } else if (process.platform === "linux") { // Hint when hardware is present but drivers are missing. try { diff --git a/src/lib/onboard/sandbox-gpu-mode.ts b/src/lib/onboard/sandbox-gpu-mode.ts new file mode 100644 index 00000000000..158fcfee421 --- /dev/null +++ b/src/lib/onboard/sandbox-gpu-mode.ts @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import type { GpuDetection } from "../inference/nim"; + +export type SandboxGpuMode = "auto" | "1" | "0"; +export type SandboxGpuFlag = "enable" | "disable" | null; + +export function resolveSandboxGpuMode(args: { + envMode: SandboxGpuMode | null; + gpu: GpuDetection | null | undefined; + flag?: SandboxGpuFlag; +}): SandboxGpuMode { + let mode: SandboxGpuMode = args.envMode ?? "auto"; + // GPU sandbox passthrough does not currently work on Jetson; disable by default + if (args.gpu?.platform === "jetson" && args.envMode === null) mode = "0"; + if (args.flag === "enable") mode = "1"; + if (args.flag === "disable") mode = "0"; + return mode; +} diff --git a/test/onboard.test.ts b/test/onboard.test.ts index f8ce6b019a0..4f6eda15fac 100644 --- a/test/onboard.test.ts +++ b/test/onboard.test.ts @@ -386,6 +386,17 @@ describe("onboard helpers", () => { expect(forced.errors.join("\n")).toContain("no NVIDIA GPU"); }); + it("defaults to CPU sandbox on Jetson when NEMOCLAW_SANDBOX_GPU is unset", () => { + const jetson = { type: "nvidia", platform: "jetson" as const }; + expect(resolveSandboxGpuConfig(jetson, { env: {} }).sandboxGpuEnabled).toBe(false); + // Explicit env opt-in still wins over the platform default. + expect( + resolveSandboxGpuConfig(jetson, { env: { NEMOCLAW_SANDBOX_GPU: "1" } }).sandboxGpuEnabled, + ).toBe(true); + // --gpu also overrides the platform default. + expect(resolveSandboxGpuConfig(jetson, { flag: "enable", env: {} }).mode).toBe("1"); + }); + it("resumes sandbox GPU auto mode without turning CPU fallback into explicit opt-out", () => { const resumedAuto = getResumeSandboxGpuOverrides( { sandboxGpuMode: "auto", sandboxGpuDevice: null },