Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
06eaa07
fix(onboard): require managed images for stock agents
ericksoa Aug 24, 2026
81d1ff9
fix(e2e): allow publication evidence validation
ericksoa Aug 24, 2026
e7b1c41
fix(e2e): bind public NVIDIA qualification key
ericksoa Aug 24, 2026
dd3bead
fix(e2e): bind trusted cohort receipt
ericksoa Aug 24, 2026
7aed52b
fix(e2e): repair exact runtime lifecycle probes
ericksoa Aug 24, 2026
202c292
fix(e2e): enforce complete cohort receipts
ericksoa Aug 24, 2026
fbedb28
test(onboard): remove stock fallback fixtures
ericksoa Aug 24, 2026
603b5eb
test(onboard): remove stock fallback fixtures
ericksoa Aug 24, 2026
1432c79
fix(e2e): bind self-hosted catalog revision
senthilr-nv Aug 24, 2026
dac3a86
test(e2e): bind publication workflow contract
rsliter Aug 24, 2026
f1f3392
fix(e2e): bind runtime cohort descriptors
ericksoa Aug 24, 2026
eaecfba
fix(onboard): preserve trusted agent build context
rsliter Aug 24, 2026
916c6ab
ci(e2e): trigger exact managed image publication
rsliter Aug 24, 2026
b8990bd
docs(onboard): clarify generated build context
rsliter Aug 24, 2026
ce3c1da
merge: resolve main conflicts for E2E
rsliter Aug 24, 2026
cd8e797
Merge branch 'main' into fix/main-e2e-managed-only-recovery-20260824
cv Aug 24, 2026
9473645
ci(test): rebalance E2E support shards
rsliter Aug 24, 2026
3644622
test(e2e): correct credential and Slack fixtures
rsliter Aug 24, 2026
6cebd43
Merge remote-tracking branch 'origin/main' into codex/pr10113-e2e-202…
rsliter Aug 24, 2026
871edc3
docs(e2e): correct workflow and CLI guidance
rsliter Aug 24, 2026
078fedf
Merge remote-tracking branch 'origin/main' into codex/pr10113-e2e-202…
rsliter Aug 24, 2026
55c4d2c
test(e2e): tunnel Slack socket probe through proxy
rsliter Aug 25, 2026
fcaf24f
Merge remote-tracking branch 'refs/remotes/origin/main' into fix/main…
ericksoa Aug 25, 2026
7b3b33d
fix(test): repair grouped imports after main merge
ericksoa Aug 25, 2026
b96c25f
fix(hermes): refresh managed wrapper integrity pin
ericksoa Aug 25, 2026
adc42ec
ci(e2e): bind PR qualification to branch image cohort
ericksoa Aug 25, 2026
16b7e5f
Revert "ci(e2e): bind PR qualification to branch image cohort"
ericksoa Aug 25, 2026
6fe1c0b
fix(hermes): sync profile policy integrity pin
rsliter Aug 25, 2026
b0cba3a
merge: resolve current main conflicts for E2E
rsliter Aug 25, 2026
ee88761
test: restore virtual source-shape imports
rsliter Aug 25, 2026
3420b6d
merge: bind PR E2E to published main cohort
ericksoa Aug 25, 2026
91d92c9
merge: resolve latest main conflicts for E2E
rsliter Aug 25, 2026
6e80d61
merge: integrate concurrent PR E2E update
rsliter Aug 25, 2026
9464631
test: repair grouped test paths
rsliter Aug 25, 2026
c25243e
merge: incorporate current main for E2E comparison
ericksoa Aug 25, 2026
db2c297
merge: incorporate main validation metadata
ericksoa Aug 25, 2026
7e4dcc6
merge: incorporate current main path repairs
sandl99 Aug 25, 2026
69848aa
fix(onboard): clean stale Hermes state on recreation
sandl99 Aug 25, 2026
84c615e
Merge branch 'main' into fix/main-e2e-managed-only-recovery-20260824
sandl99 Aug 25, 2026
7c05243
Merge branch 'main' into fix/main-e2e-managed-only-recovery-20260824
cv Aug 25, 2026
e91f8b1
fix(onboard): bind managed image provenance and cleanup
sandl99 Aug 25, 2026
c021ad2
test(e2e): stabilize PTY identity fixture
sandl99 Aug 25, 2026
78c97b5
merge: incorporate current main messaging fixes
ericksoa Aug 25, 2026
f0f881f
merge: incorporate current main provider reconciliation
ericksoa Aug 25, 2026
da42148
test(e2e): preserve trusted PR controller compatibility
rsliter Aug 25, 2026
117025f
merge: incorporate current main runner diagnostics
ericksoa Aug 25, 2026
d6630ed
test(onboard): stay within recreation file budget
rsliter Aug 25, 2026
26ad79e
merge: incorporate current main E2E fixes
ericksoa Aug 25, 2026
43e0b97
test(e2e): use scoped Slack credential references
rsliter Aug 25, 2026
53ff2b9
test(e2e): wait for sandbox readiness before recovery
rsliter Aug 25, 2026
71343c7
merge: incorporate current main runtime fixes
ericksoa Aug 25, 2026
f171144
Merge remote-tracking branch 'origin/main' into HEAD
ericksoa Aug 25, 2026
9249ba2
Merge remote-tracking branch 'origin/main' into HEAD
ericksoa Aug 25, 2026
042237b
merge: synchronize current main
rsliter Aug 26, 2026
cefc213
Merge remote-tracking branch 'origin/main' into codex/merge-main-1011…
rsliter Aug 26, 2026
323f05b
merge: synchronize current main for E2E
ericksoa Aug 26, 2026
40a3c23
fix(e2e): accept base image workflow rename
ericksoa Aug 26, 2026
550e807
fix(e2e): accept renamed publisher jobs
ericksoa Aug 26, 2026
fcf27c5
merge: synchronize current main for E2E
ericksoa Aug 26, 2026
de04ad8
merge: synchronize latest main for E2E
ericksoa Aug 26, 2026
cdfd674
merge: synchronize current main for CI
ericksoa Aug 27, 2026
a09600f
fix(e2e): close qualification trust gaps
ericksoa Aug 27, 2026
6ab7a35
refactor(e2e): reuse managed cohort receipt checks
ericksoa Aug 27, 2026
0fa7dc9
test(onboard): allow loaded shard headroom
ericksoa Aug 27, 2026
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
4 changes: 4 additions & 0 deletions .github/workflows/e2e-standard-profile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ on:
managed_image_revision:
required: true
type: string
managed_image_receipt:
required: true
type: string
credential_boundary:
required: true
type: string
Expand Down Expand Up @@ -113,6 +116,7 @@ jobs:
E2E_TARGET_ID: ${{ inputs.target_id }}
NEMOCLAW_RUN_LIVE_E2E: "1"
NEMOCLAW_E2E_EXPECTED_SHA: ${{ inputs.candidate_sha }}
E2E_MANAGED_IMAGE_COHORT_RECEIPT: ${{ inputs.managed_image_receipt }}
NEMOCLAW_E2E_CORRELATION_ID: ${{ inputs.risk_signal_correlation_id }}
NEMOCLAW_E2E_RISK_SIGNAL_EXPECTED_SHA: ${{ inputs.risk_signal_expected_sha }}
NEMOCLAW_LLAMA_CPP_QUALIFICATION_HEAD_SHA: ${{ inputs.candidate_sha }}
Expand Down
182 changes: 77 additions & 105 deletions .github/workflows/e2e.yaml

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions .github/workflows/pr-self-hosted.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ jobs:
printf 'selected=%s\n' "$selected"
} >>"$GITHUB_OUTPUT"

