Skip to content

Commit 393a3ec

Browse files
authored
Merge branch 'main' into k8s-autoscaling
2 parents 8741da7 + b9c82f1 commit 393a3ec

14 files changed

Lines changed: 975 additions & 64 deletions

src/lib/actions/sandbox/rebuild-agent-base-image-preflight.test.ts

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ const agentDefsPath = "../../agent/defs.js";
1818
const agentOnboardPath = "../../agent/onboard.js";
1919
const dockerImagePath = "../../adapters/docker/image.js";
2020
const overrideEnvVar = "NEMOCLAW_HERMES_SANDBOX_BASE_IMAGE_REF";
21+
const cachedRemoteRef = `ghcr.io/nvidia/nemoclaw/hermes-sandbox-base@sha256:${"a".repeat(64)}`;
22+
const refreshedRemoteRef = `ghcr.io/nvidia/nemoclaw/hermes-sandbox-base@sha256:${"b".repeat(64)}`;
23+
const rebuiltLocalRef = `nemoclaw-hermes-sandbox-base-local:image-${"c".repeat(64)}`;
24+
const rebuiltLocalTrust = {
25+
ref: rebuiltLocalRef,
26+
provenance: `${"d".repeat(64)}.${"e".repeat(64)}`,
27+
};
2128

