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
106 changes: 106 additions & 0 deletions src/lib/onboard/sandbox-registration.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { createHash } from "node:crypto";
import { createRequire } from "node:module";
import { afterEach, describe, expect, it, vi } from "vitest";

import { managedStartupE2eProfile } from "../../../scripts/checks/generate-managed-startup-profile-fixture.mts";
import {
serializedHostLocalInferenceReceipt,
serializedLlamaCppHostLocalInferenceReceipt,
} from "../../../test/helpers/host-local-inference-receipt";
import type { SandboxWorkloadReceipt } from "../state/registry/types";
import { createSandboxHostLocalInferenceProvenance } from "../state/registry/host-local-inference";
import {
MANAGED_IMAGE_CAPABILITY_CONTRACT_VERSION,
MANAGED_IMAGE_REPOSITORIES,
MANAGED_IMAGE_STARTUP_PROFILE_CONTRACT_VERSION,
type ManagedImageAgent,
} from "./managed-image/contract";
import { encodeManagedStartupProfile } from "./managed-startup/profile";

const requireDist = createRequire(import.meta.url);
const onboardSession = requireDist("../state/onboard-session.js");
Expand All @@ -31,7 +41,103 @@ const runtimeFields = {
openshellVersion: "0.1.2",
};

function managedWorkloadReceipt(
agent: ManagedImageAgent,
): Extract<SandboxWorkloadReceipt, { readonly kind: "managed-image" }> {
const encodedProfile = encodeManagedStartupProfile(managedStartupE2eProfile(agent));
const digest = agent === "openclaw" ? "a" : "b";
return {
schemaVersion: 1,
kind: "managed-image",
reference: `${MANAGED_IMAGE_REPOSITORIES[agent]}@sha256:${digest.repeat(64)}`,
platform: "linux/amd64",
release: "v0.0.100",
sourceRevision: "d".repeat(40),
sourceCohort: "ghrun-9356-1",
capabilityContractVersion: MANAGED_IMAGE_CAPABILITY_CONTRACT_VERSION,
startupProfileContractVersion: MANAGED_IMAGE_STARTUP_PROFILE_CONTRACT_VERSION,
encodedProfile,
startupProfileSha256: createHash("sha256").update(encodedProfile, "utf8").digest("hex"),
credentialProxyReplayRequired: false,
shared: true,
};
}

function createdRegistryEntryInput(
overrides: Partial<Parameters<typeof buildCreatedSandboxRegistryEntry>[0]> = {},
): Parameters<typeof buildCreatedSandboxRegistryEntry>[0] {
return {
sandboxName: "demo",
inferenceSelection: {
model: "llama",
provider: "openai-compatible",
endpointUrl: null,
credentialEnv: null,
preferredInferenceApi: null,
compatibleEndpointReasoning: null,
compatibleEndpointReasoningEffort: null,
nimContainer: null,
},
runtimeFields,
agent: null,
agentVersionKnown: true,
imageTag: null,
appliedPolicies: [],
plannedMessagingState: undefined,
hermesToolGateways: [],
hermesDashboardState: { enabled: false, config: null },
dashboardPort: 18789,
gatewayName: "nemoclaw",
gatewayPort: 8080,
...overrides,
};
}

describe("buildCreatedSandboxRegistryEntry", () => {
it("records explicit OpenClaw identity for a managed workload receipt (#9356)", () => {
const workload = managedWorkloadReceipt("openclaw");
const entry = buildCreatedSandboxRegistryEntry(
createdRegistryEntryInput({ imageTag: workload.reference, workload }),
);
const authority = requireDist(
"./workload/authority.ts",
) as typeof import("./workload/authority");

expect(entry.agent).toBe("openclaw");
expect(authority.readManagedWorkloadAuthority(entry)?.agent).toBe("openclaw");
});

it("keeps the legacy OpenClaw registry identity for a custom image (#9356)", () => {
const entry = buildCreatedSandboxRegistryEntry(
createdRegistryEntryInput({
agentVersionKnown: false,
fromDockerfile: "/tmp/Dockerfile.custom",
imageTag: "custom-openclaw:latest",
workload: {
schemaVersion: 1,
kind: "legacy-dockerfile",
reference: "custom-openclaw:latest",
shared: false,
},
}),
);

expect(entry.agent).toBeNull();
});

it("rejects a managed receipt for a different agent before registry mutation (#9356)", () => {
const workload = managedWorkloadReceipt("hermes");
const registerSandbox = vi.fn();

expect(() =>
registerCreatedSandbox({
...createdRegistryEntryInput({ imageTag: workload.reference, workload }),
registerSandbox,
}),
).toThrow(/agent identity does not match its managed workload receipt/u);
expect(registerSandbox).not.toHaveBeenCalled();
});

it("copies matching session profile provenance into the durable registry (#8246)", () => {
const provenance = {
schemaVersion: 1,
Expand Down
18 changes: 16 additions & 2 deletions src/lib/onboard/sandbox-registration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { DEFAULT_TOOL_DISCLOSURE, type ToolDisclosure } from "../tool-disclosure
import type { DcodeAutoApprovalMode } from "./dcode-auto-approval";
import { cloneSandboxHostMounts } from "../state/registry/host-mount";
import { resolveOnboardHermesApiPort } from "./hermes-api-port";
import { isManagedImageAgent, MANAGED_IMAGE_REPOSITORIES } from "./managed-image/contract";
import {
getHermesDashboardRegistryFields,
type HermesDashboardOnboardState,
Expand All @@ -37,7 +38,7 @@ import {
requireRuntimeProviderBundleForSandbox,
requireRuntimeProviderMutationAuthority,
} from "./runtime-provider/access";
import { getSandboxAgentRegistryFields } from "./sandbox-agent";
import { getRequestedSandboxAgentName, getSandboxAgentRegistryFields } from "./sandbox-agent";

export type CreatedSandboxRuntimeFields = Pick<
SandboxEntry,
Expand Down Expand Up @@ -224,13 +225,26 @@ export function buildCreatedSandboxRegistryEntry(
hostLocalInferenceReceipt,
);
}
const agentFields = getSandboxAgentRegistryFields(input.agent, input.agentVersionKnown);
if (workload?.kind === "managed-image") {
const requestedAgent = getRequestedSandboxAgentName(input.agent);
if (
!isManagedImageAgent(requestedAgent) ||
!workload.reference.startsWith(`${MANAGED_IMAGE_REPOSITORIES[requestedAgent]}@sha256:`)
) {
throw new RuntimeProviderSelectionError(
"Sandbox agent identity does not match its managed workload receipt.",
);
}
agentFields.agent = requestedAgent;
}

return {
name: input.sandboxName,
servingProfileProvenance,
...inferenceSelectionRegistryFields(input.inferenceSelection),
...input.runtimeFields,
...getSandboxAgentRegistryFields(input.agent, input.agentVersionKnown),
...agentFields,
imageTag: input.imageTag,
workload,
...(hostLocalInferenceReceipt !== undefined ? { hostLocalInferenceReceipt } : {}),
Expand Down
2 changes: 1 addition & 1 deletion test/helpers/managed-image-buildless-e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,7 @@ function assertManagedLaunch(
result.payload.registerCalls,
)}`,
).toBeDefined();
expect(registration?.agent).toBe(agent === "openclaw" ? null : agent);
expect(registration?.agent).toBe(agent);
if (agent === "langchain-deepagents-code") {
expect(registration?.dashboardPort).toBe(0);
}
Expand Down
Loading