From 6d9fdd17da0a3ce2eb14232960fefcdd427a2f74 Mon Sep 17 00:00:00 2001 From: Tinson Lai Date: Wed, 15 Jul 2026 12:07:54 +0000 Subject: [PATCH 1/4] refactor(inference): migrate Bedrock runtime adapter launcher to .mts Signed-off-by: Tinson Lai --- ...-adapter.js => bedrock-runtime-adapter.mts} | 4 +--- .../inference/bedrock-runtime-adapter.test.ts | 18 ++++++++++++++++++ src/lib/inference/bedrock-runtime-adapter.ts | 10 +++++++--- ...edrock-runtime-compatible-anthropic.test.ts | 2 +- 4 files changed, 27 insertions(+), 7 deletions(-) rename scripts/{bedrock-runtime-adapter.js => bedrock-runtime-adapter.mts} (74%) diff --git a/scripts/bedrock-runtime-adapter.js b/scripts/bedrock-runtime-adapter.mts similarity index 74% rename from scripts/bedrock-runtime-adapter.js rename to scripts/bedrock-runtime-adapter.mts index 1a024291deb..2e3de9e9f7f 100755 --- a/scripts/bedrock-runtime-adapter.js +++ b/scripts/bedrock-runtime-adapter.mts @@ -2,9 +2,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -const { - startBedrockRuntimeAdapterFromEnv, -} = require("../dist/lib/inference/bedrock-runtime-adapter"); +import { startBedrockRuntimeAdapterFromEnv } from "../dist/lib/inference/bedrock-runtime-adapter.js"; try { startBedrockRuntimeAdapterFromEnv(); diff --git a/src/lib/inference/bedrock-runtime-adapter.test.ts b/src/lib/inference/bedrock-runtime-adapter.test.ts index f4dbe1587a6..237fdb4eae3 100644 --- a/src/lib/inference/bedrock-runtime-adapter.test.ts +++ b/src/lib/inference/bedrock-runtime-adapter.test.ts @@ -12,6 +12,7 @@ import { createOpenAiChatCompletion, streamOpenAiChatCompletion, } from "./bedrock-runtime-adapter"; +import { isLocalAdapterProcess } from "./local-adapter-lifecycle"; const servers: http.Server[] = []; @@ -382,6 +383,23 @@ describe("Bedrock Runtime OpenAI adapter", () => { expect(body.error.message).toContain("Could not load credentials"); }); + it("spawns the typed .mts launcher entrypoint", () => { + expect(__test.getAdapterScriptPath().endsWith("bedrock-runtime-adapter.mts")).toBe(true); + }); + + it("recognizes adapter processes launched from the old and new launcher filenames", () => { + const needle = __test.adapterProcessNeedle; + expect( + isLocalAdapterProcess(4321, needle, () => "node /opt/nemoclaw/scripts/bedrock-runtime-adapter.mts"), + ).toBe(true); + expect( + isLocalAdapterProcess(4321, needle, () => "node /opt/nemoclaw/scripts/bedrock-runtime-adapter.js"), + ).toBe(true); + expect( + isLocalAdapterProcess(4321, needle, () => "node /opt/nemoclaw/scripts/openrouter-runtime-adapter-entry.js"), + ).toBe(false); + }); + it("includes forwarded AWS environment in the adapter reuse hash", () => { const savedContainerCredentials = process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI; const savedSharedCredentials = process.env.AWS_SHARED_CREDENTIALS_FILE; diff --git a/src/lib/inference/bedrock-runtime-adapter.ts b/src/lib/inference/bedrock-runtime-adapter.ts index 83b8b91993b..e99adb241fc 100644 --- a/src/lib/inference/bedrock-runtime-adapter.ts +++ b/src/lib/inference/bedrock-runtime-adapter.ts @@ -316,14 +316,16 @@ function loadPersistedPid(): number | null { return loadLocalAdapterPid(PID_PATH); } +const ADAPTER_PROCESS_NEEDLE = "bedrock-runtime-adapter"; + function isAdapterProcess(pid: number | null | undefined): boolean { - return isLocalAdapterProcess(pid, "bedrock-runtime-adapter.js", runCapture); + return isLocalAdapterProcess(pid, ADAPTER_PROCESS_NEEDLE, runCapture); } function killStaleAdapter(): void { killLocalAdapterPid({ pidPath: PID_PATH, - processNeedle: "bedrock-runtime-adapter.js", + processNeedle: ADAPTER_PROCESS_NEEDLE, run, runCapture, }); @@ -331,7 +333,7 @@ function killStaleAdapter(): void { function getAdapterScriptPath(): string { const scriptsDir = typeof SCRIPTS === "string" ? SCRIPTS : path.join(process.cwd(), "scripts"); - return path.join(scriptsDir, "bedrock-runtime-adapter.js"); + return path.join(scriptsDir, "bedrock-runtime-adapter.mts"); } function copyAwsEnv(extra: Record): void { @@ -478,4 +480,6 @@ export function getCompatibleAnthropicCredentialForBedrock(): string | null { export const __test = { adapterCredentialHash, + adapterProcessNeedle: ADAPTER_PROCESS_NEEDLE, + getAdapterScriptPath, }; diff --git a/test/e2e/live/bedrock-runtime-compatible-anthropic.test.ts b/test/e2e/live/bedrock-runtime-compatible-anthropic.test.ts index 48da2c017aa..3f5a1c14f8d 100644 --- a/test/e2e/live/bedrock-runtime-compatible-anthropic.test.ts +++ b/test/e2e/live/bedrock-runtime-compatible-anthropic.test.ts @@ -582,7 +582,7 @@ function stopBedrockAdapter(home: string): void { } function isBedrockAdapterProcess(pid: number): boolean { - const expectedScript = "bedrock-runtime-adapter.js"; + const expectedScript = "bedrock-runtime-adapter"; try { const cmdline = fs.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\0", " "); if (cmdline.includes(expectedScript)) return true; From 29c7858b1f89fcb5ea21ed0b1e87e8485e788947 Mon Sep 17 00:00:00 2001 From: Tinson Lai Date: Wed, 15 Jul 2026 14:03:08 +0000 Subject: [PATCH 2/4] chore(inference): format bedrock adapter test with biome Signed-off-by: Tinson Lai --- .../inference/bedrock-runtime-adapter.test.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/lib/inference/bedrock-runtime-adapter.test.ts b/src/lib/inference/bedrock-runtime-adapter.test.ts index 237fdb4eae3..ee4c5e5379f 100644 --- a/src/lib/inference/bedrock-runtime-adapter.test.ts +++ b/src/lib/inference/bedrock-runtime-adapter.test.ts @@ -390,13 +390,25 @@ describe("Bedrock Runtime OpenAI adapter", () => { it("recognizes adapter processes launched from the old and new launcher filenames", () => { const needle = __test.adapterProcessNeedle; expect( - isLocalAdapterProcess(4321, needle, () => "node /opt/nemoclaw/scripts/bedrock-runtime-adapter.mts"), + isLocalAdapterProcess( + 4321, + needle, + () => "node /opt/nemoclaw/scripts/bedrock-runtime-adapter.mts", + ), ).toBe(true); expect( - isLocalAdapterProcess(4321, needle, () => "node /opt/nemoclaw/scripts/bedrock-runtime-adapter.js"), + isLocalAdapterProcess( + 4321, + needle, + () => "node /opt/nemoclaw/scripts/bedrock-runtime-adapter.js", + ), ).toBe(true); expect( - isLocalAdapterProcess(4321, needle, () => "node /opt/nemoclaw/scripts/openrouter-runtime-adapter-entry.js"), + isLocalAdapterProcess( + 4321, + needle, + () => "node /opt/nemoclaw/scripts/openrouter-runtime-adapter-entry.js", + ), ).toBe(false); }); From 512045446513d22807d7090a4075981827fad70d Mon Sep 17 00:00:00 2001 From: Tinson Lai Date: Wed, 15 Jul 2026 14:33:39 +0000 Subject: [PATCH 3/4] fix(inference): match exact launcher basenames for bedrock adapter process detection Signed-off-by: Tinson Lai --- src/lib/inference/bedrock-runtime-adapter.test.ts | 7 +++++++ src/lib/inference/bedrock-runtime-adapter.ts | 7 ++++++- src/lib/inference/local-adapter-lifecycle.ts | 12 ++++++++---- .../bedrock-runtime-compatible-anthropic.test.ts | 8 +++++--- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/lib/inference/bedrock-runtime-adapter.test.ts b/src/lib/inference/bedrock-runtime-adapter.test.ts index ee4c5e5379f..38908c684fc 100644 --- a/src/lib/inference/bedrock-runtime-adapter.test.ts +++ b/src/lib/inference/bedrock-runtime-adapter.test.ts @@ -410,6 +410,13 @@ describe("Bedrock Runtime OpenAI adapter", () => { () => "node /opt/nemoclaw/scripts/openrouter-runtime-adapter-entry.js", ), ).toBe(false); + expect( + isLocalAdapterProcess( + 4321, + needle, + () => "node /opt/nemoclaw/scripts/my-bedrock-runtime-adapter.mts", + ), + ).toBe(false); }); it("includes forwarded AWS environment in the adapter reuse hash", () => { diff --git a/src/lib/inference/bedrock-runtime-adapter.ts b/src/lib/inference/bedrock-runtime-adapter.ts index e99adb241fc..36f3691f738 100644 --- a/src/lib/inference/bedrock-runtime-adapter.ts +++ b/src/lib/inference/bedrock-runtime-adapter.ts @@ -316,7 +316,12 @@ function loadPersistedPid(): number | null { return loadLocalAdapterPid(PID_PATH); } -const ADAPTER_PROCESS_NEEDLE = "bedrock-runtime-adapter"; +const ADAPTER_LAUNCHER_BASENAMES = ["bedrock-runtime-adapter.mts", "bedrock-runtime-adapter.js"]; +const ADAPTER_PROCESS_NEEDLE = new RegExp( + `(?:^|[^A-Za-z0-9_.-])(?:${ADAPTER_LAUNCHER_BASENAMES.map((name) => + name.replaceAll(".", "\\."), + ).join("|")})(?:$|[^A-Za-z0-9_.-])`, +); function isAdapterProcess(pid: number | null | undefined): boolean { return isLocalAdapterProcess(pid, ADAPTER_PROCESS_NEEDLE, runCapture); diff --git a/src/lib/inference/local-adapter-lifecycle.ts b/src/lib/inference/local-adapter-lifecycle.ts index 5c028dc1d0d..bf697277464 100644 --- a/src/lib/inference/local-adapter-lifecycle.ts +++ b/src/lib/inference/local-adapter-lifecycle.ts @@ -105,17 +105,21 @@ export function loadLocalAdapterPid(filePath: string): number | null { export function isLocalAdapterProcess( pid: number | null | undefined, - processNeedle: string, + processNeedle: string | RegExp, runCapture: RunCaptureFn, ): boolean { if (!Number.isInteger(pid) || !pid || pid <= 0) return false; - const cmdline = runCapture(["ps", "-p", String(pid), "-o", "args="], { ignoreError: true }); - return Boolean(String(cmdline || "").includes(processNeedle)); + const cmdline = String( + runCapture(["ps", "-p", String(pid), "-o", "args="], { ignoreError: true }) || "", + ); + return typeof processNeedle === "string" + ? cmdline.includes(processNeedle) + : processNeedle.test(cmdline); } export function killLocalAdapterPid(options: { pidPath: string; - processNeedle: string; + processNeedle: string | RegExp; run: RunFn; runCapture: RunCaptureFn; }): void { diff --git a/test/e2e/live/bedrock-runtime-compatible-anthropic.test.ts b/test/e2e/live/bedrock-runtime-compatible-anthropic.test.ts index 3f5a1c14f8d..b725d3df328 100644 --- a/test/e2e/live/bedrock-runtime-compatible-anthropic.test.ts +++ b/test/e2e/live/bedrock-runtime-compatible-anthropic.test.ts @@ -581,11 +581,13 @@ function stopBedrockAdapter(home: string): void { } } +const BEDROCK_ADAPTER_LAUNCHER_PATTERN = + /(?:^|[^A-Za-z0-9_.-])bedrock-runtime-adapter\.(?:mts|js)(?:$|[^A-Za-z0-9_.-])/; + function isBedrockAdapterProcess(pid: number): boolean { - const expectedScript = "bedrock-runtime-adapter"; try { const cmdline = fs.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\0", " "); - if (cmdline.includes(expectedScript)) return true; + if (BEDROCK_ADAPTER_LAUNCHER_PATTERN.test(cmdline)) return true; } catch { // Fall back to ps on platforms without procfs. } @@ -594,7 +596,7 @@ function isBedrockAdapterProcess(pid: number): boolean { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], }); - return ps.status === 0 && ps.stdout.includes(expectedScript); + return ps.status === 0 && BEDROCK_ADAPTER_LAUNCHER_PATTERN.test(ps.stdout); } async function restoreHostsFile( From bd39bfef8616f55320b8afe3bc987956d8d31877 Mon Sep 17 00:00:00 2001 From: Tinson Lai Date: Wed, 15 Jul 2026 17:41:15 +0000 Subject: [PATCH 4/4] fix(ci): shard bedrock E2E signal by matrix agent Signed-off-by: Tinson Lai --- .github/workflows/e2e.yaml | 1 + tools/e2e/workflow-boundary.mts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index bd556b4e7e0..b4f92af1c00 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -4321,6 +4321,7 @@ jobs: E2E_ARTIFACT_DIR: ${{ github.workspace }}/e2e-artifacts/live/bedrock-runtime-compatible-anthropic/${{ matrix.agent }} NEMOCLAW_CLI_BIN: ${{ github.workspace }}/bin/nemoclaw.js NEMOCLAW_RUN_LIVE_E2E: "1" + NEMOCLAW_E2E_SHARD: ${{ matrix.agent }} NEMOCLAW_NON_INTERACTIVE: "1" NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" NEMOCLAW_RECREATE_SANDBOX: "1" diff --git a/tools/e2e/workflow-boundary.mts b/tools/e2e/workflow-boundary.mts index b6d571de1b8..20b50ff16d5 100644 --- a/tools/e2e/workflow-boundary.mts +++ b/tools/e2e/workflow-boundary.mts @@ -3453,6 +3453,11 @@ function validateBedrockRuntimeCompatibleAnthropicJob( if (jobEnv.NEMOCLAW_RUN_LIVE_E2E !== "1") { errors.push("bedrock-runtime-compatible-anthropic job must set NEMOCLAW_RUN_LIVE_E2E=1"); } + if (jobEnv.NEMOCLAW_E2E_SHARD !== "${{ matrix.agent }}") { + errors.push( + "bedrock-runtime-compatible-anthropic job must derive NEMOCLAW_E2E_SHARD from matrix.agent", + ); + } if (jobEnv.NEMOCLAW_NON_INTERACTIVE !== "1") { errors.push("bedrock-runtime-compatible-anthropic job must set NEMOCLAW_NON_INTERACTIVE=1"); }