From 18409cd5ba40f07e0fb6a54765dcbde5fbe8d77c Mon Sep 17 00:00:00 2001 From: Tinson Lai Date: Sun, 19 Jul 2026 07:21:45 +0000 Subject: [PATCH 1/7] feat(onboard): surface back-navigation hint on inference setup prompts Signed-off-by: Tinson Lai --- src/lib/navigation.ts | 6 +++ src/lib/onboard/credential-navigation.test.ts | 9 +++++ src/lib/onboard/credential-navigation.ts | 11 ++++- src/lib/onboard/hermes-auth.test.ts | 40 +++++++++++++++++++ src/lib/onboard/hermes-auth.ts | 3 ++ src/lib/onboard/prompt-helpers.test.ts | 28 ++++++++++++- src/lib/onboard/prompt-helpers.ts | 2 +- 7 files changed, 95 insertions(+), 4 deletions(-) diff --git a/src/lib/navigation.ts b/src/lib/navigation.ts index 2f8a4d8cf87..52a067480d2 100644 --- a/src/lib/navigation.ts +++ b/src/lib/navigation.ts @@ -7,3 +7,9 @@ export type BackToSelection = typeof BACK_TO_SELECTION; export function isBackToSelection(value: unknown): value is BackToSelection { return value === BACK_TO_SELECTION; } + +export const NAVIGATION_HINT = " Enter b to go back, or exit to quit."; + +export function printNavigationHint(log: (message?: string) => void = console.log): void { + log(NAVIGATION_HINT); +} diff --git a/src/lib/onboard/credential-navigation.test.ts b/src/lib/onboard/credential-navigation.test.ts index add39e38615..b184e8592b8 100644 --- a/src/lib/onboard/credential-navigation.test.ts +++ b/src/lib/onboard/credential-navigation.test.ts @@ -5,11 +5,20 @@ import { describe, expect, it, vi } from "vitest"; import { BACK_TO_SELECTION, + getNavigationChoice, returningToProviderSelection, shouldReturnToProviderSelection, } from "./credential-navigation"; describe("credential prompt navigation helpers", () => { + it("accepts the short b token as back so the advertised key works", () => { + expect(getNavigationChoice("b")).toBe("back"); + expect(getNavigationChoice(" B ")).toBe("back"); + expect(getNavigationChoice("back")).toBe("back"); + expect(getNavigationChoice("exit")).toBe("exit"); + expect(getNavigationChoice("nvapi-xxxx")).toBeNull(); + }); + it("treats both the shared back sentinel and credential back intents as provider-selection navigation", () => { const exitOnboard = vi.fn(() => { throw new Error("unexpected exit"); diff --git a/src/lib/onboard/credential-navigation.ts b/src/lib/onboard/credential-navigation.ts index a41810abe3b..89a27f9f27c 100644 --- a/src/lib/onboard/credential-navigation.ts +++ b/src/lib/onboard/credential-navigation.ts @@ -2,7 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import * as credentials from "../credentials/store"; -import { BACK_TO_SELECTION, type BackToSelection, isBackToSelection } from "../navigation"; +import { + BACK_TO_SELECTION, + type BackToSelection, + isBackToSelection, + printNavigationHint, +} from "../navigation"; export type BackNavigationResult = BackToSelection | { kind: "back" }; export type { BackToSelection }; @@ -12,7 +17,7 @@ export function getNavigationChoice(value = ""): "back" | "exit" | null { const normalized = String(value || "") .trim() .toLowerCase(); - if (normalized === "back") return "back"; + if (normalized === "back" || normalized === "b") return "back"; if (normalized === "exit" || normalized === "quit") return "exit"; return null; } @@ -81,6 +86,8 @@ export async function replaceNamedCredential({ console.log(""); } + printNavigationHint(); + while (true) { const key = await readCredentialValue(` ${label}: `, exitOnboardFromPrompt); if (isBackToSelection(key)) return key; diff --git a/src/lib/onboard/hermes-auth.test.ts b/src/lib/onboard/hermes-auth.test.ts index 2676923446f..d50c95d0602 100644 --- a/src/lib/onboard/hermes-auth.test.ts +++ b/src/lib/onboard/hermes-auth.test.ts @@ -3,6 +3,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; +import { NAVIGATION_HINT } from "../navigation"; import { createHermesAuthHelpers, HERMES_AUTH_METHOD_API_KEY, @@ -46,6 +47,45 @@ afterEach(() => { vi.unstubAllEnvs(); }); +describe("Hermes auth back-navigation affordance", () => { + it("prints the navigation hint before the interactive auth-method prompt", async () => { + clearHermesAuthEnvironment(); + const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined); + const deps = createDeps({ + isNonInteractive: vi.fn(() => false), + prompt: vi.fn(async () => "1"), + }); + + await createHermesAuthHelpers(deps).promptHermesAuthMethod(); + + expect(logSpy.mock.calls.some((call) => call[0] === NAVIGATION_HINT)).toBe(true); + }); + + it("returns to provider selection when the auth-method prompt replies back", async () => { + clearHermesAuthEnvironment(); + vi.spyOn(console, "log").mockImplementation(() => undefined); + const deps = createDeps({ + isNonInteractive: vi.fn(() => false), + prompt: vi.fn(async () => "b"), + getNavigationChoice: vi.fn((): "back" => "back"), + }); + + const result = await createHermesAuthHelpers(deps).promptHermesAuthMethod(); + + expect(result).toBe(deps.backToSelection); + }); + + it("does not print the navigation hint in non-interactive mode", async () => { + clearHermesAuthEnvironment(); + const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined); + const deps = createDeps({ isNonInteractive: vi.fn(() => true) }); + + await createHermesAuthHelpers(deps).promptHermesAuthMethod(); + + expect(logSpy.mock.calls.some((call) => call[0] === NAVIGATION_HINT)).toBe(false); + }); +}); + describe("Hermes authentication exit boundaries", () => { it("uses the injected exit for an unsupported requested auth method", async () => { vi.stubEnv("NEMOCLAW_HERMES_AUTH_METHOD", "certificate"); diff --git a/src/lib/onboard/hermes-auth.ts b/src/lib/onboard/hermes-auth.ts index c804c73b731..05da929c2ab 100644 --- a/src/lib/onboard/hermes-auth.ts +++ b/src/lib/onboard/hermes-auth.ts @@ -4,6 +4,7 @@ import { normalizeCredentialValue } from "../credentials/store"; import type { HermesAuthMethod } from "../hermes-provider-auth"; import * as hermesProviderAuth from "../hermes-provider-auth"; +import { printNavigationHint } from "../navigation"; export type { HermesAuthMethod }; @@ -126,6 +127,7 @@ export function createHermesAuthHelpers(deps: HermesAuthFlowDeps): HermesAuthHel const defaultIdx = (requested ? methods.findIndex((method) => method.key === requested) : 0) + 1; + printNavigationHint(); const choice = await deps.prompt(` Choose [${defaultIdx}]: `); const navigation = deps.getNavigationChoice(choice); if (navigation === "back") return deps.backToSelection; @@ -159,6 +161,7 @@ export function createHermesAuthHelpers(deps: HermesAuthFlowDeps): HermesAuthHel console.log(""); console.log(" Hermes Provider Nous API Key"); console.log(` Create or copy a key from ${HERMES_NOUS_API_KEY_HELP_URL}`); + printNavigationHint(); const rawKey = await deps.prompt(" Nous API Key: ", { secret: true, }); diff --git a/src/lib/onboard/prompt-helpers.test.ts b/src/lib/onboard/prompt-helpers.test.ts index 77efb36c472..21d8e0f96ce 100644 --- a/src/lib/onboard/prompt-helpers.test.ts +++ b/src/lib/onboard/prompt-helpers.test.ts @@ -3,7 +3,11 @@ import { describe, expect, it, vi } from "vitest"; // Import source directly so tests cannot pass against a stale build. -import { promptOrDefault, selectFromNumberedMenuOrExit } from "./prompt-helpers"; +import { + getNavigationChoice, + promptOrDefault, + selectFromNumberedMenuOrExit, +} from "./prompt-helpers"; function makeDeps(promptReply: string) { return { @@ -13,6 +17,28 @@ function makeDeps(promptReply: string) { }; } +describe("getNavigationChoice back token", () => { + it("treats the short b token as back so the advertised key works", () => { + expect(getNavigationChoice("b")).toBe("back"); + expect(getNavigationChoice(" B ")).toBe("back"); + }); + + it("still treats the full back word as back", () => { + expect(getNavigationChoice("back")).toBe("back"); + }); + + it("keeps exit and quit mapped to exit", () => { + expect(getNavigationChoice("exit")).toBe("exit"); + expect(getNavigationChoice("quit")).toBe("exit"); + }); + + it("returns null for ordinary values", () => { + expect(getNavigationChoice("brave")).toBeNull(); + expect(getNavigationChoice("2")).toBeNull(); + expect(getNavigationChoice("")).toBeNull(); + }); +}); + describe("promptOrDefault interactive default fallback (#4387)", () => { it("returns defaultValue when the user just presses Enter (empty reply)", async () => { const deps = makeDeps(""); diff --git a/src/lib/onboard/prompt-helpers.ts b/src/lib/onboard/prompt-helpers.ts index 252a7eb864e..d8756e0d110 100644 --- a/src/lib/onboard/prompt-helpers.ts +++ b/src/lib/onboard/prompt-helpers.ts @@ -11,7 +11,7 @@ export function getNavigationChoice(value = ""): "back" | "exit" | null { const normalized = String(value || "") .trim() .toLowerCase(); - if (normalized === "back") return "back"; + if (normalized === "back" || normalized === "b") return "back"; if (normalized === "exit" || normalized === "quit") return "exit"; return null; } From 0067b96b4a7f38c8ecf26706ad4a42c13dea9448 Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Sun, 19 Jul 2026 00:56:41 -0700 Subject: [PATCH 2/7] test(onboard): harden back-navigation coverage Co-authored-by: Tinson Lai Signed-off-by: Apurv Kumaria --- src/lib/onboard/credential-navigation.test.ts | 2 +- src/lib/onboard/hermes-auth.test.ts | 17 ++++++++++++----- src/lib/onboard/prompt-helpers.test.ts | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/lib/onboard/credential-navigation.test.ts b/src/lib/onboard/credential-navigation.test.ts index b184e8592b8..e3719690cd6 100644 --- a/src/lib/onboard/credential-navigation.test.ts +++ b/src/lib/onboard/credential-navigation.test.ts @@ -10,7 +10,7 @@ import { shouldReturnToProviderSelection, } from "./credential-navigation"; -describe("credential prompt navigation helpers", () => { +describe("credential prompt navigation helpers (#6005)", () => { it("accepts the short b token as back so the advertised key works", () => { expect(getNavigationChoice("b")).toBe("back"); expect(getNavigationChoice(" B ")).toBe("back"); diff --git a/src/lib/onboard/hermes-auth.test.ts b/src/lib/onboard/hermes-auth.test.ts index d50c95d0602..612db1dcd29 100644 --- a/src/lib/onboard/hermes-auth.test.ts +++ b/src/lib/onboard/hermes-auth.test.ts @@ -11,6 +11,7 @@ import { HERMES_NOUS_API_KEY_CREDENTIAL_ENV, type HermesAuthFlowDeps, } from "./hermes-auth"; +import { getNavigationChoice as getPromptNavigationChoice } from "./prompt-helpers"; function clearHermesAuthEnvironment(): void { vi.stubEnv("NEMOCLAW_HERMES_AUTH_METHOD", undefined); @@ -47,18 +48,24 @@ afterEach(() => { vi.unstubAllEnvs(); }); -describe("Hermes auth back-navigation affordance", () => { +describe("Hermes auth back-navigation affordance (#6005)", () => { it("prints the navigation hint before the interactive auth-method prompt", async () => { clearHermesAuthEnvironment(); - const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined); + const events: string[] = []; + vi.spyOn(console, "log").mockImplementation((message?: unknown) => { + if (message === NAVIGATION_HINT) events.push("hint"); + }); const deps = createDeps({ isNonInteractive: vi.fn(() => false), - prompt: vi.fn(async () => "1"), + prompt: vi.fn(async () => { + events.push("prompt"); + return "1"; + }), }); await createHermesAuthHelpers(deps).promptHermesAuthMethod(); - expect(logSpy.mock.calls.some((call) => call[0] === NAVIGATION_HINT)).toBe(true); + expect(events).toEqual(["hint", "prompt"]); }); it("returns to provider selection when the auth-method prompt replies back", async () => { @@ -67,7 +74,7 @@ describe("Hermes auth back-navigation affordance", () => { const deps = createDeps({ isNonInteractive: vi.fn(() => false), prompt: vi.fn(async () => "b"), - getNavigationChoice: vi.fn((): "back" => "back"), + getNavigationChoice: getPromptNavigationChoice, }); const result = await createHermesAuthHelpers(deps).promptHermesAuthMethod(); diff --git a/src/lib/onboard/prompt-helpers.test.ts b/src/lib/onboard/prompt-helpers.test.ts index 21d8e0f96ce..6f251a82188 100644 --- a/src/lib/onboard/prompt-helpers.test.ts +++ b/src/lib/onboard/prompt-helpers.test.ts @@ -17,7 +17,7 @@ function makeDeps(promptReply: string) { }; } -describe("getNavigationChoice back token", () => { +describe("getNavigationChoice back token (#6005)", () => { it("treats the short b token as back so the advertised key works", () => { expect(getNavigationChoice("b")).toBe("back"); expect(getNavigationChoice(" B ")).toBe("back"); From d3bacdcc6f15b5e8fcdd1c2a77a7e7465c97e078 Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Sun, 19 Jul 2026 01:28:23 -0700 Subject: [PATCH 3/7] test(onboard): keep hint ordering assertion branchless Co-authored-by: Tinson Lai Signed-off-by: Apurv Kumaria --- src/lib/onboard/hermes-auth.test.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib/onboard/hermes-auth.test.ts b/src/lib/onboard/hermes-auth.test.ts index 612db1dcd29..5f7b2f10ccd 100644 --- a/src/lib/onboard/hermes-auth.test.ts +++ b/src/lib/onboard/hermes-auth.test.ts @@ -53,7 +53,7 @@ describe("Hermes auth back-navigation affordance (#6005)", () => { clearHermesAuthEnvironment(); const events: string[] = []; vi.spyOn(console, "log").mockImplementation((message?: unknown) => { - if (message === NAVIGATION_HINT) events.push("hint"); + events.push(String(message)); }); const deps = createDeps({ isNonInteractive: vi.fn(() => false), @@ -65,7 +65,11 @@ describe("Hermes auth back-navigation affordance (#6005)", () => { await createHermesAuthHelpers(deps).promptHermesAuthMethod(); - expect(events).toEqual(["hint", "prompt"]); + const hintIndex = events.indexOf(NAVIGATION_HINT); + const promptIndex = events.indexOf("prompt"); + expect(hintIndex).toBeGreaterThanOrEqual(0); + expect(promptIndex).toBeGreaterThanOrEqual(0); + expect(hintIndex).toBeLessThan(promptIndex); }); it("returns to provider selection when the auth-method prompt replies back", async () => { From 1387686325f880b2f256447b3fc20a8fa5fe6354 Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Sun, 19 Jul 2026 01:34:39 -0700 Subject: [PATCH 4/7] docs(onboard): document hinted back shortcut Co-authored-by: Tinson Lai Signed-off-by: Apurv Kumaria --- docs/get-started/quickstart-hermes.mdx | 1 + docs/get-started/quickstart-langchain-deepagents-code.mdx | 1 + docs/get-started/quickstart.mdx | 1 + 3 files changed, 3 insertions(+) diff --git a/docs/get-started/quickstart-hermes.mdx b/docs/get-started/quickstart-hermes.mdx index c4f1fd83249..3f1f8129a6b 100644 --- a/docs/get-started/quickstart-hermes.mdx +++ b/docs/get-started/quickstart-hermes.mdx @@ -158,6 +158,7 @@ Use these details when your first-run path needs more control. The wizard asks for an inference provider, model, required credential, and sandbox name before it prints the review summary. After confirmation, NemoClaw registers inference, prompts for optional Tavily Search and supported messaging channels, builds and starts the sandbox, sets up Hermes, and applies the selected network policy tier and presets. At any prompt, press Enter to accept the default shown in `[brackets]`, type `back` to return to the previous prompt, or type `exit` to quit. + At prompts that display the navigation hint, type `b` to return to provider selection. The default Hermes sandbox name is `hermes`. Use a distinct name, such as `my-hermes`, when you run Hermes and OpenClaw sandboxes side by side. diff --git a/docs/get-started/quickstart-langchain-deepagents-code.mdx b/docs/get-started/quickstart-langchain-deepagents-code.mdx index c3215595abc..10cf5a51fda 100644 --- a/docs/get-started/quickstart-langchain-deepagents-code.mdx +++ b/docs/get-started/quickstart-langchain-deepagents-code.mdx @@ -97,6 +97,7 @@ nemoclaw onboard --agent langchain The wizard asks for an inference provider, model, required credential, sandbox name, and policy tier before it prints the review summary. At any prompt, press Enter to accept the default shown in `[brackets]`, type `back` to return to the previous prompt, or type `exit` to quit. +At prompts that display the navigation hint, type `b` to return to provider selection. The default Deep Agents sandbox name is `deepagents-code`. Use a distinct name, such as `my-deepagents`, when you run Deep Agents, Hermes, and OpenClaw sandboxes side by side. Refer to [Choose an Inference Provider](../inference/learn-and-choose/choose-inference-provider) for provider-specific prompts. diff --git a/docs/get-started/quickstart.mdx b/docs/get-started/quickstart.mdx index b9982ea8c18..edcc1a24e43 100644 --- a/docs/get-started/quickstart.mdx +++ b/docs/get-started/quickstart.mdx @@ -240,6 +240,7 @@ Use these details when your first-run path needs more control. It prints a review summary before it registers the provider with OpenShell. After confirmation, NemoClaw registers inference, prompts for optional web search and messaging channels, builds and starts the sandbox, sets up OpenClaw, and applies the selected network policy tier and presets. At any prompt, press Enter to accept the default shown in `[brackets]`, type `back` to return to the previous prompt, or type `exit` to quit. + At prompts that display the navigation hint, type `b` to return to provider selection. If registered sandboxes already exist, the installer prepares the current NemoClaw CLI without replacing OpenShell, requires a fresh backup of every registered sandbox before it changes the gateway, and runs `nemoclaw upgrade-sandboxes --auto` after the host upgrade. After backup, it retires the running gateway before replacing OpenShell only when the installed OpenShell version is outside the current release's supported range; an unknown installed version or an invalid or missing range stops the update without retiring the gateway, while any retirement failure stops the update with the sandbox backups preserved. From dfed1028f679809a640a068228d3a3c8a3a8043e Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Sun, 19 Jul 2026 01:45:54 -0700 Subject: [PATCH 5/7] fix(credentials): honor advertised short back token Co-authored-by: Tinson Lai Signed-off-by: Apurv Kumaria --- src/lib/credentials/store.ts | 2 +- src/lib/onboard/credential-navigation.test.ts | 28 ++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/lib/credentials/store.ts b/src/lib/credentials/store.ts index 5f4ee38d1b1..c22f13e0e4b 100644 --- a/src/lib/credentials/store.ts +++ b/src/lib/credentials/store.ts @@ -155,7 +155,7 @@ export function normalizeCredentialValue(value: CredentialInput): string { export function getCredentialPromptIntent(value: CredentialInput): CredentialPromptIntent { const normalized = normalizeCredentialValue(value); const navigation = normalized.toLowerCase(); - if (navigation === "back") return { kind: "back" }; + if (navigation === "b" || navigation === "back") return { kind: "back" }; if (navigation === "exit" || navigation === "quit") return { kind: "exit" }; if (navigation === "?" || navigation === "help") return { kind: "help" }; return { kind: "credential", value: normalized }; diff --git a/src/lib/onboard/credential-navigation.test.ts b/src/lib/onboard/credential-navigation.test.ts index e3719690cd6..90628ddebfe 100644 --- a/src/lib/onboard/credential-navigation.test.ts +++ b/src/lib/onboard/credential-navigation.test.ts @@ -1,15 +1,23 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { describe, expect, it, vi } from "vitest"; +import { afterEach, describe, expect, it, vi } from "vitest"; + +import * as credentials from "../credentials/store"; import { BACK_TO_SELECTION, getNavigationChoice, + replaceNamedCredential, returningToProviderSelection, shouldReturnToProviderSelection, } from "./credential-navigation"; +afterEach(() => { + vi.restoreAllMocks(); + delete process.env.NEMOCLAW_TEST_NAVIGATION_KEY; +}); + describe("credential prompt navigation helpers (#6005)", () => { it("accepts the short b token as back so the advertised key works", () => { expect(getNavigationChoice("b")).toBe("back"); @@ -19,6 +27,24 @@ describe("credential prompt navigation helpers (#6005)", () => { expect(getNavigationChoice("nvapi-xxxx")).toBeNull(); }); + it("routes a trimmed short b token through the real secret prompt without staging it", async () => { + vi.spyOn(credentials, "prompt").mockResolvedValue(" B "); + const saveCredential = vi.spyOn(credentials, "saveCredential"); + const exitOnboard = vi.fn(() => { + throw new Error("unexpected exit"); + }) as unknown as () => never; + + const result = await replaceNamedCredential({ + envName: "NEMOCLAW_TEST_NAVIGATION_KEY", + label: "Test credential", + exitOnboardFromPrompt: exitOnboard, + }); + + expect(result).toBe(BACK_TO_SELECTION); + expect(saveCredential).not.toHaveBeenCalled(); + expect(process.env.NEMOCLAW_TEST_NAVIGATION_KEY).toBeUndefined(); + }); + it("treats both the shared back sentinel and credential back intents as provider-selection navigation", () => { const exitOnboard = vi.fn(() => { throw new Error("unexpected exit"); From b72c765d6a57a36e767ff1ee5f5b18f096b1667a Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Sun, 19 Jul 2026 02:35:14 -0700 Subject: [PATCH 6/7] test(onboard): preserve navigation prompt state Co-authored-by: Tinson Lai Signed-off-by: Apurv Kumaria --- .../quickstart-langchain-deepagents-code.mdx | 2 +- src/lib/onboard/credential-navigation.test.ts | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/get-started/quickstart-langchain-deepagents-code.mdx b/docs/get-started/quickstart-langchain-deepagents-code.mdx index 10cf5a51fda..b31fcdecd4a 100644 --- a/docs/get-started/quickstart-langchain-deepagents-code.mdx +++ b/docs/get-started/quickstart-langchain-deepagents-code.mdx @@ -97,7 +97,7 @@ nemoclaw onboard --agent langchain The wizard asks for an inference provider, model, required credential, sandbox name, and policy tier before it prints the review summary. At any prompt, press Enter to accept the default shown in `[brackets]`, type `back` to return to the previous prompt, or type `exit` to quit. -At prompts that display the navigation hint, type `b` to return to provider selection. +At prompts that display the navigation hint, type `b` to return to the previous prompt. The default Deep Agents sandbox name is `deepagents-code`. Use a distinct name, such as `my-deepagents`, when you run Deep Agents, Hermes, and OpenClaw sandboxes side by side. Refer to [Choose an Inference Provider](../inference/learn-and-choose/choose-inference-provider) for provider-specific prompts. diff --git a/src/lib/onboard/credential-navigation.test.ts b/src/lib/onboard/credential-navigation.test.ts index 90628ddebfe..7ce590eaf6d 100644 --- a/src/lib/onboard/credential-navigation.test.ts +++ b/src/lib/onboard/credential-navigation.test.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { afterEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import * as credentials from "../credentials/store"; @@ -13,9 +13,19 @@ import { shouldReturnToProviderSelection, } from "./credential-navigation"; +let navigationKeyBeforeTest: string | undefined; + +beforeEach(() => { + navigationKeyBeforeTest = process.env.NEMOCLAW_TEST_NAVIGATION_KEY; +}); + afterEach(() => { vi.restoreAllMocks(); - delete process.env.NEMOCLAW_TEST_NAVIGATION_KEY; + if (navigationKeyBeforeTest === undefined) { + delete process.env.NEMOCLAW_TEST_NAVIGATION_KEY; + } else { + process.env.NEMOCLAW_TEST_NAVIGATION_KEY = navigationKeyBeforeTest; + } }); describe("credential prompt navigation helpers (#6005)", () => { @@ -42,7 +52,7 @@ describe("credential prompt navigation helpers (#6005)", () => { expect(result).toBe(BACK_TO_SELECTION); expect(saveCredential).not.toHaveBeenCalled(); - expect(process.env.NEMOCLAW_TEST_NAVIGATION_KEY).toBeUndefined(); + expect(process.env.NEMOCLAW_TEST_NAVIGATION_KEY).toBe(navigationKeyBeforeTest); }); it("treats both the shared back sentinel and credential back intents as provider-selection navigation", () => { From ba733eac7338b10894bc93bb318a2699ab616c76 Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Sun, 19 Jul 2026 02:38:30 -0700 Subject: [PATCH 7/7] test(onboard): restore prompt env without branches Co-authored-by: Tinson Lai Signed-off-by: Apurv Kumaria --- src/lib/onboard/credential-navigation.test.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/lib/onboard/credential-navigation.test.ts b/src/lib/onboard/credential-navigation.test.ts index 7ce590eaf6d..28f62b734c8 100644 --- a/src/lib/onboard/credential-navigation.test.ts +++ b/src/lib/onboard/credential-navigation.test.ts @@ -17,15 +17,12 @@ let navigationKeyBeforeTest: string | undefined; beforeEach(() => { navigationKeyBeforeTest = process.env.NEMOCLAW_TEST_NAVIGATION_KEY; + vi.stubEnv("NEMOCLAW_TEST_NAVIGATION_KEY", navigationKeyBeforeTest); }); afterEach(() => { vi.restoreAllMocks(); - if (navigationKeyBeforeTest === undefined) { - delete process.env.NEMOCLAW_TEST_NAVIGATION_KEY; - } else { - process.env.NEMOCLAW_TEST_NAVIGATION_KEY = navigationKeyBeforeTest; - } + vi.unstubAllEnvs(); }); describe("credential prompt navigation helpers (#6005)", () => {