Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/lib/onboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,8 @@ const {
}: typeof import("./onboard/created-sandbox-finalization") = require("./onboard/created-sandbox-finalization");
const providerKeyBridge: typeof import("./onboard/provider-key-bridge") = require("./onboard/provider-key-bridge");
const compatibleEndpointGatewayRoute: typeof import("./onboard/inference-providers/compatible-endpoint-gateway-route") = require("./onboard/inference-providers/compatible-endpoint-gateway-route");
const {
isLinuxDockerDriverGatewayEnabled,
}: typeof import("./onboard/docker-driver-platform") = require("./onboard/docker-driver-platform");
const dockerDriverPlatform: typeof import("./onboard/docker-driver-platform") = require("./onboard/docker-driver-platform");
const { isLinuxDockerDriverGatewayEnabled } = dockerDriverPlatform;
const {
reconcileGatewayGpuReuseForGpuIntent,
}: typeof import("./onboard/gateway-gpu-passthrough") = require("./onboard/gateway-gpu-passthrough");
Expand Down Expand Up @@ -2207,7 +2206,8 @@ async function recoverGatewayRuntime() {

const { getSandboxRuntimeRegistryFields, hasSandboxGpuDrift, updateReusedSandboxMetadata } =
sandboxRegistryMetadata.createSandboxRegistryMetadataHelpers({
isLinuxDockerDriverGatewayEnabled,
getOpenShellComputeDriverName: () =>
dockerDriverPlatform.resolveCurrentOpenShellComputePlan().driverName,
getInstalledOpenshellVersion,
runCaptureOpenshell,
});
Expand Down
70 changes: 70 additions & 0 deletions src/lib/onboard/compute/plan.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { describe, expect, it } from "vitest";
import { isLinuxDockerDriverGatewayEnabled } from "../docker-driver-platform";
import { resolveCurrentOpenShellComputePlan, usesManagedDockerGateway } from "./plan";

describe("current OpenShell compute plan", () => {
it.each([
{
label: "Linux x64",
platform: "linux" as const,
arch: "x64" as const,
driverName: "docker",
gatewayLauncher: "nemoclaw",
},
{
label: "Linux arm64",
platform: "linux" as const,
arch: "arm64" as const,
driverName: "docker",
gatewayLauncher: "nemoclaw",
},
{
label: "Apple Silicon macOS",
platform: "darwin" as const,
arch: "arm64" as const,
driverName: "docker",
gatewayLauncher: "nemoclaw",
},
{
label: "Intel macOS",
platform: "darwin" as const,
arch: "x64" as const,
driverName: "kubernetes",
gatewayLauncher: "openshell",
},
{
label: "Windows x64",
platform: "win32" as const,
arch: "x64" as const,
driverName: "kubernetes",
gatewayLauncher: "openshell",
},
])("preserves the existing driver and gateway-launch behavior on $label (#7744)", ({
platform,
arch,
driverName,
gatewayLauncher,
}) => {
expect(resolveCurrentOpenShellComputePlan(platform, arch)).toEqual({
driverName,
gatewayLauncher,
});
expect(isLinuxDockerDriverGatewayEnabled(platform, arch)).toBe(driverName === "docker");
});

it.each([
{ driverName: "docker", gatewayLauncher: "nemoclaw", expected: true },
{ driverName: "docker", gatewayLauncher: "openshell", expected: false },
{ driverName: "podman", gatewayLauncher: "nemoclaw", expected: false },
{ driverName: "mxc", gatewayLauncher: "nemoclaw", expected: false },
] as const)("reports Docker lifecycle ownership as $expected for $driverName with the $gatewayLauncher launcher (#7744)", ({
driverName,
gatewayLauncher,
expected,
}) => {
expect(usesManagedDockerGateway({ driverName, gatewayLauncher })).toBe(expected);
});
});
36 changes: 36 additions & 0 deletions src/lib/onboard/compute/plan.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

export type OpenShellGatewayLauncher = "nemoclaw" | "openshell";

/**
* Keeps OpenShell driver identity separate from the component that launches
* its gateway. A future driver does not inherit Docker lifecycle behavior
* because NemoClaw launches its gateway.
*/
export interface OpenShellComputePlan {
readonly driverName: string;
readonly gatewayLauncher: OpenShellGatewayLauncher;
}

/**
* Describes the behavior NemoClaw uses today. Driver selection will move behind
* this seam without changing the existing Docker and Kubernetes paths first.
*/
export function resolveCurrentOpenShellComputePlan(
platform: NodeJS.Platform = process.platform,
arch: NodeJS.Architecture = process.arch,
): OpenShellComputePlan {
const managedDockerGateway = platform === "linux" || (platform === "darwin" && arch === "arm64");

return {
driverName: managedDockerGateway ? "docker" : "kubernetes",
gatewayLauncher: managedDockerGateway ? "nemoclaw" : "openshell",
};
}

export function usesManagedDockerGateway(
plan: Pick<OpenShellComputePlan, "driverName" | "gatewayLauncher">,
): boolean {
return plan.driverName === "docker" && plan.gatewayLauncher === "nemoclaw";
}
6 changes: 5 additions & 1 deletion src/lib/onboard/docker-driver-platform.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { resolveCurrentOpenShellComputePlan, usesManagedDockerGateway } from "./compute/plan";

export { resolveCurrentOpenShellComputePlan } from "./compute/plan";

export function isLinuxDockerDriverGatewayEnabled(
platform: NodeJS.Platform = process.platform,
arch: NodeJS.Architecture = process.arch,
): boolean {
return platform === "linux" || (platform === "darwin" && arch === "arm64");
return usesManagedDockerGateway(resolveCurrentOpenShellComputePlan(platform, arch));
}
66 changes: 30 additions & 36 deletions src/lib/onboard/sandbox-registry-metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,16 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import type { AgentDefinition } from "../agent/defs";
import type { SandboxGpuConfig } from "./sandbox-gpu-mode";

const ORIGINAL_PLATFORM = Object.getOwnPropertyDescriptor(process, "platform");

/**
* Overrides process.platform for runtime-driver metadata tests.
*/
function setPlatform(platform: NodeJS.Platform): void {
Object.defineProperty(process, "platform", { value: platform, configurable: true });
}

/**
* Restores the original process.platform descriptor after each platform-specific assertion.
* Loads the compiled metadata helpers with an explicit resolved compute driver.
*/
function restorePlatform(): void {
if (ORIGINAL_PLATFORM) {
Object.defineProperty(process, "platform", ORIGINAL_PLATFORM);
}
}

/**
* Loads the compiled metadata helpers after each test has configured process state.
*/
async function makeHelpers(opts: { dockerDriverEnabled: boolean }) {
async function makeHelpers(driverName: string) {
// Import the compiled module: sandbox-registry-metadata.ts pulls in state/registry,
// which transitively requires the JS-only `./platform` helper that vitest cannot
// resolve from TS source. Same pattern as `vm-dns-monkeypatch.test.ts`.
const metadata = await import("./sandbox-registry-metadata");
return metadata.createSandboxRegistryMetadataHelpers({
isLinuxDockerDriverGatewayEnabled: () => opts.dockerDriverEnabled,
getOpenShellComputeDriverName: () => driverName,
getInstalledOpenshellVersion: () => "0.0.42",
runCaptureOpenshell: () => null,
});
Expand Down Expand Up @@ -106,7 +88,7 @@ describe("sandbox registry metadata", () => {
});

const helpers = metadata.createSandboxRegistryMetadataHelpers({
isLinuxDockerDriverGatewayEnabled: () => true,
getOpenShellComputeDriverName: () => "docker",
getInstalledOpenshellVersion: () => "0.0.44",
runCaptureOpenshell: () => "openshell 0.0.44",
});
Expand Down Expand Up @@ -155,7 +137,7 @@ describe("sandbox registry metadata", () => {
const dashboardPorts = await import("./dashboard-port");
const gatewayRegistry = await import("../state/gateway-registry");
const helpers = metadata.createSandboxRegistryMetadataHelpers({
isLinuxDockerDriverGatewayEnabled: () => true,
getOpenShellComputeDriverName: () => "docker",
getInstalledOpenshellVersion: () => "0.0.44",
runCaptureOpenshell: () => "openshell 0.0.44",
});
Expand Down Expand Up @@ -190,32 +172,44 @@ describe("sandbox registry metadata", () => {
});

describe("getSandboxRuntimeRegistryFields openshellDriver", () => {
afterEach(restorePlatform);

it("records Docker for macOS sandboxes on the Docker-driver gateway path", async () => {
setPlatform("darwin");
const helpers = await makeHelpers({ dockerDriverEnabled: true });
it("records the resolved Docker compute driver (#7744)", async () => {
const helpers = await makeHelpers("docker");

const fields = helpers.getSandboxRuntimeRegistryFields(GPU_OFF);

expect(fields.openshellDriver).toBe("docker");
});

it("records Docker for Linux sandboxes on the Docker-driver gateway path", async () => {
setPlatform("linux");
const helpers = await makeHelpers({ dockerDriverEnabled: true });
it("records the resolved Kubernetes compute driver (#7744)", async () => {
const helpers = await makeHelpers("kubernetes");

const fields = helpers.getSandboxRuntimeRegistryFields(GPU_OFF);

expect(fields.openshellDriver).toBe("docker");
expect(fields.openshellDriver).toBe("kubernetes");
});

it("records Kubernetes for legacy Linux sandboxes when the Docker-driver gateway is disabled", async () => {
setPlatform("linux");
const helpers = await makeHelpers({ dockerDriverEnabled: false });
it.each([
"podman",
"mxc",
])("passes the resolved %s driver through to registry metadata (#7744)", async (driverName) => {
const helpers = await makeHelpers(driverName);

const fields = helpers.getSandboxRuntimeRegistryFields(GPU_OFF);

expect(fields.openshellDriver).toBe("kubernetes");
expect(fields.openshellDriver).toBe(driverName);
});

it("resolves driver identity when metadata is recorded rather than at module load (#7744)", async () => {
const metadata = await import("./sandbox-registry-metadata");
let driverName = "docker";
const helpers = metadata.createSandboxRegistryMetadataHelpers({
getOpenShellComputeDriverName: () => driverName,
getInstalledOpenshellVersion: () => "0.0.42",
runCaptureOpenshell: () => null,
});

driverName = "kubernetes";

expect(helpers.getSandboxRuntimeRegistryFields(GPU_OFF).openshellDriver).toBe("kubernetes");
});
});
10 changes: 4 additions & 6 deletions src/lib/onboard/sandbox-registry-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { getSandboxAgentRegistryFields } from "./sandbox-agent";
import type { SandboxGpuConfig } from "./sandbox-gpu-mode";

export interface SandboxRegistryMetadataDeps {
isLinuxDockerDriverGatewayEnabled(): boolean;
getOpenShellComputeDriverName(): string;
getInstalledOpenshellVersion(versionOutput?: string | null): string | null;
runCaptureOpenshell(args: string[], opts?: Record<string, unknown>): string | null;
}
Expand Down Expand Up @@ -55,10 +55,6 @@ export function createSandboxRegistryMetadataHelpers(
| "openshellDriver"
| "openshellVersion"
> {
// OpenShell's Docker-driver gateway always starts with OPENSHELL_DRIVERS=docker,
// including on macOS arm64 (#3454). Recording "vm" for darwin here makes later
// setup misclassify the sandbox and run VM-only DNS monkeypatch / warning paths
// (#3728).
return {
gpuEnabled: config.sandboxGpuEnabled,
hostGpuDetected: config.hostGpuDetected,
Expand All @@ -68,7 +64,9 @@ export function createSandboxRegistryMetadataHelpers(
// Only persist a proof when this run produced one; omit on reuse/update
// paths so a prior proof result is preserved rather than nulled out.
...(config.sandboxGpuProof ? { sandboxGpuProof: config.sandboxGpuProof } : {}),
openshellDriver: deps.isLinuxDockerDriverGatewayEnabled() ? "docker" : "kubernetes",
// Driver identity comes from the resolved compute plan, not the host
// gateway launcher; those layers may differ (#7744).
openshellDriver: deps.getOpenShellComputeDriverName(),
openshellVersion: deps.getInstalledOpenshellVersion(
deps.runCaptureOpenshell(["--version"], { ignoreError: true }),
),
Expand Down
Loading