2229
function loadRebuildFlowHelpers(): RebuildFlowHelpersModule {
2330
delete require.cache[requireDist.resolve(rebuildFlowHelpersPath)];
@@ -67,17 +74,24 @@ describe("ensureRebuildAgentBaseImage", () => {
6774
vi.spyOn(loadAgentDefs(), "loadAgent").mockReturnValue(agent);
6875
const ensureAgentBaseImage = vi
6976
.spyOn(loadAgentOnboard(), "ensureAgentBaseImage")
70-
.mockImplementation((_agent, options = {}) => ({
71-
imageTag: options.forceBaseImageRefresh
72-
? "hermes:refreshed"
73-
: options.resolutionHint
74-
? "hermes:cached"
75-
: "hermes:rebuilt",
76-
built: !options.resolutionHint,
77-
}));
77+
.mockImplementation((_agent, options = {}) =>
78+
options.forceBaseImageRebuild
79+
? {
80+
imageTag: rebuiltLocalRef,
81+
built: true,
82+
trustedLocalOverride: rebuiltLocalTrust,
83+
}
84+
: {
85+
imageTag: options.forceBaseImageRefresh ? refreshedRemoteRef : cachedRemoteRef,
86+
built: false,
87+
},
88+
);
7889
const bindLocalAgentBaseImageToPinnedProvenance = vi
7990
.spyOn(loadAgentOnboard(), "bindLocalAgentBaseImageToPinnedProvenance")
8091
.mockReturnValue(null);
92+
const bindLocalAgentBaseImageHandoffToResolution = vi
93+
.spyOn(loadAgentOnboard(), "bindLocalAgentBaseImageHandoffToResolution")
94+
.mockReturnValue(null);
8195
const pinAgentSandboxBaseImageRef = vi
8296
.spyOn(loadAgentOnboard(), "pinAgentSandboxBaseImageRef")
8397
.mockImplementation((_agentName, imageRef) => imageRef);
@@ -92,6 +106,7 @@ describe("ensureRebuildAgentBaseImage", () => {
92106
agent,
93107
ensureAgentBaseImage,
94108
bindLocalAgentBaseImageToPinnedProvenance,
109+
bindLocalAgentBaseImageHandoffToResolution,
95110
pinAgentSandboxBaseImageRef,
96111
pinTrustedAgentRemoteBaseImageOverrideForOperation,
97112
restoreTrustedRemoteOverride,
@@ -105,7 +120,7 @@ describe("ensureRebuildAgentBaseImage", () => {
105120

106121
expect(ensureRebuildAgentBaseImage("hermes", makeBail(), { resolutionHint: hint })).toEqual({
107122
ok: true,
108-
imageRef: "hermes:cached",
123+
imageRef: cachedRemoteRef,
109124
overrideEnvVar,
110125
});
111126
expect(ensureAgentBaseImage).toHaveBeenCalledWith(agent, {
@@ -120,8 +135,9 @@ describe("ensureRebuildAgentBaseImage", () => {
120135

121136
expect(ensureRebuildAgentBaseImage("hermes", makeBail())).toEqual({
122137
ok: true,
123-
imageRef: "hermes:rebuilt",
138+
imageRef: rebuiltLocalRef,
124139
overrideEnvVar,
140+
trustedLocalOverride: rebuiltLocalTrust,
125141
});
126142
expect(ensureAgentBaseImage).toHaveBeenCalledWith(agent, {
127143
forceBaseImageRebuild: true,
@@ -179,7 +195,7 @@ describe("ensureRebuildAgentBaseImage", () => {
179195
}),
180196
).toEqual({
181197
ok: true,
182-
imageRef: "hermes:refreshed",
198+
imageRef: refreshedRemoteRef,
183199
overrideEnvVar,
184200
});
185201
expect(ensureAgentBaseImage).toHaveBeenCalledWith(agent, {
@@ -271,11 +287,32 @@ describe("ensureRebuildAgentBaseImage", () => {
271287
});
272288

273289
it("retains exit cleanup until a failed temporary removal succeeds (#7144)", () => {
274-
const { ensureAgentBaseImage, pinAgentSandboxBaseImageRef, dockerRmi } = setup();
290+
const {
291+
ensureAgentBaseImage,
292+
bindLocalAgentBaseImageHandoffToResolution,
293+
pinAgentSandboxBaseImageRef,
294+
dockerRmi,
295+
} = setup();
275296
const platformRef = "hermes:mutable-override";
276297
const localRef = `nemoclaw-hermes-sandbox-base-local:rebuild-123-${"b".repeat(16)}-image-${"c".repeat(64)}`;
277-
ensureAgentBaseImage.mockReturnValue({ imageTag: platformRef, built: false });
298+
const resolutionMetadata = {
299+
ref: platformRef,
300+
digest: null,
301+
source: "local",
302+
imageId: `sha256:${"c".repeat(64)}`,
303+
} as SandboxBaseImageResolutionMetadata;
304+
const handoffTrust = {
305+
ref: localRef,
306+
provenance: `${"d".repeat(64)}.${"e".repeat(64)}`,
307+
};
308+
ensureAgentBaseImage.mockReturnValue({
309+
imageTag: platformRef,
310+
built: false,
311+
resolutionMetadata,
312+
reusedResolutionHint: resolutionMetadata,
313+
});
278314
pinAgentSandboxBaseImageRef.mockReturnValue(localRef);
315+
bindLocalAgentBaseImageHandoffToResolution.mockReturnValue(handoffTrust);
279316
dockerRmi
280317
.mockReturnValueOnce({ status: 23 } as never)
281318
.mockReturnValueOnce({ status: 0 } as never);

src/lib/actions/sandbox/rebuild-dcode-base-image-lease.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515

1616
const overrideEnvName = "NEMOCLAW_LANGCHAIN_DEEPAGENTS_CODE_SANDBOX_BASE_IMAGE_REF";
1717
const trustedLocalOverride = {
18-
ref: "nemoclaw-langchain-deepagents-code-base:test",
18+
ref: `nemoclaw-langchain-deepagents-code-sandbox-base-local:image-${"a".repeat(64)}`,
1919
provenance: `${"b".repeat(64)}.${"c".repeat(64)}`,
2020
};
2121

src/lib/actions/sandbox/rebuild-flow-helpers.test.ts

Lines changed: 188 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as dockerImage from "../../adapters/docker/image";
66
import * as agentDefs from "../../agent/defs";
77
import * as agentOnboard from "../../agent/onboard";
88
import * as gatewayRuntime from "../../gateway-runtime-action";
9+
import type { SandboxBaseImageResolutionMetadata } from "../../sandbox-base-image";
910
import * as sandboxState from "../../state/sandbox";
1011
import * as userManagedFilesProbe from "../../state/user-managed-files-probe";
1112
import {
@@ -135,7 +136,9 @@ describe("rebuild agent base image preflight", () => {
135136
});
136137

137138
function mockBaseImagePreflight(imageRef: string) {
138-
vi.spyOn(agentDefs, "loadAgent").mockReturnValue({ name: "hermes" } as never);
139+
const loadAgent = vi
140+
.spyOn(agentDefs, "loadAgent")
141+
.mockReturnValue({ name: "hermes", displayName: "Hermes Agent" } as never);
139142
const ensureAgentBaseImage = vi
140143
.spyOn(agentOnboard, "ensureAgentBaseImage")
141144
.mockReturnValue({ imageTag: imageRef, built: true });
@@ -145,39 +148,57 @@ describe("rebuild agent base image preflight", () => {
145148
const pinAgentSandboxBaseImageRef = vi
146149
.spyOn(agentOnboard, "pinAgentSandboxBaseImageRef")
147150
.mockImplementation((_agentName, ref) => String(ref));
151+
const bindLocalAgentBaseImageHandoffToResolution = vi
152+
.spyOn(agentOnboard, "bindLocalAgentBaseImageHandoffToResolution")
153+
.mockReturnValue(null);
148154
const dockerRmi = vi.spyOn(dockerImage, "dockerRmi").mockReturnValue({ status: 0 } as never);
149155
return {
156+
loadAgent,
150157
ensureAgentBaseImage,
151158
bindLocalAgentBaseImageToPinnedProvenance,
159+
bindLocalAgentBaseImageHandoffToResolution,
152160
pinAgentSandboxBaseImageRef,
153161
dockerRmi,
154162
};
155163
}
156164

157165
it("forces a repository-local build and returns its exact ref when no override exists", () => {
158-
const imageRef = "nemoclaw-hermes-sandbox-base-local:12345678";
166+
const imageRef = `nemoclaw-hermes-sandbox-base-local:image-${"a".repeat(64)}`;
167+
const trustedLocalOverride = {
168+
ref: imageRef,
169+
provenance: `${"b".repeat(64)}.${"c".repeat(64)}`,
170+
};
159171
const { ensureAgentBaseImage } = mockBaseImagePreflight(imageRef);
172+
ensureAgentBaseImage.mockReturnValue({
173+
imageTag: imageRef,
174+
built: true,
175+
trustedLocalOverride,
176+
});
160177

161178
const result = ensureRebuildAgentBaseImage("hermes", makeBail());
162179

163180
expect(ensureAgentBaseImage).toHaveBeenCalledWith(expect.objectContaining({ name: "hermes" }), {
164181
forceBaseImageRebuild: true,
165182
});
166-
expect(result).toEqual({ ok: true, imageRef, overrideEnvVar });
183+
expect(result).toEqual({
184+
ok: true,
185+
imageRef,
186+
overrideEnvVar,
187+
trustedLocalOverride,
188+
});
167189
});
168190

169-
it("resolves an explicit caller override instead of replacing it during preflight", () => {
191+
it("fails closed when an explicit local result lacks validated outer metadata", () => {
170192
process.env[overrideEnvVar] = "nemoclaw-hermes-sandbox-base-local:caller";
171193
const mutableRef = "nemoclaw-hermes-sandbox-base-local:resolved";
172-
const immutableRef = `nemoclaw-hermes-sandbox-base-local:image-${"a".repeat(64)}`;
173-
const {
174-
ensureAgentBaseImage,
175-
bindLocalAgentBaseImageToPinnedProvenance,
176-
pinAgentSandboxBaseImageRef,
177-
} = mockBaseImagePreflight(mutableRef);
178-
pinAgentSandboxBaseImageRef.mockReturnValue(immutableRef);
194+
const rebuildRef = `nemoclaw-hermes-sandbox-base-local:rebuild-343338-${"b".repeat(16)}-image-${"a".repeat(64)}`;
195+
const { ensureAgentBaseImage, pinAgentSandboxBaseImageRef, dockerRmi } =
196+
mockBaseImagePreflight(mutableRef);
197+
pinAgentSandboxBaseImageRef.mockReturnValue(rebuildRef);
179198

180-
const result = ensureRebuildAgentBaseImage("hermes", makeBail());
199+
expect(() => ensureRebuildAgentBaseImage("hermes", makeBail())).toThrow(
200+
"could not be bound to its rebuild handoff",
201+
);
181202

182203
expect(ensureAgentBaseImage).toHaveBeenCalledWith(expect.objectContaining({ name: "hermes" }), {
183204
forceBaseImageRebuild: false,
@@ -186,17 +207,10 @@ describe("rebuild agent base image preflight", () => {
186207
forceLocal: true,
187208
temporary: true,
188209
});
189-
expect(bindLocalAgentBaseImageToPinnedProvenance).toHaveBeenCalledWith(
190-
expect.objectContaining({ name: "hermes" }),
191-
immutableRef,
192-
);
193-
expect(result).toEqual({
194-
ok: true,
195-
imageRef: immutableRef,
196-
overrideEnvVar,
197-
disposeImageRef: expect.any(Function),
210+
expect(dockerRmi).toHaveBeenCalledWith(rebuildRef, {
211+
ignoreError: true,
212+
suppressOutput: true,
198213
});
199-
expect(disposeRebuildAgentBaseImagePreflight(result)).toBe(true);
200214
});
201215

202216
it("proves a caller alias before resolving it as the pinned remote image (#7144)", () => {
@@ -252,6 +266,158 @@ describe("rebuild agent base image preflight", () => {
252266
});
253267
});
254268

269+
it("leases a temporary local handoff only from the stable outer resolution", () => {
270+
const inputHashRef = "nemoclaw-hermes-sandbox-base-local:3ef2ca87";
271+
const rebuildRef = `nemoclaw-hermes-sandbox-base-local:rebuild-343338-${"c".repeat(16)}-image-${"d".repeat(64)}`;
272+
const provenance = `${"e".repeat(64)}.${"f".repeat(64)}`;
273+
const resolutionMetadata = {
274+
ref: inputHashRef,
275+
digest: null,
276+
source: "local",
277+
imageId: `sha256:${"d".repeat(64)}`,
278+
} as SandboxBaseImageResolutionMetadata;
279+
const mocks = mockBaseImagePreflight(inputHashRef);
280+
const {
281+
bindLocalAgentBaseImageHandoffToResolution,
282+
ensureAgentBaseImage,
283+
pinAgentSandboxBaseImageRef,
284+
} = mocks;
285+
pinAgentSandboxBaseImageRef.mockReturnValue(rebuildRef);
286+
ensureAgentBaseImage.mockReturnValue({
287+
imageTag: inputHashRef,
288+
built: false,
289+
resolutionMetadata,
290+
reusedResolutionHint: resolutionMetadata,
291+
});
292+
bindLocalAgentBaseImageHandoffToResolution.mockReturnValue({
293+
ref: rebuildRef,
294+
provenance,
295+
});
296+
297+
try {
298+
const result = ensureRebuildAgentBaseImage("hermes", makeBail(), {
299+
resolutionHint: resolutionMetadata,
300+
});
301+
302+
expect(bindLocalAgentBaseImageHandoffToResolution).toHaveBeenCalledWith(
303+
expect.objectContaining({ name: "hermes" }),
304+
inputHashRef,
305+
rebuildRef,
306+
resolutionMetadata,
307+
resolutionMetadata,
308+
);
309+
expect(result).toMatchObject({
310+
imageRef: rebuildRef,
311+
resolutionMetadata,
312+
trustedLocalOverride: { ref: rebuildRef, provenance },
313+
});
314+
} finally {
315+
for (const mock of Object.values(mocks)) mock.mockRestore();
316+
}
317+
});
318+
319+
it("force-rebuilds instead of trusting fresh fallback metadata after a local tag moved", () => {
320+
const sourceRef = "nemoclaw-hermes-sandbox-base-local:3ef2ca87";
321+
const imageId = `sha256:${"d".repeat(64)}`;
322+
const canonicalRef = `nemoclaw-hermes-sandbox-base-local:image-${"d".repeat(64)}`;
323+
const persistedHint = {
324+
ref: sourceRef,
325+
digest: null,
326+
source: "local",
327+
imageId: `sha256:${"a".repeat(64)}`,
328+
} as SandboxBaseImageResolutionMetadata;
329+
const freshFallbackMetadata = {
330+
...persistedHint,
331+
imageId,
332+
};
333+
const forcedMetadata = {
334+
...freshFallbackMetadata,
335+
ref: canonicalRef,
336+
};
337+
const forcedTrust = {
338+
ref: canonicalRef,
339+
provenance: `${"e".repeat(64)}.${"f".repeat(64)}`,
340+
};
341+
const {
342+
bindLocalAgentBaseImageHandoffToResolution,
343+
ensureAgentBaseImage,
344+
pinAgentSandboxBaseImageRef,
345+
} = mockBaseImagePreflight(sourceRef);
346+
ensureAgentBaseImage
347+
.mockReturnValueOnce({
348+
imageTag: sourceRef,
349+
built: false,
350+
resolutionMetadata: freshFallbackMetadata,
351+
})
352+
.mockReturnValueOnce({
353+
imageTag: canonicalRef,
354+
built: true,
355+
resolutionMetadata: forcedMetadata,
356+
trustedLocalOverride: forcedTrust,
357+
});
358+
pinAgentSandboxBaseImageRef.mockReturnValue(canonicalRef);
359+
360+
const result = ensureRebuildAgentBaseImage("hermes", makeBail(), {
361+
resolutionHint: persistedHint,
362+
});
363+
364+
expect(bindLocalAgentBaseImageHandoffToResolution).not.toHaveBeenCalled();
365+
expect(ensureAgentBaseImage).toHaveBeenCalledTimes(2);
366+
expect(ensureAgentBaseImage).toHaveBeenNthCalledWith(2, expect.anything(), {
367+
forceBaseImageRebuild: true,
368+
});
369+
expect(result).toMatchObject({
370+
imageRef: canonicalRef,
371+
resolutionMetadata: forcedMetadata,
372+
trustedLocalOverride: forcedTrust,
373+
});
374+
});
375+
376+
it("reuses the canonical forced-build metadata on the next offline rebuild", () => {
377+
const canonicalRef = `nemoclaw-hermes-sandbox-base-local:image-${"d".repeat(64)}`;
378+
const resolutionMetadata = {
379+
ref: canonicalRef,
380+
digest: null,
381+
source: "local",
382+
imageId: `sha256:${"d".repeat(64)}`,
383+
} as SandboxBaseImageResolutionMetadata;
384+
const provenance = `${"e".repeat(64)}.${"f".repeat(64)}`;
385+
const {
386+
bindLocalAgentBaseImageHandoffToResolution,
387+
ensureAgentBaseImage,
388+
pinAgentSandboxBaseImageRef,
389+
} = mockBaseImagePreflight(canonicalRef);
390+
ensureAgentBaseImage.mockReturnValue({
391+
imageTag: canonicalRef,
392+
built: false,
393+
resolutionMetadata,
394+
reusedResolutionHint: resolutionMetadata,
395+
});
396+
pinAgentSandboxBaseImageRef.mockReturnValue(canonicalRef);
397+
bindLocalAgentBaseImageHandoffToResolution.mockReturnValue({
398+
ref: canonicalRef,
399+
provenance,
400+
});
401+
402+
const result = ensureRebuildAgentBaseImage("hermes", makeBail(), {
403+
resolutionHint: resolutionMetadata,
404+
});
405+
406+
expect(ensureAgentBaseImage).toHaveBeenCalledOnce();
407+
expect(ensureAgentBaseImage).toHaveBeenCalledWith(expect.anything(), {
408+
forceBaseImageRebuild: false,
409+
resolutionHint: resolutionMetadata,
410+
});
411+
expect(bindLocalAgentBaseImageHandoffToResolution).toHaveBeenCalledWith(
412+
expect.anything(),
413+
canonicalRef,
414+
canonicalRef,
415+
resolutionMetadata,
416+
resolutionMetadata,
417+
);
418+
expect(result.trustedLocalOverride).toEqual({ ref: canonicalRef, provenance });
419+
});
420+
255421
it("disposes a temporary recreate handoff at most once (#7144)", () => {
256422
const disposeImageRef = vi.fn(() => true);
257423
const preflight = {

0 commit comments

Comments
 (0)