From 077cb4bdad02fd6c81cde9ab2108a87ef9b04748 Mon Sep 17 00:00:00 2001 From: Deepak Jain Date: Fri, 8 May 2026 13:30:21 -0700 Subject: [PATCH 1/4] fix(onboard): support reasoning compatible endpoints Fixes #3279 Signed-off-by: Deepak Jain --- src/lib/onboard.ts | 49 ++++++++++++++-- test/onboard-selection.test.ts | 100 +++++++++++++++++++++++++++++++++ test/onboard.test.ts | 29 ++++++++++ 3 files changed, 173 insertions(+), 5 deletions(-) diff --git a/src/lib/onboard.ts b/src/lib/onboard.ts index c36d876765..d5af14c19e 100644 --- a/src/lib/onboard.ts +++ b/src/lib/onboard.ts @@ -1492,6 +1492,25 @@ function getNavigationChoice(value = ""): "back" | "exit" | null { return null; } +function normalizeReasoningFlag(value: string | null | undefined): "true" | "false" | null { + const normalized = String(value ?? "") + .trim() + .toLowerCase(); + if (normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "y") { + return "true"; + } + if (normalized === "false" || normalized === "0" || normalized === "no" || normalized === "n") { + return "false"; + } + return null; +} + +async function configureCompatibleEndpointReasoning(): Promise<"true" | "false"> { + const configured = normalizeReasoningFlag(process.env.NEMOCLAW_REASONING); + process.env.NEMOCLAW_REASONING = configured ?? "false"; + return process.env.NEMOCLAW_REASONING as "true" | "false"; +} + function exitOnboardFromPrompt(): never { console.log(" Exiting onboarding."); process.exit(1); @@ -2043,7 +2062,7 @@ print(json.dumps({ "messages": [ {"role": "user", "content": "Reply with exactly: PONG"} ], - "max_tokens": 32, + "max_tokens": 512, })) PYPAYLOAD @@ -2081,7 +2100,19 @@ content = ( .get("content") ) if not isinstance(content, str) or not content.strip(): - print("inference.local response did not contain choices[0].message.content: %s" % json.dumps(data)[:1000], file=sys.stderr) + content = ( + data.get("choices", [{}])[0] + .get("message", {}) + .get("reasoning_content") + ) +if not isinstance(content, str) or not content.strip(): + content = ( + data.get("choices", [{}])[0] + .get("message", {}) + .get("reasoning") + ) +if not isinstance(content, str) or not content.strip(): + print("inference.local response did not contain message content or reasoning text: %s" % json.dumps(data)[:1000], file=sys.stderr) sys.exit(1) print("INFERENCE_SMOKE_OK " + content.strip()[:200]) @@ -2945,10 +2976,12 @@ async function validateCustomOpenAiLikeSelection( helpUrl: string | null = null, ): Promise { const apiKey = getCredential(credentialEnv); + const reasoningEnabled = process.env.NEMOCLAW_REASONING === "true"; const probe = probeOpenAiLikeEndpoint(endpointUrl, model, apiKey, { - requireResponsesToolCalling: true, - skipResponsesProbe: shouldForceCompletionsApi(process.env.NEMOCLAW_PREFERRED_API), - probeStreaming: true, + requireResponsesToolCalling: !reasoningEnabled, + skipResponsesProbe: + reasoningEnabled || shouldForceCompletionsApi(process.env.NEMOCLAW_PREFERRED_API), + probeStreaming: !reasoningEnabled, }); if (probe.ok) { if (probe.note) { @@ -6599,6 +6632,10 @@ async function setupNim( } if (selected.key === "custom") { + const reasoning = await configureCompatibleEndpointReasoning(); + if (reasoning === "true") { + console.log(" Reasoning mode enabled for this compatible endpoint model."); + } const validation = await validateCustomOpenAiLikeSelection( remoteConfig.label, endpointUrl || OPENAI_ENDPOINT_URL, @@ -10425,6 +10462,8 @@ module.exports = { printSandboxCreateRecoveryHints, promptYesNoOrDefault, providerExistsInGateway, + normalizeReasoningFlag, + configureCompatibleEndpointReasoning, parsePolicyPresetEnv, parseSandboxStatus, pruneStaleSandboxEntry, diff --git a/test/onboard-selection.test.ts b/test/onboard-selection.test.ts index 4a206025a9..da0f0d7f41 100644 --- a/test/onboard-selection.test.ts +++ b/test/onboard-selection.test.ts @@ -2060,6 +2060,106 @@ const { setupNim } = require(${onboardPath}); ); }); + it("honors NEMOCLAW_REASONING for custom OpenAI-compatible endpoint models", () => { + const repoRoot = path.join(import.meta.dirname, ".."); + const tmpDir = fs.mkdtempSync( + path.join(os.tmpdir(), "nemoclaw-onboard-custom-openai-reasoning-"), + ); + const fakeBin = path.join(tmpDir, "bin"); + const scriptPath = path.join(tmpDir, "custom-openai-reasoning-check.js"); + const onboardPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "onboard.js")); + const credentialsPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "credentials", "store.js")); + const runnerPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "runner.js")); + + fs.mkdirSync(fakeBin, { recursive: true }); + fs.writeFileSync( + path.join(fakeBin, "curl"), + `#!/usr/bin/env bash +body='{"error":{"message":"bad request"}}' +status="400" +outfile="" +url="" +while [ "$#" -gt 0 ]; do + case "$1" in + -o) outfile="$2"; shift 2 ;; + *) url="$1"; shift ;; + esac +done +if echo "$url" | grep -q '/chat/completions$'; then + body='{"id":"chatcmpl-123","choices":[{"message":{"content":"","reasoning_content":"OK"}}]}' + status="200" +fi +printf '%s' "$body" > "$outfile" +printf '%s' "$status" +`, + { mode: 0o755 }, + ); + + const script = String.raw` +const credentials = require(${credentialsPath}); +const runner = require(${runnerPath}); + +const answers = ["3", "https://proxy.example.com/v1", "reasoning-model"]; +const messages = []; + +credentials.prompt = async (message) => { + messages.push(message); + return answers.shift() || ""; +}; +runner.runCapture = () => ""; + +const { setupNim } = require(${onboardPath}); + +(async () => { + process.env.COMPATIBLE_API_KEY = "proxy-key"; + process.env.NEMOCLAW_REASONING = "yes"; + const originalLog = console.log; + const originalError = console.error; + const lines = []; + console.log = (...args) => lines.push(args.join(" ")); + console.error = (...args) => lines.push(args.join(" ")); + try { + const result = await setupNim(null); + originalLog(JSON.stringify({ + result, + messages, + lines, + reasoning: process.env.NEMOCLAW_REASONING, + })); + } finally { + console.log = originalLog; + console.error = originalError; + } +})().catch((error) => { + console.error(error); + process.exit(1); +}); +`; + fs.writeFileSync(scriptPath, script); + + const result = spawnSync(process.execPath, [scriptPath], { + cwd: repoRoot, + encoding: "utf-8", + env: { + ...process.env, + HOME: tmpDir, + PATH: `${fakeBin}:${process.env.PATH || ""}`, + }, + }); + + assert.equal(result.status, 0, result.stderr); + const payload = JSON.parse(result.stdout.trim()); + assert.equal(payload.result.provider, "compatible-endpoint"); + assert.equal(payload.result.model, "reasoning-model"); + assert.equal(payload.result.preferredInferenceApi, "openai-completions"); + assert.equal(payload.reasoning, "true"); + assert.ok( + payload.messages.every( + (message: string) => !/Enable reasoning mode for this model/.test(message), + ), + ); + }); + it("forces chat completions for custom OpenAI-compatible endpoints even when /responses returns valid tool calls (#1932)", () => { const repoRoot = path.join(import.meta.dirname, ".."); const tmpDir = fs.mkdtempSync( diff --git a/test/onboard.test.ts b/test/onboard.test.ts index 609638fea2..7ca0de58e1 100644 --- a/test/onboard.test.ts +++ b/test/onboard.test.ts @@ -108,6 +108,8 @@ type OnboardTestInternals = { value: string | null | undefined, flavor: "openai" | "anthropic", ) => string; + normalizeReasoningFlag: (value?: string | null) => "true" | "false" | null; + configureCompatibleEndpointReasoning: () => Promise<"true" | "false">; providerNameToOptionKey: (name?: string | null) => string | null; parsePolicyPresetEnv: (value: string | null) => string[]; patchStagedDockerfile: ShimFn; @@ -153,6 +155,8 @@ function isOnboardTestInternals( typeof value.getSandboxPromptDefault === "function" && typeof value.getRequestedSandboxAgentName === "function" && typeof value.normalizeSandboxAgentName === "function" && + typeof value.normalizeReasoningFlag === "function" && + typeof value.configureCompatibleEndpointReasoning === "function" && typeof value.agentSupportsWebSearch === "function" && typeof value.configureWebSearch === "function" && typeof value.formatSandboxBuildEstimateNote === "function" && @@ -211,6 +215,8 @@ const { configureWebSearch, isLoopbackHostname, normalizeProviderBaseUrl, + normalizeReasoningFlag, + configureCompatibleEndpointReasoning, providerNameToOptionKey, parsePolicyPresetEnv, patchStagedDockerfile, @@ -376,10 +382,33 @@ describe("onboard helpers", () => { assert.match(script, /apiKey.*unused/); assert.match(script, /agents\.defaults\.model\.primary/); assert.match(script, /curl[\s\S]*\/chat\/completions/); + assert.match(script, /"max_tokens": 512/); + assert.match(script, /reasoning_content/); assert.doesNotMatch(script, /COMPATIBLE_API_KEY/); assert.doesNotMatch(script, /api\.deepinfra\.com/); }); + it("normalizes compatible-endpoint reasoning flag aliases", async () => { + const previousReasoning = process.env.NEMOCLAW_REASONING; + try { + expect(normalizeReasoningFlag("yes")).toBe("true"); + expect(normalizeReasoningFlag("1")).toBe("true"); + expect(normalizeReasoningFlag("no")).toBe("false"); + expect(normalizeReasoningFlag("0")).toBe("false"); + expect(normalizeReasoningFlag("maybe")).toBeNull(); + + process.env.NEMOCLAW_REASONING = "yes"; + await expect(configureCompatibleEndpointReasoning()).resolves.toBe("true"); + expect(process.env.NEMOCLAW_REASONING).toBe("true"); + } finally { + if (previousReasoning === undefined) { + delete process.env.NEMOCLAW_REASONING; + } else { + process.env.NEMOCLAW_REASONING = previousReasoning; + } + } + }); + it("wraps compatible-endpoint smoke script without newlines for OpenShell exec", () => { const command = buildCompatibleEndpointSandboxSmokeCommand("deepseek-ai/DeepSeek-V4-Flash"); From 66bbe29e680eac33c7d35b9be318b06083892cd1 Mon Sep 17 00:00:00 2001 From: Test User Date: Fri, 8 May 2026 13:47:19 -0700 Subject: [PATCH 2/4] fix: persist compatible endpoint reasoning state Signed-off-by: Test User --- src/lib/onboard.ts | 48 ++++++++++++++++++++++++++++++-- src/lib/state/onboard-session.ts | 4 +++ test/onboard-selection.test.ts | 7 +++++ test/onboard.test.ts | 10 ++++++- 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/lib/onboard.ts b/src/lib/onboard.ts index d5af14c19e..af1c04315b 100644 --- a/src/lib/onboard.ts +++ b/src/lib/onboard.ts @@ -1492,6 +1492,9 @@ function getNavigationChoice(value = ""): "back" | "exit" | null { return null; } +/** + * Normalize user-provided truthy/falsy aliases for compatible endpoint reasoning mode. + */ function normalizeReasoningFlag(value: string | null | undefined): "true" | "false" | null { const normalized = String(value ?? "") .trim() @@ -1505,12 +1508,25 @@ function normalizeReasoningFlag(value: string | null | undefined): "true" | "fal return null; } -async function configureCompatibleEndpointReasoning(): Promise<"true" | "false"> { - const configured = normalizeReasoningFlag(process.env.NEMOCLAW_REASONING); +/** + * Resolve compatible-endpoint reasoning mode and mirror it into process env for probes/builds. + */ +async function configureCompatibleEndpointReasoning( + storedValue?: string | null, +): Promise<"true" | "false"> { + const configured = normalizeReasoningFlag(storedValue ?? process.env.NEMOCLAW_REASONING); process.env.NEMOCLAW_REASONING = configured ?? "false"; return process.env.NEMOCLAW_REASONING as "true" | "false"; } +/** + * Drop compatible-endpoint reasoning state when the user switches providers. + */ +function clearCompatibleEndpointReasoning(): null { + delete process.env.NEMOCLAW_REASONING; + return null; +} + function exitOnboardFromPrompt(): never { console.log(" Exiting onboarding."); process.exit(1); @@ -6107,6 +6123,7 @@ async function setupNim( endpointUrl: string | null; credentialEnv: string | null; preferredInferenceApi: string | null; + compatibleEndpointReasoning: string | null; nimContainer: string | null; }> { step(3, 8, "Configuring inference (NIM)"); @@ -6117,6 +6134,7 @@ async function setupNim( let endpointUrl: string | null = REMOTE_PROVIDER_CONFIG.build.endpointUrl; let credentialEnv: string | null = REMOTE_PROVIDER_CONFIG.build.credentialEnv; let preferredInferenceApi: string | null = null; + let compatibleEndpointReasoning: string | null = null; // Detect local inference options. Bound curl with --connect-timeout/--max-time // so a half-open port or stalled listener cannot hang the onboard at step 3 @@ -6633,6 +6651,7 @@ async function setupNim( if (selected.key === "custom") { const reasoning = await configureCompatibleEndpointReasoning(); + compatibleEndpointReasoning = reasoning; if (reasoning === "true") { console.log(" Reasoning mode enabled for this compatible endpoint model."); } @@ -6681,6 +6700,7 @@ async function setupNim( continue selectionLoop; } } else if (selected.key === "anthropicCompatible") { + compatibleEndpointReasoning = clearCompatibleEndpointReasoning(); const validation = await validateCustomAnthropicSelection( remoteConfig.label, endpointUrl || ANTHROPIC_ENDPOINT_URL, @@ -7248,7 +7268,18 @@ async function setupNim( } } - return { model, provider, endpointUrl, credentialEnv, preferredInferenceApi, nimContainer }; + if (provider !== "compatible-endpoint") { + compatibleEndpointReasoning = clearCompatibleEndpointReasoning(); + } + return { + model, + provider, + endpointUrl, + credentialEnv, + preferredInferenceApi, + compatibleEndpointReasoning, + nimContainer, + }; } // ── Step 4: Inference provider ─────────────────────────────────── @@ -9394,6 +9425,7 @@ function toSessionUpdates( endpointUrl?: string | null; credentialEnv?: string | null; preferredInferenceApi?: string | null; + compatibleEndpointReasoning?: string | null; nimContainer?: string | null; webSearchConfig?: WebSearchConfig | null; policyPresets?: string[] | null; @@ -9413,6 +9445,9 @@ function toSessionUpdates( if (updates.preferredInferenceApi !== undefined) { normalized.preferredInferenceApi = toOptionalString(updates.preferredInferenceApi); } + if (updates.compatibleEndpointReasoning !== undefined) { + normalized.compatibleEndpointReasoning = updates.compatibleEndpointReasoning ?? null; + } if (updates.nimContainer !== undefined) normalized.nimContainer = toOptionalString(updates.nimContainer); if (updates.webSearchConfig !== undefined) normalized.webSearchConfig = updates.webSearchConfig; @@ -9948,6 +9983,7 @@ async function onboard(opts: OnboardOptions = {}): Promise { let endpointUrl = session?.endpointUrl || null; let credentialEnv = session?.credentialEnv || null; let preferredInferenceApi = session?.preferredInferenceApi || null; + let compatibleEndpointReasoning = session?.compatibleEndpointReasoning || null; let nimContainer = session?.nimContainer || null; let webSearchConfig = session?.webSearchConfig || null; let forceProviderSelection = false; @@ -9961,6 +9997,10 @@ async function onboard(opts: OnboardOptions = {}): Promise { if (resumeProviderSelection) { skippedStepMessage("provider_selection", `${provider} / ${model}`); hydrateCredentialEnv(credentialEnv); + compatibleEndpointReasoning = + provider === "compatible-endpoint" + ? await configureCompatibleEndpointReasoning(compatibleEndpointReasoning) + : clearCompatibleEndpointReasoning(); } else { // #2753: do not persist sandboxName to onboard-session.json before // the sandbox actually exists in the gateway (Step 6 markStepComplete @@ -9974,6 +10014,7 @@ async function onboard(opts: OnboardOptions = {}): Promise { endpointUrl = selection.endpointUrl; credentialEnv = selection.credentialEnv; preferredInferenceApi = selection.preferredInferenceApi; + compatibleEndpointReasoning = selection.compatibleEndpointReasoning; nimContainer = selection.nimContainer; onboardSession.markStepComplete( "provider_selection", @@ -9983,6 +10024,7 @@ async function onboard(opts: OnboardOptions = {}): Promise { endpointUrl, credentialEnv, preferredInferenceApi, + compatibleEndpointReasoning, nimContainer, }), ); diff --git a/src/lib/state/onboard-session.ts b/src/lib/state/onboard-session.ts index bed04ea39f..c66f219c6d 100644 --- a/src/lib/state/onboard-session.ts +++ b/src/lib/state/onboard-session.ts @@ -76,6 +76,7 @@ export interface Session { endpointUrl: string | null; credentialEnv: string | null; preferredInferenceApi: string | null; + compatibleEndpointReasoning: string | null; nimContainer: string | null; routerPid: number | null; routerCredentialHash: string | null; @@ -127,6 +128,7 @@ export interface SessionUpdates { endpointUrl?: string; credentialEnv?: string; preferredInferenceApi?: string; + compatibleEndpointReasoning?: string | null; nimContainer?: string; routerPid?: number; routerCredentialHash?: string; @@ -309,6 +311,7 @@ export function createSession(overrides: Partial = {}): Session { endpointUrl: overrides.endpointUrl ?? null, credentialEnv: overrides.credentialEnv ?? null, preferredInferenceApi: overrides.preferredInferenceApi ?? null, + compatibleEndpointReasoning: overrides.compatibleEndpointReasoning ?? null, nimContainer: overrides.nimContainer ?? null, routerPid: readPositiveInteger(overrides.routerPid), routerCredentialHash: overrides.routerCredentialHash ?? null, @@ -348,6 +351,7 @@ export function normalizeSession(data: Session | SessionJsonValue | undefined): endpointUrl: typeof data.endpointUrl === "string" ? redactUrl(data.endpointUrl) : null, credentialEnv: readString(data.credentialEnv), preferredInferenceApi: readString(data.preferredInferenceApi), + compatibleEndpointReasoning: readString(data.compatibleEndpointReasoning), nimContainer: readString(data.nimContainer), routerPid: readPositiveInteger(data.routerPid), routerCredentialHash: readString(data.routerCredentialHash), diff --git a/test/onboard-selection.test.ts b/test/onboard-selection.test.ts index da0f0d7f41..a35d1e661f 100644 --- a/test/onboard-selection.test.ts +++ b/test/onboard-selection.test.ts @@ -2067,6 +2067,7 @@ const { setupNim } = require(${onboardPath}); ); const fakeBin = path.join(tmpDir, "bin"); const scriptPath = path.join(tmpDir, "custom-openai-reasoning-check.js"); + const curlArgsLog = path.join(tmpDir, "custom-openai-reasoning-curl-args.log"); const onboardPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "onboard.js")); const credentialsPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "credentials", "store.js")); const runnerPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "runner.js")); @@ -2075,6 +2076,8 @@ const { setupNim } = require(${onboardPath}); fs.writeFileSync( path.join(fakeBin, "curl"), `#!/usr/bin/env bash +args_log=${JSON.stringify(curlArgsLog)} +printf '%s\\n' "$*" >> "$args_log" body='{"error":{"message":"bad request"}}' status="400" outfile="" @@ -2153,6 +2156,10 @@ const { setupNim } = require(${onboardPath}); assert.equal(payload.result.model, "reasoning-model"); assert.equal(payload.result.preferredInferenceApi, "openai-completions"); assert.equal(payload.reasoning, "true"); + const curlInvocations = fs.readFileSync(curlArgsLog, "utf-8"); + assert.match(curlInvocations, /chat\/completions/); + assert.doesNotMatch(curlInvocations, /\/responses/); + assert.doesNotMatch(curlInvocations, /(^|\s)-N(\s|$)/); assert.ok( payload.messages.every( (message: string) => !/Enable reasoning mode for this model/.test(message), diff --git a/test/onboard.test.ts b/test/onboard.test.ts index 7ca0de58e1..b6bfe8bc00 100644 --- a/test/onboard.test.ts +++ b/test/onboard.test.ts @@ -109,7 +109,7 @@ type OnboardTestInternals = { flavor: "openai" | "anthropic", ) => string; normalizeReasoningFlag: (value?: string | null) => "true" | "false" | null; - configureCompatibleEndpointReasoning: () => Promise<"true" | "false">; + configureCompatibleEndpointReasoning: (storedValue?: string | null) => Promise<"true" | "false">; providerNameToOptionKey: (name?: string | null) => string | null; parsePolicyPresetEnv: (value: string | null) => string[]; patchStagedDockerfile: ShimFn; @@ -397,6 +397,14 @@ describe("onboard helpers", () => { expect(normalizeReasoningFlag("0")).toBe("false"); expect(normalizeReasoningFlag("maybe")).toBeNull(); + delete process.env.NEMOCLAW_REASONING; + await expect(configureCompatibleEndpointReasoning()).resolves.toBe("false"); + expect(process.env.NEMOCLAW_REASONING).toBe("false"); + + process.env.NEMOCLAW_REASONING = "no"; + await expect(configureCompatibleEndpointReasoning("yes")).resolves.toBe("true"); + expect(process.env.NEMOCLAW_REASONING).toBe("true"); + process.env.NEMOCLAW_REASONING = "yes"; await expect(configureCompatibleEndpointReasoning()).resolves.toBe("true"); expect(process.env.NEMOCLAW_REASONING).toBe("true"); From 2c1672b1e4d18da0be67853373c8f0d7d570f768 Mon Sep 17 00:00:00 2001 From: Test User Date: Fri, 8 May 2026 13:55:57 -0700 Subject: [PATCH 3/4] fix: persist reasoning state through session updates Signed-off-by: Test User --- src/lib/state/onboard-session.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/state/onboard-session.ts b/src/lib/state/onboard-session.ts index c66f219c6d..16d0f4352e 100644 --- a/src/lib/state/onboard-session.ts +++ b/src/lib/state/onboard-session.ts @@ -714,6 +714,11 @@ export function filterSafeUpdates(updates: SessionUpdates): Partial { if (typeof updates.credentialEnv === "string") safe.credentialEnv = updates.credentialEnv; if (typeof updates.preferredInferenceApi === "string") safe.preferredInferenceApi = updates.preferredInferenceApi; + if (typeof updates.compatibleEndpointReasoning === "string") { + safe.compatibleEndpointReasoning = updates.compatibleEndpointReasoning; + } else if (updates.compatibleEndpointReasoning === null) { + safe.compatibleEndpointReasoning = null; + } if (typeof updates.nimContainer === "string") safe.nimContainer = updates.nimContainer; if (typeof updates.routerPid === "number" && Number.isInteger(updates.routerPid) && updates.routerPid > 0) { safe.routerPid = updates.routerPid; From 747848cad2a34dba34ce82e9dcc7b926f6850fc0 Mon Sep 17 00:00:00 2001 From: Test User Date: Fri, 8 May 2026 14:00:04 -0700 Subject: [PATCH 4/4] fix: expose reasoning mode in debug summary Signed-off-by: Test User --- src/lib/state/onboard-session.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/state/onboard-session.ts b/src/lib/state/onboard-session.ts index 16d0f4352e..14f04592a1 100644 --- a/src/lib/state/onboard-session.ts +++ b/src/lib/state/onboard-session.ts @@ -156,6 +156,7 @@ export interface DebugSessionSummary { endpointUrl: string | null; credentialEnv: string | null; preferredInferenceApi: string | null; + compatibleEndpointReasoning: string | null; nimContainer: string | null; policyPresets: string[] | null; gpuPassthrough: boolean; @@ -863,6 +864,7 @@ export function summarizeForDebug( endpointUrl: redactUrl(session.endpointUrl), credentialEnv: session.credentialEnv, preferredInferenceApi: session.preferredInferenceApi, + compatibleEndpointReasoning: session.compatibleEndpointReasoning, nimContainer: session.nimContainer, policyPresets: session.policyPresets, gpuPassthrough: session.gpuPassthrough,