- name: Check out trusted publication gate
- name: Check out PR base SHA for publication verification
if: ${{ steps.changed.outputs.selected == 'true' }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand All @@ -104,12 +104,12 @@ jobs:
with:
node-version: 22

- name: Install trusted publication gate dependencies
- name: Install publication verifier dependencies
if: ${{ steps.changed.outputs.selected == 'true' }}
run: npm ci --ignore-scripts --no-audit --no-fund

- id: publication
name: Verify applicable base-image publication
name: Verify complete base and managed-image publication
if: ${{ steps.changed.outputs.selected == 'true' }}
env:
EXPECTED_SHA: ${{ steps.changed.outputs.base_sha }}
Expand Down
5 changes: 5 additions & 0 deletions ci/source-shape-test-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@
"test": "selects exact-commit rootless evidence for Portable recovery changes (#9707)",
"category": "security"
},
{
"file": "test/e2e/support/pr-self-hosted-llama-selector.test.ts",
"test": "binds trusted base publication to the generic NVIDIA GPU job",
"category": "security"
},
{
"file": "test/agents/hermes/hermes-final-image-layout.test.ts",
"test": "pins $source to its current bytes at $target",
Expand Down
2 changes: 1 addition & 1 deletion docs/deployment/sandbox-hardening.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The NemoClaw sandbox image applies several security measures to reduce the attac
## Immutable Managed Image Selection

Stock onboarding through the OpenShell Docker driver selects an exact managed-image digest and validates the complete OpenClaw, Hermes, and LangChain Deep Agents Code publication cohort before sandbox creation.
If registry or catalog availability prevents resolution, the ordinary `prefer-managed` path builds the shipped, reviewed repository Dockerfile instead; it never selects an unpinned `:latest` image.
If registry or catalog availability prevents resolution, stock onboarding stops before sandbox creation and does not build a shipped Dockerfile.
An available but incomplete, mixed, mutable, wrong-platform, or identity-inconsistent cohort fails closed before sandbox creation.
Passing `--from <Dockerfile>` remains a separate explicit opt-in whose complete custom image must be reviewed independently.

Expand Down
3 changes: 2 additions & 1 deletion docs/get-started/quickstart-hermes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@ Review the [Prerequisites](prerequisites) before you begin.
If the installer does not offer Express setup, or if you enter `n` at the Express prompt on a supported non-N1x host, choose an inference provider and model, then provide its credential when prompted.
For that interactive path, skip optional web search and messaging setup on a first run, then accept the suggested network policy tier.
With the OpenShell Docker driver, stock Hermes onboarding normally uses the release's exact managed-image digest.
If registry or catalog availability prevents resolution, it builds the shipped repository Dockerfile instead; it never selects an unpinned `:latest` image.
If registry or catalog availability prevents resolution, stock onboarding stops before sandbox creation and does not build a shipped Dockerfile.
Invalid or inconsistent catalog evidence fails closed before sandbox creation.
An explicit `nemohermes onboard --from <Dockerfile>` remains a separate custom-image path.
</Step>

<Step title="Confirm the Sandbox Is Ready">
Expand Down
3 changes: 2 additions & 1 deletion docs/get-started/quickstart-langchain-deepagents-code.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ Review the [Prerequisites](prerequisites) before you begin.
If the installer does not offer Express setup, or if you enter `n` at the Express prompt on a supported non-N1x host, choose an inference provider and model, then provide its credential when prompted.
For that interactive path, accept the suggested network policy tier on a first run.
With the OpenShell Docker driver, stock Deep Agents Code onboarding normally uses the release's exact managed-image digest.
If registry or catalog availability prevents resolution, it builds the shipped repository Dockerfile instead; it never selects an unpinned `:latest` image.
If registry or catalog availability prevents resolution, stock onboarding stops before sandbox creation and does not build a shipped Dockerfile.
Invalid or inconsistent catalog evidence fails closed before sandbox creation.
An explicit `nemo-deepagents onboard --from <Dockerfile>` remains a separate custom-image path.
</Step>

<Step title="Confirm the Sandbox Is Ready">
Expand Down
3 changes: 2 additions & 1 deletion docs/get-started/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ Review the [Prerequisites](prerequisites) before you begin.
Press Enter to accept the suggested `my-assistant` sandbox name.
For a first run, skip optional web search and messaging setup, then accept the suggested network policy tier.
With the OpenShell Docker driver, stock OpenClaw onboarding normally uses the release's exact managed-image digest.
If registry or catalog availability prevents resolution, it builds the shipped repository Dockerfile instead; it never selects an unpinned `:latest` image.
If registry or catalog availability prevents resolution, stock onboarding stops before sandbox creation and does not build a shipped Dockerfile.
Invalid or inconsistent catalog evidence fails closed before sandbox creation.
An explicit `nemoclaw onboard --from <Dockerfile>` remains a separate custom-image path.

<Note>
The installer can display `Run express install with these settings? [Y/n]:` before the agent-selection prompt on DGX Spark, qualifying DGX Station, or Windows Subsystem for Linux (WSL) hosts.
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ The maintained onboarding path for this agent does not consume the component.

Stock onboarding through the OpenShell Docker driver for OpenClaw, Hermes, and LangChain Deep Agents Code selects an immutable managed image for the installed release and host architecture.
Before selecting one agent image, NemoClaw validates a complete three-agent cohort with one release, source revision, publication cohort, and compatible startup and capability contracts.
If registry or catalog availability prevents resolution, the ordinary `prefer-managed` path builds the shipped, reviewed repository Dockerfile instead and never selects an unpinned tag.
If registry or catalog availability prevents resolution, stock onboarding stops before sandbox creation and does not build a shipped Dockerfile.
Available catalog evidence that is incomplete, mixed, mutable, wrong-platform, or identity-inconsistent fails closed before sandbox creation.
An explicit `--from <Dockerfile>` remains a separate complete custom-image path.
The portable experimental profile retains its existing workload path, and native Podman remains disabled.
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -977,8 +977,8 @@ Docker and Podman gateways cannot reuse one state directory. For NemoClaw-manage

Without `--from`, onboarding through the OpenShell Docker driver for OpenClaw, Hermes, and LangChain Deep Agents Code selects an immutable managed image for the installed release and host architecture.
NemoClaw validates the complete three-agent publication cohort before selecting any member.
If registry or catalog availability prevents resolution, the ordinary `prefer-managed` path builds the shipped, reviewed repository Dockerfile instead; it never selects an unpinned `:latest` image.
Available catalog evidence that is incomplete, mixed, mutable, wrong-platform, or identity-inconsistent fails closed before sandbox creation.
If registry or catalog availability prevents resolution, stock onboarding stops before sandbox creation and does not build a shipped Dockerfile.
Catalog evidence that is incomplete, mixed, mutable, wrong-platform, or identity-inconsistent also fails closed before sandbox creation.
The portable experimental profile and native Podman are not part of this activation.

Build the sandbox image from a custom Dockerfile instead of the stock NemoClaw image.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
} from "./provider-inference.test-support";

