From 03cf3eab09fee8f1c783abac4c185590d8466a7e Mon Sep 17 00:00:00 2001 From: San Dang Date: Thu, 25 Jun 2026 11:51:39 +0700 Subject: [PATCH 1/2] fix(onboard): detach terminal agent create stream on ready Signed-off-by: San Dang --- agents/langchain-deepagents-code/start.sh | 3 ++- src/lib/onboard.ts | 1 + test/dcode-start-keepalive.test.ts | 1 + test/langchain-deepagents-code-image.test.ts | 8 ++++++++ test/onboard-terminal-dashboard.test.ts | 13 +++++++++++-- 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/agents/langchain-deepagents-code/start.sh b/agents/langchain-deepagents-code/start.sh index 822501e0322..081f702bb1b 100755 --- a/agents/langchain-deepagents-code/start.sh +++ b/agents/langchain-deepagents-code/start.sh @@ -129,7 +129,8 @@ prepare_runtime_env # phase, which breaks the Docker GPU-patch supervisor reconnect and leaves GPU # posture unreliable (#5717). Idle forever instead so the sandbox stays Ready. if [ "$#" -eq 0 ]; then - exec sleep infinity + printf '%s\n' 'Setting up NemoClaw Deep Agents Code runtime...' + exec tail -f /dev/null fi exec "$@" diff --git a/src/lib/onboard.ts b/src/lib/onboard.ts index d8be5f44d2b..29b4dca4caa 100644 --- a/src/lib/onboard.ts +++ b/src/lib/onboard.ts @@ -3064,6 +3064,7 @@ async function createSandbox( dockerGpuCreatePatch.maybeApplyDuringCreate(); return false; }, + readyCheckOutputPatterns: agentDefs.isTerminalAgent(agent) ? [] : undefined, failureCheck: dockerGpuCreatePatch.createFailureMessage, traceEvent: onboardTracing.addTraceEvent, }); diff --git a/test/dcode-start-keepalive.test.ts b/test/dcode-start-keepalive.test.ts index f6bc3333b55..ee2125852c0 100644 --- a/test/dcode-start-keepalive.test.ts +++ b/test/dcode-start-keepalive.test.ts @@ -44,6 +44,7 @@ describe("Deep Agents Code sandbox entrypoint keep-alive (#5717)", () => { // A self-exiting entrypoint would return status 0 with no signal. expect(result.signal).toBe("SIGTERM"); expect(result.status).toBeNull(); + expect(result.stdout).toContain("Setting up NemoClaw Deep Agents Code runtime..."); }); it("execs an explicitly supplied command instead of idling", () => { diff --git a/test/langchain-deepagents-code-image.test.ts b/test/langchain-deepagents-code-image.test.ts index d1d1184c6b8..6307426183c 100644 --- a/test/langchain-deepagents-code-image.test.ts +++ b/test/langchain-deepagents-code-image.test.ts @@ -59,6 +59,14 @@ describe("LangChain Deep Agents Code image contracts", () => { ); }); + it("prints NemoClaw setup output before idling as a terminal runtime", () => { + const startScript = readAgentFile("start.sh"); + + expect(startScript).toContain("Setting up NemoClaw Deep Agents Code runtime"); + expect(startScript).toContain("exec tail -f /dev/null"); + expect(startScript).not.toContain("exec sleep infinity"); + }); + it("does not serialize provider or optional service secrets into the shell env file", () => { const startScript = readAgentFile("start.sh"); diff --git a/test/onboard-terminal-dashboard.test.ts b/test/onboard-terminal-dashboard.test.ts index 9043e812a24..9ef3090d6ae 100644 --- a/test/onboard-terminal-dashboard.test.ts +++ b/test/onboard-terminal-dashboard.test.ts @@ -18,7 +18,11 @@ function writeExecutable(target: string, contents: string) { } function parseStdoutJson(stdout: string): T { - const line = stdout.trim().split("\n").pop(); + const line = stdout + .trim() + .split("\n") + .reverse() + .find((candidate) => candidate.startsWith("{") && candidate.endsWith("}")); assert.ok(line, `expected JSON payload in stdout:\n${stdout}`); return JSON.parse(line); } @@ -55,6 +59,7 @@ const sandboxName = "deepagents-box"; const commands = []; const registerCalls = []; const updateCalls = []; +const keepAlive = setInterval(() => {}, 1000); const _n = (c) => (Array.isArray(c) ? c.join(" ") : String(c)).replace(/'/g, ""); agentOnboard.createAgentSandbox = () => { @@ -104,11 +109,11 @@ childProcess.spawn = (...args) => { child.stdout = new EventEmitter(); child.stderr = new EventEmitter(); child.unref = () => {}; + child.kill = () => true; child.pid = 4242; commands.push({ command: _n([args[0], ...(Array.isArray(args[1]) ? args[1] : [])]), env: args[2]?.env || null }); process.nextTick(() => { child.stdout.emit("data", Buffer.from("Created sandbox: " + sandboxName + "\n")); - child.emit("close", 0); }); return child; }; @@ -122,7 +127,9 @@ const agent = agentDefs.loadAgent("langchain-deepagents-code"); process.env.NEMOCLAW_DASHBOARD_PORT = "19000"; const resultName = await createSandbox(null, "gpt-5.4", "nvidia-prod", null, sandboxName, null, null, null, agent); console.log(JSON.stringify({ resultName, commands, registerCalls, updateCalls })); + clearInterval(keepAlive); })().catch((error) => { + clearInterval(keepAlive); console.error(error); process.exit(1); }); @@ -137,7 +144,9 @@ const agent = agentDefs.loadAgent("langchain-deepagents-code"); HOME: tmpDir, PATH: `${fakeBin}:${process.env.PATH || ""}`, NEMOCLAW_NON_INTERACTIVE: "1", + OPENSHELL_DRIVERS: scenario === "create" ? "vm" : "docker", }, + timeout: 15000, }); assert.equal(result.status, 0, result.stderr); return parseStdoutJson<{ From 31647f869ce996a5e93655389cc5557252f37cbf Mon Sep 17 00:00:00 2001 From: San Dang Date: Thu, 25 Jun 2026 12:23:49 +0700 Subject: [PATCH 2/2] chore(onboard): keep entrypoint growth neutral Signed-off-by: San Dang --- src/lib/onboard.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/onboard.ts b/src/lib/onboard.ts index 29b4dca4caa..7ae91212b05 100644 --- a/src/lib/onboard.ts +++ b/src/lib/onboard.ts @@ -3068,7 +3068,6 @@ async function createSandbox( failureCheck: dockerGpuCreatePatch.createFailureMessage, traceEvent: onboardTracing.addTraceEvent, }); - if (initialSandboxPolicy.cleanup && initialSandboxPolicy.cleanup()) { process.removeListener("exit", initialSandboxPolicy.cleanup); }