feat(policy): tier-based policy selector with combined access-mode UI#1753
feat(policy): tier-based policy selector with combined access-mode UI#1753ericksoa merged 7 commits intoNVIDIA:mainfrom
Conversation
Implement a three-tier (Restricted / Balanced / Open) policy preset selector for the NemoClaw onboarding wizard, with a combined preset selection and access-mode screen replacing the previous two-step flow. Tier selector: - Radio-button TUI (↑/↓, Space to select, Enter to confirm) - Green [✓]/dim [ ] buttons; green key hints, dim action labels - Defaults to Balanced Preset + access screen: - All available presets shown in one screen; tier defaults pre-checked - Space to include/exclude any preset, r to toggle read vs read-write - Orange [rw] badge signals write-capable access as a visual warning - Tier presets listed first, extras below Other changes: - Fix brave preset regression: credential-based suggestions now seed the interactive preset selector correctly - Update tiers.yaml: all presets read-write, add brave to Balanced, add jira/outlook to Open, fix discord access - Add defaultModelId support to promptCloudModel for env-var defaulting - Add 6 integration tests for selectTierPresetsAndAccess - Add scripts/dev-tier-selector.js smoke-test (not part of CI)
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR introduces a policy tier system enabling users to select one of three preset security tiers ( Changes
Sequence DiagramsequenceDiagram
participant User as User/CLI
participant TierUI as Tier Selector
participant Registry as Sandbox Registry
participant TierModule as Tier Module
participant PolicyModule as Policy Module
User->>TierUI: selectPolicyTier()
activate TierUI
TierUI->>TierModule: listTiers()
TierModule->>TierModule: Read tiers.yaml
TierModule-->>TierUI: Return tiers list
TierUI->>User: Prompt: Choose tier
User-->>TierUI: Select tier (e.g., "balanced")
TierUI-->>User: Return chosen tier name
deactivate TierUI
User->>TierUI: selectTierPresetsAndAccess(tierName)
activate TierUI
TierUI->>TierModule: resolveTierPresets(tierName)
TierModule->>TierModule: Resolve tier presets with access levels
TierModule-->>TierUI: Return [{ name, access }]
TierUI->>User: Prompt: Configure access per preset
User-->>TierUI: Confirm/override access levels
TierUI-->>User: Return configured presets
deactivate TierUI
User->>Registry: updateSandbox({ policyTier })
activate Registry
Registry->>Registry: Persist tier to registry
Registry-->>User: Sandbox updated
deactivate Registry
User->>PolicyModule: applyPreset(sandboxName, presetName, { access })
activate PolicyModule
PolicyModule->>PolicyModule: Apply preset with specified access level
PolicyModule-->>User: Preset applied
deactivate PolicyModule
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
bin/lib/tiers.js (1)
26-30: Validate parsed tier config shape before returning it.If
tiers.yamlis malformed or missingtiers, downstream code fails with opaque errors. Add an explicit structural check here and throw a targeted message.💡 Proposed refactor
function listTiers() { const content = fs.readFileSync(TIERS_FILE, "utf-8"); const parsed = YAML.parse(content); - return parsed.tiers; + if (!parsed || !Array.isArray(parsed.tiers)) { + throw new Error(`Invalid tier configuration in ${TIERS_FILE}: expected top-level 'tiers' array`); + } + return parsed.tiers; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bin/lib/tiers.js` around lines 26 - 30, The listTiers function should validate the parsed YAML shape before returning; after parsing with YAML.parse(TIERS_FILE) check that parsed is an object and that parsed.tiers exists and is the expected type (e.g., Array.isArray(parsed.tiers) or typeof parsed.tiers === "object" per your schema), and if not throw a clear error mentioning TIERS_FILE and that "tiers" is missing or malformed so callers get a targeted message instead of opaque failures; update listTiers to perform this check and throw a descriptive Error when validation fails.src/lib/onboard.ts (4)
2730-2737: Misleading underscore prefix on actively used variable.The variable
_envUrlis actively used throughout this block, so the underscore prefix is misleading. Per coding guidelines, underscore prefixes should only be used for unused variables.♻️ Suggested rename
- const _envUrl = (process.env.NEMOCLAW_ENDPOINT_URL || "").trim(); + const envUrl = (process.env.NEMOCLAW_ENDPOINT_URL || "").trim(); const endpointInput = isNonInteractive() - ? _envUrl - : (await prompt( - _envUrl - ? ` OpenAI-compatible base URL [${_envUrl}]: ` + ? envUrl + : (await prompt( + envUrl + ? ` OpenAI-compatible base URL [${envUrl}]: ` : " OpenAI-compatible base URL (e.g., https://openrouter.ai): ", - )) || _envUrl; + )) || envUrl;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/onboard.ts` around lines 2730 - 2737, Rename the misleadingly prefixed variable _envUrl to envUrl in this onboarding block in src/lib/onboard.ts so it no longer implies an unused variable; update the declaration and every usage (the declaration currently using process.env.NEMOCLAW_ENDPOINT_URL, the conditional that uses isNonInteractive(), and the prompt call that references the variable) to use envUrl instead so the code reads clearly and follows naming conventions.
2730-2842: Consolidating feedback on underscore-prefixed variables.Throughout this section, several variables are prefixed with underscore (
_envUrl,_nvProviderKey,_providerKeyHint,_envModel,_envModelRemote) but are actively used. Per coding guidelines, underscore prefix should indicate unused variables.If the intent is to avoid shadowing outer-scope variables, consider using more descriptive names (e.g.,
envEndpointUrl,providerKeyEnv,envModelHint) instead of underscore prefixes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/onboard.ts` around lines 2730 - 2842, Several local variables (e.g., _envUrl, _nvProviderKey, _providerKeyHint, _envModel, _envModelRemote) are prefixed with underscores but are used; rename them to descriptive non-underscored names to follow conventions and avoid implying "unused." Update occurrences in the onboarding selection logic where these are referenced (the blocks using getNavigationChoice, normalizeProviderBaseUrl, validateNvidiaApiKeyValue, ensureApiKey, ensureNamedCredential, and the model selection logic) — for example rename _envUrl -> envEndpointUrl, _nvProviderKey -> providerKeyEnv, _providerKeyHint -> providerKeyHintEnv, _envModel -> envModelHint, _envModelRemote -> envModelRemote — and replace all uses accordingly so behavior (prompt defaults, env fallback, model selection, and credential assignment) remains identical.
2757-2764: Same misleading underscore prefix pattern.As noted above,
_envUrlis actively used and should not have an underscore prefix.♻️ Suggested rename
- const _envUrl = (process.env.NEMOCLAW_ENDPOINT_URL || "").trim(); + const envUrl = (process.env.NEMOCLAW_ENDPOINT_URL || "").trim(); const endpointInput = isNonInteractive() - ? _envUrl - : (await prompt( - _envUrl - ? ` Anthropic-compatible base URL [${_envUrl}]: ` + ? envUrl + : (await prompt( + envUrl + ? ` Anthropic-compatible base URL [${envUrl}]: ` : " Anthropic-compatible base URL (e.g., https://proxy.example.com): ", - )) || _envUrl; + )) || envUrl;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/onboard.ts` around lines 2757 - 2764, The variable name `_envUrl` uses a misleading underscore prefix despite being used; rename it to a non-underscored, descriptive identifier (e.g., envUrl) wherever declared and referenced in this block so references in the ternary and prompt logic (variables/functions: _envUrl, endpointInput, isNonInteractive, prompt) are updated consistently; ensure you update both the declaration and the uses inside the prompt/default fallback expression to avoid any undefined reference.
3800-3814: Minor issues in non-TTY fallback path.
- Line 3802: The forEach callback declares
ibut doesn't use it (onlytis used).- Line 3810:
parseIntshould include radix 10 for clarity and safety.♻️ Suggested fixes
console.log(""); console.log(" Policy tier — controls which network presets are enabled:"); - allTiers.forEach((t, i) => { + allTiers.forEach((t) => { const marker = t.name === defaultTier.name ? RADIO_ON : RADIO_OFF; console.log(` ${marker} ${t.label}`); }); console.log(""); const answer = await prompt( ` Select tier [1-${allTiers.length}] (default: ${allTiers.indexOf(defaultTier) + 1} ${defaultTier.name}): `, ); - const idx = answer.trim() === "" ? allTiers.indexOf(defaultTier) : parseInt(answer.trim(), 10) - 1; + const idx = answer.trim() === "" ? allTiers.indexOf(defaultTier) : Number.parseInt(answer.trim(), 10) - 1;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/onboard.ts` around lines 3800 - 3814, Remove the unused index parameter from the forEach callback over allTiers (change forEach((t, i) => ...) to forEach((t) => ...) or use _i if you want to signal unused) and make sure the parseInt call that parses the prompt answer explicitly includes radix 10 (use parseInt(answer.trim(), 10)) so the code in this prompt-handling block (references: allTiers.forEach, parseInt, defaultTier, RADIO_ON, RADIO_OFF, prompt) has no unused parameters and uses a safe radix.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lib/model-prompts.ts`:
- Around line 160-170: The current fallback uses manualDefault directly in the
promptFn, which can cause an endless validation loop if defaultModelId is
invalid; update the logic around manualDefault/promptFn so the fallback is only
returned after verifying it with
deps.validateNvidiaEndpointModelFn(manualDefault, nvidiaApiKey) (or run that
validation once when computing manualDefault/defaultCuratedIdx), and otherwise
return an empty string (or no fallback) so promptManualModelId can re-prompt;
ensure references to manualDefault, promptManualModelId, deps.promptFn,
deps.validateNvidiaEndpointModelFn, defaultModelId and defaultCuratedIdx are
used to locate and implement this guard.
In `@src/lib/registry.ts`:
- Line 17: registerSandbox currently persists a SandboxEntry without the new
policyTier field so the value is lost on initial registration; update the
initial write in registerSandbox to include policyTier from the incoming data
(and any defaults or nullability consistent with SandboxEntry) when constructing
the entry to persist so the field is not dropped on first save. Ensure any
create/insert path in registerSandbox uses the same property name policyTier as
defined on SandboxEntry.
---
Nitpick comments:
In `@bin/lib/tiers.js`:
- Around line 26-30: The listTiers function should validate the parsed YAML
shape before returning; after parsing with YAML.parse(TIERS_FILE) check that
parsed is an object and that parsed.tiers exists and is the expected type (e.g.,
Array.isArray(parsed.tiers) or typeof parsed.tiers === "object" per your
schema), and if not throw a clear error mentioning TIERS_FILE and that "tiers"
is missing or malformed so callers get a targeted message instead of opaque
failures; update listTiers to perform this check and throw a descriptive Error
when validation fails.
In `@src/lib/onboard.ts`:
- Around line 2730-2737: Rename the misleadingly prefixed variable _envUrl to
envUrl in this onboarding block in src/lib/onboard.ts so it no longer implies an
unused variable; update the declaration and every usage (the declaration
currently using process.env.NEMOCLAW_ENDPOINT_URL, the conditional that uses
isNonInteractive(), and the prompt call that references the variable) to use
envUrl instead so the code reads clearly and follows naming conventions.
- Around line 2730-2842: Several local variables (e.g., _envUrl, _nvProviderKey,
_providerKeyHint, _envModel, _envModelRemote) are prefixed with underscores but
are used; rename them to descriptive non-underscored names to follow conventions
and avoid implying "unused." Update occurrences in the onboarding selection
logic where these are referenced (the blocks using getNavigationChoice,
normalizeProviderBaseUrl, validateNvidiaApiKeyValue, ensureApiKey,
ensureNamedCredential, and the model selection logic) — for example rename
_envUrl -> envEndpointUrl, _nvProviderKey -> providerKeyEnv, _providerKeyHint ->
providerKeyHintEnv, _envModel -> envModelHint, _envModelRemote -> envModelRemote
— and replace all uses accordingly so behavior (prompt defaults, env fallback,
model selection, and credential assignment) remains identical.
- Around line 2757-2764: The variable name `_envUrl` uses a misleading
underscore prefix despite being used; rename it to a non-underscored,
descriptive identifier (e.g., envUrl) wherever declared and referenced in this
block so references in the ternary and prompt logic (variables/functions:
_envUrl, endpointInput, isNonInteractive, prompt) are updated consistently;
ensure you update both the declaration and the uses inside the prompt/default
fallback expression to avoid any undefined reference.
- Around line 3800-3814: Remove the unused index parameter from the forEach
callback over allTiers (change forEach((t, i) => ...) to forEach((t) => ...) or
use _i if you want to signal unused) and make sure the parseInt call that parses
the prompt answer explicitly includes radix 10 (use parseInt(answer.trim(), 10))
so the code in this prompt-handling block (references: allTiers.forEach,
parseInt, defaultTier, RADIO_ON, RADIO_OFF, prompt) has no unused parameters and
uses a safe radix.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 083d95f6-3672-44d6-a5d0-38bc7127a122
📒 Files selected for processing (9)
bin/lib/onboard.jsbin/lib/tiers.jsnemoclaw-blueprint/policies/tiers.yamlscripts/dev-tier-selector.jssrc/lib/model-prompts.tssrc/lib/onboard.tssrc/lib/registry.tstest/policy-tiers-onboard.test.jstest/policy-tiers.test.js
| // If default is a custom (non-curated) model ID, pre-fill it in the manual prompt | ||
| const manualDefault = defaultCuratedIdx < 0 && defaultModelId ? defaultModelId : ""; | ||
| const manualLabel = manualDefault | ||
| ? ` NVIDIA Endpoints model id [${manualDefault}]: ` | ||
| : " NVIDIA Endpoints model id: "; | ||
| return promptManualModelId( | ||
| " NVIDIA Endpoints model id: ", | ||
| manualLabel, | ||
| "NVIDIA Endpoints", | ||
| (model) => deps.validateNvidiaEndpointModelFn(model, nvidiaApiKey), | ||
| deps, | ||
| { ...deps, promptFn: async (q) => (await deps.promptFn(q)) || manualDefault }, | ||
| ); |
There was a problem hiding this comment.
Guard manual default fallback with model-id safety checks.
When defaultModelId is invalid and non-curated, the fallback promptFn can repeatedly return that invalid default, causing an endless validation loop.
💡 Proposed fix
- const manualDefault = defaultCuratedIdx < 0 && defaultModelId ? defaultModelId : "";
+ const safeDefaultModelId = isSafeModelId(defaultModelId) ? defaultModelId : "";
+ const manualDefault = defaultCuratedIdx < 0 ? safeDefaultModelId : "";
const manualLabel = manualDefault
? ` NVIDIA Endpoints model id [${manualDefault}]: `
: " NVIDIA Endpoints model id: ";
return promptManualModelId(
manualLabel,
"NVIDIA Endpoints",
(model) => deps.validateNvidiaEndpointModelFn(model, nvidiaApiKey),
- { ...deps, promptFn: async (q) => (await deps.promptFn(q)) || manualDefault },
+ {
+ ...deps,
+ promptFn: async (q) => {
+ const answer = await deps.promptFn(q);
+ return answer || manualDefault;
+ },
+ },
);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/model-prompts.ts` around lines 160 - 170, The current fallback uses
manualDefault directly in the promptFn, which can cause an endless validation
loop if defaultModelId is invalid; update the logic around
manualDefault/promptFn so the fallback is only returned after verifying it with
deps.validateNvidiaEndpointModelFn(manualDefault, nvidiaApiKey) (or run that
validation once when computing manualDefault/defaultCuratedIdx), and otherwise
return an empty string (or no fallback) so promptManualModelId can re-prompt;
ensure references to manualDefault, promptManualModelId, deps.promptFn,
deps.validateNvidiaEndpointModelFn, defaultModelId and defaultCuratedIdx are
used to locate and implement this guard.
|
✨ Thanks for submitting this PR, which proposes a new feature to implement a tier-based policy selector . |
Migrate PR's tiers module from bin/lib/tiers.js (CJS implementation) to src/lib/tiers.ts, matching the shim-removal pattern from NVIDIA#1713. Update test imports from deleted bin/lib/ shims to dist/lib/ paths. Keep both PR's policyTier field and main's agent/dangerouslySkipPermissions fields in the SandboxEntry interface. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (1)
src/lib/model-prompts.ts (1)
160-169:⚠️ Potential issue | 🟠 MajorGuard the manual fallback before reusing it on empty input.
Line 169 still feeds
manualDefaultback intopromptManualModelId()for every blank answer. IfdefaultModelIdis unsafe or failsvalidateNvidiaEndpointModelFn(), pressing Enter just replays the same bad value forever and the prompt cannot recover.💡 Suggested guard
- const manualDefault = defaultCuratedIdx < 0 && defaultModelId ? defaultModelId : ""; + let manualDefault = ""; + if (defaultCuratedIdx < 0 && defaultModelId && isSafeModelId(defaultModelId)) { + const validation = deps.validateNvidiaEndpointModelFn(defaultModelId, nvidiaApiKey); + if (validation.ok || shouldDeferValidationFailure(validation)) { + manualDefault = defaultModelId; + } + } const manualLabel = manualDefault ? ` NVIDIA Endpoints model id [${manualDefault}]: ` : " NVIDIA Endpoints model id: ";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/model-prompts.ts` around lines 160 - 169, The manual fallback value manualDefault is being unconditionally replayed on blank input which can trap the prompt with an invalid default; update the prompt call around promptManualModelId so the injected promptFn wrapper only returns manualDefault when the user input is blank AND manualDefault passes validateNvidiaEndpointModelFn(nvidiaApiKey) (or when defaultCuratedIdx >= 0 logic indicates a safe curated default), otherwise return the raw user input (empty string) so validation can run and the prompt can recover; reference manualDefault, promptManualModelId, validateNvidiaEndpointModelFn, deps.promptFn and defaultModelId when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lib/onboard.ts`:
- Around line 4559-4562: The code currently maps resolvedPresets to names
(interactiveChoice) and calls onSelection(interactiveChoice), discarding the
chosen access mode; update the plumbing so the full objects from
selectTierPresetsAndAccess (each {name, access}) are forwarded to
onSelection/resume and then used when applying policies instead of just name.
Specifically, stop mapping to names, pass the resolvedPresets array into
onSelection (and any resume handler), and change the policy application call
(currently policies.applyPreset(sandboxName, name)) to consume both identifiers
(e.g., policies.applyPreset(sandboxName, name, access) or an equivalent API such
as policies.applyPresetWithAccess or policies.setPresetAccess) so the selected
access from selectTierPresetsAndAccess is honored. Ensure corresponding callers
of onSelection/resume and policies.applyPreset are updated to accept and forward
the {name, access} shape or hide the access toggle until policy application
supports it.
- Around line 4181-4205: The final selection should be built by iterating the
stable ordered array instead of the mutable Set to preserve apply order; change
places that currently do [...included].map(...) or iterate `included` directly
to iterate `ordered` and filter by membership in `included` (e.g.,
ordered.filter(p => included.has(p.name)) or ordered.map over p.name when
checking included) and then map each to { name, access: accessModes[name] };
update all occurrences that read from `included` (including the non-interactive
return and other loops that render/apply presets) so the output order always
follows `ordered` while still respecting the toggled membership in `included`.
- Around line 2899-2904: The envProviderHint is lowercased but compared against
options' mixed-case keys, causing valid hints (e.g., "anthropicCompatible" or
"cloud") to miss and fall back to "build"; update the matching in the
envProviderIdx calculation to compare against a normalized key (for example
compare envProviderHint to o.key.toLowerCase()) and include any known aliases
(e.g., treat "cloud" as the equivalent key) so options.findIndex((o) => ...)
uses normalized forms; ensure defaultIdx logic remains the same and still adds 1
after resolving the correct index.
In `@src/lib/tiers.ts`:
- Around line 59-60: The code currently uses falsy coalescing (e.g., const
selected = options.selected || null) which converts an explicit empty array ([])
into null and breaks resolveTierPresets behavior; change the assignment to only
treat undefined as absent (e.g., const selected = options.selected === undefined
? null : options.selected) so [] remains distinct, and apply the same fix to the
similar assignments around presets/other option fields (lines handling
presets/overrides) so only undefined is defaulted rather than any falsy value.
- Around line 1-17: Remove the top-line "// `@ts-nocheck`", convert the CommonJS
requires to ES module imports (e.g. import fs from "fs"; import path from
"path"; import YAML from "yaml"; import { ROOT } from "./runner";), and add
explicit TypeScript types for any exported structures and functions in this
module (for example define interfaces for Tier, PresetMapping, and the function
return types used when loading/parsing tiers). Ensure all usages of fs/path/YAML
are typed (use NodeJS types and YAML.Document if needed), update any default
exports or named exports to match the rest of src/lib/* patterns, and confirm
the file compiles under tsconfig.cli.json without relying on // `@ts-nocheck`.
---
Duplicate comments:
In `@src/lib/model-prompts.ts`:
- Around line 160-169: The manual fallback value manualDefault is being
unconditionally replayed on blank input which can trap the prompt with an
invalid default; update the prompt call around promptManualModelId so the
injected promptFn wrapper only returns manualDefault when the user input is
blank AND manualDefault passes validateNvidiaEndpointModelFn(nvidiaApiKey) (or
when defaultCuratedIdx >= 0 logic indicates a safe curated default), otherwise
return the raw user input (empty string) so validation can run and the prompt
can recover; reference manualDefault, promptManualModelId,
validateNvidiaEndpointModelFn, deps.promptFn and defaultModelId when making the
change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 3bfb770c-69d5-4463-867d-5906879bb91c
📒 Files selected for processing (8)
bin/lib/tiers.jsscripts/dev-tier-selector.jssrc/lib/model-prompts.tssrc/lib/onboard.tssrc/lib/registry.tssrc/lib/tiers.tstest/policy-tiers-onboard.test.jstest/policy-tiers.test.js
✅ Files skipped from review due to trivial changes (1)
- scripts/dev-tier-selector.js
🚧 Files skipped from review as they are similar to previous changes (2)
- bin/lib/tiers.js
- src/lib/registry.ts
| // @ts-nocheck | ||
| // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| // | ||
| // Tier management — load tier definitions and resolve preset selections. | ||
| // | ||
| // Tiers are defined in nemoclaw-blueprint/policies/tiers.yaml. | ||
| // Each tier is a named posture (restricted, balanced, open) that maps to | ||
| // a set of policy presets and their default access levels. | ||
| // | ||
| // The base sandbox policy is always applied regardless of tier. | ||
| // Tiers layer additional presets on top of that baseline. | ||
|
|
||
| const fs = require("fs"); | ||
| const path = require("path"); | ||
| const YAML = require("yaml"); | ||
| const { ROOT } = require("./runner"); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
# Check if tsconfig.cli.json exists
find . -name "tsconfig.cli.json" -type f
# Get the full content of src/lib/tiers.ts
wc -l src/lib/tiers.tsRepository: NVIDIA/NemoClaw
Length of output: 98
🏁 Script executed:
# Read the full file to understand context
cat -n src/lib/tiers.tsRepository: NVIDIA/NemoClaw
Length of output: 3251
🏁 Script executed:
# Check the tsconfig.cli.json configuration
cat tsconfig.cli.json 2>/dev/null || echo "File not found"Repository: NVIDIA/NemoClaw
Length of output: 589
🏁 Script executed:
# Check if there's a tsconfig.json
cat tsconfig.json 2>/dev/null || echo "File not found"Repository: NVIDIA/NemoClaw
Length of output: 73
🏁 Script executed:
# Check for other TS files in src/lib to see how they handle types
fd -t f '\.(ts|tsx)$' src/lib --exec head -5 {} \; 2>/dev/null | head -50Repository: NVIDIA/NemoClaw
Length of output: 2317
Remove @ts-nocheck and use TypeScript types for this CLI module.
src/lib/tiers.ts must comply with the guideline for src/lib/**/*.{ts,tsx} to use TypeScript with type-checking via tsconfig.cli.json. Replace the @ts-nocheck directive, convert CommonJS require() statements to ES6 imports, and define proper TypeScript types instead of JSDoc annotations. The file is already in scope for compilation and should follow the same typing pattern as other files in src/lib/.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/tiers.ts` around lines 1 - 17, Remove the top-line "// `@ts-nocheck`",
convert the CommonJS requires to ES module imports (e.g. import fs from "fs";
import path from "path"; import YAML from "yaml"; import { ROOT } from
"./runner";), and add explicit TypeScript types for any exported structures and
functions in this module (for example define interfaces for Tier, PresetMapping,
and the function return types used when loading/parsing tiers). Ensure all
usages of fs/path/YAML are typed (use NodeJS types and YAML.Document if needed),
update any default exports or named exports to match the rest of src/lib/*
patterns, and confirm the file compiles under tsconfig.cli.json without relying
on // `@ts-nocheck`.
- Guard manualDefault with isSafeModelId to prevent infinite validation loop - Case-insensitive provider hint matching for NEMOCLAW_PROVIDER env var - Use ordered array instead of Set iteration for stable preset ordering - Plumb access mode through to applyPreset for future read/read-write support - Fix resolveTierPresets treating empty selected array as null (permission expansion) - Add @ts-nocheck to dev-only tier selector smoke test script Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lib/policies.ts`:
- Line 222: applyPreset currently ignores the _options parameter so UI-selected
access modes are dropped; update the applyPreset(sandboxName, presetName,
_options = {}) implementation to read _options.access and apply it to the
resulting preset (e.g., override the preset's access/tier field or merge into
the parsed YAML) before saving/activating the policy, ensuring callers like
onboard.ts (which calls policies.applyPreset(sandboxName, name, { access:
accessByName[name] })) and the UI flow triggered by selectTierPresetsAndAccess
will have their chosen access honored; alternatively, if access cannot be
applied here, remove/disable the access toggle in selectTierPresetsAndAccess,
but prefer implementing the override in applyPreset and propagate the merged
options through any functions that create or persist the preset.
In `@src/lib/tiers.ts`:
- Around line 27-31: listTiers currently calls fs.readFileSync(TIERS_FILE) and
YAML.parse without protection and will throw on a missing file or malformed
YAML; wrap the body of listTiers in a try/catch, handle fs errors (e.g., ENOENT)
and YAML parse errors from YAML.parse, and return an empty array (or a clear,
descriptive Error) instead of letting the exception propagate; include a helpful
message (or use the existing logger) that mentions TIERS_FILE and the parse
failure so callers during onboarding get a clear diagnostic.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: a4815ee8-dbbd-4a50-8064-6b32c7903dee
📒 Files selected for processing (4)
src/lib/model-prompts.tssrc/lib/onboard.tssrc/lib/policies.tssrc/lib/tiers.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/lib/model-prompts.ts
| return YAML.stringify(output); | ||
| } | ||
| function applyPreset(sandboxName, presetName) { | ||
| function applyPreset(sandboxName, presetName, _options = {}) { |
There was a problem hiding this comment.
The _options parameter is accepted but never used, making the access-mode UI non-functional.
onboard.ts calls policies.applyPreset(sandboxName, name, { access: accessByName[name] }) expecting the access mode to be honored. However, _options is completely ignored in the function body—presets are always applied with their YAML defaults regardless of what the user selects in the tier/access UI.
Either implement access-mode differentiation in the preset application logic, or remove the access toggle from selectTierPresetsAndAccess until the infrastructure supports it.
🔧 Stub implementation to acknowledge the parameter
function applyPreset(sandboxName, presetName, _options = {}) {
+ // TODO: Implement access-mode differentiation once preset infrastructure supports it.
+ // Currently _options.access is ignored; all presets apply with their YAML defaults.
+ // See: https://github.com/NVIDIA/NemoClaw/issues/XXX
+ const _access = _options.access || "read-write";
+
// Guard against truncated sandbox names — WSL can truncate hyphenated📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function applyPreset(sandboxName, presetName, _options = {}) { | |
| function applyPreset(sandboxName, presetName, _options = {}) { | |
| // TODO: Implement access-mode differentiation once preset infrastructure supports it. | |
| // Currently _options.access is ignored; all presets apply with their YAML defaults. | |
| // See: https://github.com/NVIDIA/NemoClaw/issues/XXX | |
| const _access = _options.access || "read-write"; | |
| // Guard against truncated sandbox names — WSL can truncate hyphenated |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/policies.ts` at line 222, applyPreset currently ignores the _options
parameter so UI-selected access modes are dropped; update the
applyPreset(sandboxName, presetName, _options = {}) implementation to read
_options.access and apply it to the resulting preset (e.g., override the
preset's access/tier field or merge into the parsed YAML) before
saving/activating the policy, ensuring callers like onboard.ts (which calls
policies.applyPreset(sandboxName, name, { access: accessByName[name] })) and the
UI flow triggered by selectTierPresetsAndAccess will have their chosen access
honored; alternatively, if access cannot be applied here, remove/disable the
access toggle in selectTierPresetsAndAccess, but prefer implementing the
override in applyPreset and propagate the merged options through any functions
that create or persist the preset.
| function listTiers() { | ||
| const content = fs.readFileSync(TIERS_FILE, "utf-8"); | ||
| const parsed = YAML.parse(content); | ||
| return parsed.tiers; | ||
| } |
There was a problem hiding this comment.
Add error handling for missing file or malformed YAML.
listTiers() will throw an uncaught exception if tiers.yaml is missing or contains invalid YAML. Consider wrapping in try/catch and returning an empty array or a descriptive error, especially since this runs during interactive onboarding where a clear message would help users diagnose misconfiguration.
🛡️ Suggested defensive handling
function listTiers() {
+ if (!fs.existsSync(TIERS_FILE)) {
+ console.error(` Tier definitions not found: ${TIERS_FILE}`);
+ return [];
+ }
- const content = fs.readFileSync(TIERS_FILE, "utf-8");
- const parsed = YAML.parse(content);
- return parsed.tiers;
+ try {
+ const content = fs.readFileSync(TIERS_FILE, "utf-8");
+ const parsed = YAML.parse(content);
+ return Array.isArray(parsed?.tiers) ? parsed.tiers : [];
+ } catch (err) {
+ console.error(` Failed to load tier definitions: ${err.message}`);
+ return [];
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function listTiers() { | |
| const content = fs.readFileSync(TIERS_FILE, "utf-8"); | |
| const parsed = YAML.parse(content); | |
| return parsed.tiers; | |
| } | |
| function listTiers() { | |
| if (!fs.existsSync(TIERS_FILE)) { | |
| console.error(` Tier definitions not found: ${TIERS_FILE}`); | |
| return []; | |
| } | |
| try { | |
| const content = fs.readFileSync(TIERS_FILE, "utf-8"); | |
| const parsed = YAML.parse(content); | |
| return Array.isArray(parsed?.tiers) ? parsed.tiers : []; | |
| } catch (err) { | |
| console.error(` Failed to load tier definitions: ${err.message}`); | |
| return []; | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/tiers.ts` around lines 27 - 31, listTiers currently calls
fs.readFileSync(TIERS_FILE) and YAML.parse without protection and will throw on
a missing file or malformed YAML; wrap the body of listTiers in a try/catch,
handle fs errors (e.g., ENOENT) and YAML parse errors from YAML.parse, and
return an empty array (or a clear, descriptive Error) instead of letting the
exception propagate; include a helpful message (or use the existing logger) that
mentions TIERS_FILE and the parse failure so callers during onboarding get a
clear diagnostic.
ericksoa
left a comment
There was a problem hiding this comment.
Merge conflicts resolved, CodeRabbit feedback addressed, interactive onboard TUI verified manually. All 1517 tests pass, typechecks clean, nightly E2E dispatched.
Satisfies the legacy-path guard which blocks edits to test/*.test.js files after the TS migration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@test/policy-tiers-onboard.test.ts`:
- Around line 20-35: The test helper runScript currently forces
NEMOCLAW_NON_INTERACTIVE=1 which prevents src/lib/onboard.ts from exercising the
new TUI key handling, so either (A) add a pty-backed interactive test that
spawns the onboarding script without NEMOCLAW_NON_INTERACTIVE and drives key
events (use a pty library like node-pty to send ↑/↓/Space/r/Enter to the child
process) to assert selector behavior, or (B) refactor the TUI selection logic in
src/lib/onboard.ts into a pure function (e.g., export a selector state
reducer/handleKey function) and write unit tests for that function directly
instead of relying on runScript; update test/policy-tiers-onboard.test.ts to
include one of these approaches and remove the forced non-interactive env for
that case so the new key handling is actually exercised.
In `@test/policy-tiers.test.ts`:
- Around line 74-85: The tests currently assert only containment/length for tier
presets which allows silent changes; instead, update the assertions to check
exact ordered name arrays against the canonical lists from the policy (use
tiers.getTier("balanced").presets.map(p => p.name) and the return value of
resolveTierPresets(...) where used) — replace the multiple expect(...).toContain
and length checks with a single strict equality assertion that the array equals
the exact ordered preset names defined in the YAML; apply the same replacement
pattern for the other affected blocks referenced (lines 93-98, 102-133, 137-143,
197-200) so each test asserts the full ordered membership for getTier() and
default resolveTierPresets() results.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 8d735d17-aec4-4df4-97e4-8da2e36d43ef
📒 Files selected for processing (2)
test/policy-tiers-onboard.test.tstest/policy-tiers.test.ts
| function runScript(scriptBody) { | ||
| const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-tier-onboard-")); | ||
| const scriptPath = path.join(tmpDir, "script.js"); | ||
| fs.writeFileSync(scriptPath, scriptBody); | ||
| const result = spawnSync(process.execPath, [scriptPath], { | ||
| cwd: repoRoot, | ||
| encoding: "utf-8", | ||
| env: { | ||
| ...process.env, | ||
| HOME: tmpDir, | ||
| NEMOCLAW_NON_INTERACTIVE: "1", | ||
| }, | ||
| timeout: 15000, | ||
| }); | ||
| fs.rmSync(tmpDir, { recursive: true, force: true }); | ||
| return result; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
These helpers lock the suite onto the non-interactive fast path.
Both helpers force NEMOCLAW_NON_INTERACTIVE=1, and src/lib/onboard.ts returns before any of the new TUI logic runs in that mode. That means none of these tests exercise the ↑/↓, Space, r, or Enter handling introduced by this PR. Please add at least one pty-backed interactive case, or extract the selector state transitions into a pure helper and test that directly.
Also applies to: 279-314
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/policy-tiers-onboard.test.ts` around lines 20 - 35, The test helper
runScript currently forces NEMOCLAW_NON_INTERACTIVE=1 which prevents
src/lib/onboard.ts from exercising the new TUI key handling, so either (A) add a
pty-backed interactive test that spawns the onboarding script without
NEMOCLAW_NON_INTERACTIVE and drives key events (use a pty library like node-pty
to send ↑/↓/Space/r/Enter to the child process) to assert selector behavior, or
(B) refactor the TUI selection logic in src/lib/onboard.ts into a pure function
(e.g., export a selector state reducer/handleKey function) and write unit tests
for that function directly instead of relying on runScript; update
test/policy-tiers-onboard.test.ts to include one of these approaches and remove
the forced non-interactive env for that case so the new key handling is actually
exercised.
| it("includes npm, pypi, huggingface, brew, and brave", () => { | ||
| const names = tiers.getTier("balanced").presets.map((p) => p.name); | ||
| expect(names).toContain("npm"); | ||
| expect(names).toContain("pypi"); | ||
| expect(names).toContain("huggingface"); | ||
| expect(names).toContain("brew"); | ||
| expect(names).toContain("brave"); | ||
| }); | ||
|
|
||
| it("has at least 5 presets", () => { | ||
| expect(tiers.getTier("balanced").presets.length).toBeGreaterThanOrEqual(5); | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Pin the exact preset allowlists in these contract tests.
These cases still pass if a tier silently gains extra presets, because they only assert containment, relative counts, or lengths. Since nemoclaw-blueprint/policies/tiers.yaml already defines exact memberships, assert the full ordered name lists for both getTier() and the default resolveTierPresets() results.
Also applies to: 93-98, 102-133, 137-143, 197-200
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/policy-tiers.test.ts` around lines 74 - 85, The tests currently assert
only containment/length for tier presets which allows silent changes; instead,
update the assertions to check exact ordered name arrays against the canonical
lists from the policy (use tiers.getTier("balanced").presets.map(p => p.name)
and the return value of resolveTierPresets(...) where used) — replace the
multiple expect(...).toContain and length checks with a single strict equality
assertion that the array equals the exact ordered preset names defined in the
YAML; apply the same replacement pattern for the other affected blocks
referenced (lines 93-98, 102-133, 137-143, 197-200) so each test asserts the
full ordered membership for getTier() and default resolveTierPresets() results.
d651a2a to
d4ebf13
Compare
The tier-based policy selector (#1753) defaults non-interactive onboard to the "balanced" tier, which excludes messaging presets (telegram, discord, slack). The messaging-providers E2E test needs the "open" tier to allow egress to api.telegram.org and discord.com. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…gressions (#1859) ## Summary - **messaging-providers-e2e**: The tier-based policy selector (#1753) defaults non-interactive onboard to `balanced`, which excludes messaging presets. Adds `NEMOCLAW_POLICY_TIER: "open"` to the workflow env. - **sandbox-survival-e2e**: SSH after gateway restart can fail because the sandbox SSH agent isn't immediately reachable when the gateway reports healthy (more noticeable with OpenClaw 2026.4.2). Adds a 30s retry loop for SSH connectivity. Also removes `cleanup_ssh()` from the failure path — deleting the SSH config was causing all subsequent phases to fail with "Can't open user config file", masking whether data actually persisted. ## Test plan - [ ] Nightly E2E messaging-providers job passes - [ ] Nightly E2E sandbox-survival job passes (or produces real diagnostics if data is actually lost) 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Nightly end-to-end test environment configuration updated to set the test policy tier. * **Tests** * End-to-end test flow now retries SSH connectivity multiple times and preserves connection data on failure, improving test stability and enabling richer diagnostic logs. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Summary - Document tier-based policy selector (Restricted/Balanced/Open) in commands, network policies, and customize-network-policy pages (from #1753) - Document configurable port overrides via environment variables (`NEMOCLAW_GATEWAY_PORT`, `NEMOCLAW_DASHBOARD_PORT`, `NEMOCLAW_VLLM_PORT`, `NEMOCLAW_OLLAMA_PORT`) (from #1645) - Document `nemoclaw <sandbox> skill install <path>` command (from #1845, #1856) - Document reserved sandbox name validation — CLI command collision check (from #1773) - Bump doc version switcher through 0.0.15 - Remove `--dangerously-skip-permissions` from onboard usage synopsis (docs-skip violation) - Regenerate agent skills from updated docs ## Test plan - [x] `make docs` builds without warnings - [x] All pre-commit hooks pass - [ ] Verify rendered pages in docs build output - [ ] Cross-references resolve correctly (`policy-tiers` anchor, `environment-variables` section) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…NVIDIA#1753) <!-- markdownlint-disable MD041 --> ## Summary Implement a three-tier (Restricted / Balanced / Open) policy preset selector for the NemoClaw onboarding wizard, with a combined preset selection and access-mode screen replacing the previous two-step flow. ## Related Issue <!-- Link to the issue: Fixes #NNN or Closes #NNN. Remove this section if none. --> ## Changes Tier selector: - Radio-button TUI (↑/↓, Space to select, Enter to confirm) - Green [✓]/dim [ ] buttons; green key hints, dim action labels - Defaults to Balanced Preset + access screen: - All available presets shown in one screen; tier defaults pre-checked - Space to include/exclude any preset, r to toggle read vs read-write - Orange [rw] badge signals write-capable access as a visual warning - Tier presets listed first, extras below Other changes: - Fix brave preset regression: credential-based suggestions now seed the interactive preset selector correctly - Update tiers.yaml: all presets read-write, add brave to Balanced, add jira/outlook to Open, fix discord access - Add defaultModelId support to promptCloudModel for env-var defaulting - Add 6 integration tests for selectTierPresetsAndAccess - Add scripts/dev-tier-selector.js smoke-test (not part of CI) ## Type of Change <!-- Check the one that applies. --> - [x] Code change for a new feature, bug fix, or refactor. - [ ] Code change with doc updates. - [ ] Doc only. Prose changes without code sample modifications. - [ ] Doc only. Includes code sample changes. ## Testing <!-- What testing was done? --> - [ ] `npx prek run --all-files` passes (or equivalently `make check`). - [ ] `npm test` passes. - [ ] `make docs` builds without warnings. (for doc-only changes) ## Checklist ### General - [x ] I have read and followed the [contributing guide](https://github.com/NVIDIA/NemoClaw/blob/main/CONTRIBUTING.md). - [ x] I have read and followed the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md). (for doc-only changes) ### Code Changes <!-- Skip if this is a doc-only PR. --> - [ ] Formatters applied — `npx prek run --all-files` auto-fixes formatting (or `make format` for targeted runs). - [ ] Tests added or updated for new or changed behavior. - [ ] No secrets, API keys, or credentials committed. - [ ] Doc pages updated for any user-facing behavior changes (new commands, changed defaults, new features, bug fixes that contradict existing docs). ### Doc Changes <!-- Skip if this PR has no doc changes. --> - [ ] Follows the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md). Try running the `nemoclaw-contributor-update-docs` agent skill to draft changes while complying with the style guide. For example, prompt your agent with "`/nemoclaw-contributor-update-docs` catch up the docs for the new changes I made in this PR." - [ ] New pages include SPDX license header and frontmatter, if creating a new page. - [ ] Cross-references and links verified. --- <!-- DCO sign-off (required by CI). Replace with your real name and email. --> Signed-off-by: Your Name <your-email@example.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Introduced policy tier selection system with three security tiers (restricted, balanced, open) * Added interactive preset selection with configurable per-preset access modes (read/read-write) * Added environment variable support for provider, endpoint URL, model, and policy tier configuration * **Enhancements** * Extended model prompting to support default model selection preferences <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Aaron Erickson <aerickson@nvidia.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: ColinM-sys <cmcdonough@50words.com>
…gressions (NVIDIA#1859) ## Summary - **messaging-providers-e2e**: The tier-based policy selector (NVIDIA#1753) defaults non-interactive onboard to `balanced`, which excludes messaging presets. Adds `NEMOCLAW_POLICY_TIER: "open"` to the workflow env. - **sandbox-survival-e2e**: SSH after gateway restart can fail because the sandbox SSH agent isn't immediately reachable when the gateway reports healthy (more noticeable with OpenClaw 2026.4.2). Adds a 30s retry loop for SSH connectivity. Also removes `cleanup_ssh()` from the failure path — deleting the SSH config was causing all subsequent phases to fail with "Can't open user config file", masking whether data actually persisted. ## Test plan - [ ] Nightly E2E messaging-providers job passes - [ ] Nightly E2E sandbox-survival job passes (or produces real diagnostics if data is actually lost) 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Nightly end-to-end test environment configuration updated to set the test policy tier. * **Tests** * End-to-end test flow now retries SSH connectivity multiple times and preserves connection data on failure, improving test stability and enabling richer diagnostic logs. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: ColinM-sys <cmcdonough@50words.com>
Summary
Implement a three-tier (Restricted / Balanced / Open) policy preset selector for the NemoClaw onboarding wizard, with a combined preset selection and access-mode screen replacing the previous two-step flow.
Related Issue
Changes
Tier selector:
Preset + access screen:
Other changes:
Type of Change
Testing
npx prek run --all-filespasses (or equivalentlymake check).npm testpasses.make docsbuilds without warnings. (for doc-only changes)Checklist
General
Code Changes
npx prek run --all-filesauto-fixes formatting (ormake formatfor targeted runs).Doc Changes
nemoclaw-contributor-update-docsagent skill to draft changes while complying with the style guide. For example, prompt your agent with "/nemoclaw-contributor-update-docscatch up the docs for the new changes I made in this PR."Signed-off-by: Your Name your-email@example.com
Summary by CodeRabbit
New Features
Enhancements