type TestProviderInferenceOptions = ProviderInferenceStateOptions<Gpu, Agent, Host>;

const PROBE_IMAGE = `quay.io/curl/curl@sha256:${"b".repeat(64)}`;
const NIM_IMAGE = `nvcr.io/nim/meta/llama@sha256:${"d".repeat(64)}`;
const MANAGED_IMAGE = `nvcr.io/nvidia/vllm@sha256:${"c".repeat(64)}`;
Expand All @@ -39,7 +38,6 @@ const receiptWriter = {
targetSha256: "1".repeat(64),
writeExact: (value: string) => value,
};

function publishedManagedReceipt(
service: "nim" | "vllm",
model: string,
Expand Down Expand Up @@ -245,6 +243,25 @@ function llamaCppLifecycleSelection(
}

describe("provider inference host-local startup selection", () => {
it("does not require host-local application support for a hosted candidate provider", () => {
const resolver = vi.fn();
const resolve = createCachedHostLocalInferenceSetupResolver({
resolver,
application: "pi",
provider: "nvidia-prod",
model: "nvidia/nemotron-3-super-120b-a12b",
acceleration: "cpu",
requireToolCalling: null,
freshRequireToolCalling: true,
allowPublishedResume: false,
recover: false,
recordToolCallingRequirement: vi.fn(),
});

expect(resolve("pi-sandbox")).toEqual({});
expect(resolver).not.toHaveBeenCalled();
});

it.each(["openclaw", "hermes", "langchain-deepagents-code"] as const)(
"dispatches a published %s llama.cpp route through the common lifecycle exactly once",
async (application) => {
Expand Down
1 change: 1 addition & 0 deletions src/lib/onboard/machine/handlers/provider-inference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@ function hostLocalInferenceSetupOptions(
(candidate): candidate is HostLocalInferenceApplication => candidate === input.application,
);
if (!application) {
if (!isHostLocalInferenceProvider(input.provider)) return {};
throw new Error(`Unsupported host-local inference application '${input.application}'.`);
}
const selected = resolver({
Expand Down
66 changes: 56 additions & 10 deletions src/lib/onboard/managed-workload/onboard-orchestration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";

import { describe, expect, it, vi } from "vitest";
import { afterAll, describe, expect, it, vi } from "vitest";

import { createHermesStateVolumeDockerHarness } from "../__test-helpers__/hermes-state-volume";

Expand All @@ -14,6 +14,9 @@ const preparationState = vi.hoisted(() => ({
useUnavailableCatalog: false,
}));
const prepareSandboxWorkloadSource = vi.hoisted(() => vi.fn());
const INSTALLED_REVISION = vi.hoisted(() => "d".repeat(40));
const releaseRoot = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-release-root-"));
fs.writeFileSync(path.join(releaseRoot, ".version"), "0.0.0\n");

vi.mock("../workload/preparation", async (importOriginal) => {
const original = await importOriginal<typeof import("../workload/preparation")>();
Expand All @@ -30,7 +33,10 @@ vi.mock("../workload/preparation", async (importOriginal) => {
return { ...original, prepareSandboxWorkloadSource };
});

vi.mock("../../core/version", () => ({ getVersion: () => "v0.0.0" }));
vi.mock("../../core/version", () => ({
getBuildIdentity: () => ({ nemoclawVersion: "0.0.0", sourceRevision: INSTALLED_REVISION }),
getVersion: () => "v0.0.0",
}));

import {
createManagedHermesStateVolumeOnboardLifecycle,
Expand Down Expand Up @@ -71,7 +77,7 @@ function createFreshOnboardingRuntime(
agentName: "openclaw",
legacyDockerfilePath: "agents/openclaw/Dockerfile",
customDockerfilePath: null,
rootDir: "/tmp/nemoclaw",
rootDir: releaseRoot,
model: "model",
provider: "provider",
preferredInferenceApi: null,
Expand Down Expand Up @@ -118,6 +124,10 @@ async function expectUnsupportedHermesPortableSources(
}

describe("managed workload onboard orchestration", () => {
afterAll(() => {
fs.rmSync(releaseRoot, { force: true, recursive: true });
});

it("activates stock managed images only for shipped agents outside Portable", () => {
expect(
shouldActivateStockManagedRuntime({
Expand Down Expand Up @@ -237,7 +247,7 @@ describe("managed workload onboard orchestration", () => {
},
);

lifecycle.materializeSandboxCreatePlan({} as never, (input) => {
lifecycle!.materializeSandboxCreatePlan({} as never, (input) => {
expect(input.managedStateMount).toMatchObject({ target: "/sandbox/.hermes" });
return {} as never;
});
Expand All @@ -249,17 +259,31 @@ describe("managed workload onboard orchestration", () => {

it("retains the live qualification catalog revision during fresh onboarding (#9385)", async () => {
const catalogRevision = "a".repeat(40);
const { prepared, runtime } = createFreshOnboardingRuntime({
GITHUB_ACTIONS: "true",
E2E_MANAGED_IMAGE_REVISION: catalogRevision,
});
const { prepared, runtime } = createFreshOnboardingRuntime(
{
GITHUB_ACTIONS: "true",
E2E_MANAGED_IMAGE_REVISION: catalogRevision,
},
{ stockManagedRuntime: true },
);

await expect(runtime.ensurePreparedWorkload()).resolves.toBe(prepared);
expect(prepareSandboxWorkloadSource).toHaveBeenCalledExactlyOnceWith(
expect.objectContaining({ catalogRevision }),
);
});

it("does not apply the stock cohort revision outside stock onboarding", async () => {
const { prepared, runtime } = createFreshOnboardingRuntime({
GITHUB_ACTIONS: "true",
E2E_MANAGED_IMAGE_REVISION: "a".repeat(40),
});

await expect(runtime.ensurePreparedWorkload()).resolves.toBe(prepared);
expect(prepareSandboxWorkloadSource).toHaveBeenCalledOnce();
expect(prepareSandboxWorkloadSource.mock.calls[0]?.[0]).not.toHaveProperty("catalogRevision");
});

it("binds fresh onboarding to the exact PR catalog (#9464)", async () => {
const catalogRevision = "b".repeat(40);
const fixtureRoot = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-live-e2e-catalog-"));
Expand Down Expand Up @@ -295,14 +319,35 @@ describe("managed workload onboard orchestration", () => {
expect(prepareSandboxWorkloadSource.mock.calls[0]?.[0]).not.toHaveProperty("catalogRevision");
});

it("retains an exact installed revision outside GitHub Actions", async () => {
const { prepared, runtime } = createFreshOnboardingRuntime({
NEMOCLAW_INSTALL_REF: INSTALLED_REVISION,
});

await expect(runtime.ensurePreparedWorkload()).resolves.toBe(prepared);
expect(prepareSandboxWorkloadSource).toHaveBeenCalledExactlyOnceWith(
expect.objectContaining({ catalogRevision: INSTALLED_REVISION }),
);
});

it("resolves final-image patch metadata after managed build-context staging", async () => {
const resolutionMetadata = { key: "published-dcode-base" };
const trustedDockerfile = path.join(
process.cwd(),
"agents",
"langchain-deepagents-code",
"Dockerfile",
);
let staged = false;
const resolvePatchInput = vi.fn(() => {
expect(staged).toBe(true);
return { preResolvedBaseImageMetadata: resolutionMetadata } as never;
return {
fromDockerfile: trustedDockerfile,
preResolvedBaseImageMetadata: resolutionMetadata,
} as never;
});
const resolveSandboxBuildPatch = vi.fn(async (input: Record<string, unknown>) => {
expect(input.fromDockerfile).toBeNull();
expect(input.preResolvedBaseImageMetadata).toBe(resolutionMetadata);
expect(input.stagedDockerfile).toBe("/tmp/nemoclaw-staged-context/Dockerfile");
return { buildId: "dcode-build", dashboardRemoteBindPrepared: false };
Expand Down Expand Up @@ -348,8 +393,9 @@ describe("managed workload onboard orchestration", () => {
agent: {
name: "langchain-deepagents-code",
displayName: "LangChain Deep Agents Code",
dockerfilePath: trustedDockerfile,
},
fromDockerfile: null,
fromDockerfile: trustedDockerfile,
createAgentSandbox: () => {
staged = true;
return {
Expand Down
22 changes: 19 additions & 3 deletions src/lib/onboard/managed-workload/onboard-orchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import type { MessagingTokenDef } from "../messaging-prep";
import { resolveSandboxBuildContext, resolveSandboxBuildPatch } from "../prepared-dcode-rebuild";
import {
CURRENT_RUNTIME_PROVIDER_BUNDLES,
normalizeRuntimeProviderIdentity,
type RuntimeProviderBundle,
resolveRuntimeProviderBundle,
} from "../runtime-provider/access";
Expand All @@ -54,6 +55,7 @@ import {
import { getSandboxReadyTimeoutSecs } from "../sandbox-gpu-create";
import type { SandboxGpuConfig } from "../sandbox-gpu-mode";
import {
installedManagedImageCatalogRevision,
liveE2eManagedImageCatalog,
liveE2eManagedImageRevision,
type PreparedSandboxWorkloadSource,
Expand All @@ -66,6 +68,7 @@ import {
import { resolveSandboxWorkloadRuntimeCapabilities } from "../workload/runtime";
import {
prepareManagedHermesStateVolume,
removeManagedHermesStateVolume,
type ManagedHermesStateVolumeContext,
type ManagedHermesStateVolumeDeps,
} from "./hermes-state-volume";
Expand All @@ -80,6 +83,8 @@ type BootstrapProvider = RuntimeProviderBundle & {
readonly bootstrap: RuntimeProviderManagedImageBootstrapSurface;
};

export { normalizeRuntimeProviderIdentity, removeManagedHermesStateVolume };

export type ManagedHermesStateVolumeOnboardLifecycle = {
materializeSandboxCreatePlan(
input: MaterializeSandboxCreatePlanInput,
Expand Down Expand Up @@ -240,11 +245,18 @@ export function createManagedWorkloadOnboardRuntime(
let preparedProfile: BuiltManagedStartupOnboardProfile | null = null;

const ensurePreparedWorkload = async (): Promise<PreparedSandboxWorkloadSource> => {
const catalogRevision = liveE2eManagedImageRevision(input.startupProfile.environment);
const liveCatalogRevision = input.stockManagedRuntime
? liveE2eManagedImageRevision(input.startupProfile.environment)
: null;
const liveCatalog = liveE2eManagedImageCatalog(input.startupProfile.environment);
if (catalogRevision && liveCatalog) {
if (liveCatalogRevision && liveCatalog) {
throw new Error("live E2E managed-image revision and catalog authority conflict");
}
const catalogRevision =
liveCatalogRevision ??
(liveCatalog || input.tempManagedRuntimeCatalog || input.managedWorkloadRebuild
? null
: installedManagedImageCatalogRevision(input.startupProfile.environment, input.rootDir));
preparedWorkloadPromise ??= input.managedWorkloadRebuild
? Promise.resolve(
prepareSandboxWorkloadSourceFromRebuildHandoff(
Expand Down Expand Up @@ -480,11 +492,15 @@ export async function prepareOnboardSandboxWorkloadLaunch(
} else {
const buildContext = requireLegacyBuildContext(legacyBuildContext);
input.dependencies.prepareSandboxBuildPatchConfig({ configuredMessagingChannels });
const patchInput = input.legacy.resolvePatchInput();
const patch = await (input.dependencies.resolveSandboxBuildPatch ?? resolveSandboxBuildPatch)({
// Build-context staging resolves managed-agent base-image provenance.
// Read the patch input only after that boundary so the final image gets
// the exact metadata produced by the same staging operation.
...input.legacy.resolvePatchInput(),
...patchInput,
// An explicit path to the checked-in agent Dockerfile is staged through
// the trusted agent builder. Preserve that classification at patch time.
fromDockerfile: buildContext.origin === "generated" ? null : patchInput.fromDockerfile,
selectedGpuRoute: initialGpuRoute,
stagedDockerfile: buildContext.stagedDockerfile,
});
Expand Down
Loading
Loading