Skip to content

feat(credentials): add CLI subcommand to register provider credentials#5969

Merged
cv merged 18 commits into
mainfrom
feat/credentials-add
Jul 1, 2026
Merged

feat(credentials): add CLI subcommand to register provider credentials#5969
cv merged 18 commits into
mainfrom
feat/credentials-add

Conversation

@laitingsheng

@laitingsheng laitingsheng commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds nemoclaw credentials add <PROVIDER> --type <TYPE> so users can register OpenShell provider credentials from NemoClaw without dropping to the OpenShell CLI. Ships a Tavily Search provider profile alongside so --type tavily works out of the box.

Related Issue

Fixes #5965
Resolves #1720

Changes

  • New credentials add subcommand wrapping openshell provider create. Accepts --type, --credential KEY[=VALUE] (repeatable), --config K=V (repeatable), or --from-existing. Rejects messaging-bridge names and surfaces an already exists hint that points at credentials reset.
  • New provider profile nemoclaw-blueprint/provider-profiles/tavily.yaml (host api.tavily.com, bearer authorization, TAVILY_API_KEY).
  • TAVILY_API_KEY added to KNOWN_CREDENTIAL_ENV_KEYS so sanitization and rotation cover it.
  • Top-level credentials usage and the dispatch banner now show <list|add|reset>.
  • Tests: six CLI dispatch cases for the new subcommand, profile-shape assertions for Tavily, and a known-keys assertion.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification: command help and dispatch banner already explain the new subcommand; user-guide docs land in the follow-up that wires Tavily into the onboard web-search flow.
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification:
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Verification

  • PR description includes the DCO sign-off declaration and every commit appears as Verified in GitHub
  • Git hooks passed during commit and push, or npx prek run --from-ref main --to-ref HEAD passes
  • Targeted tests pass for changed behavior
  • Full npm test passes (broad runtime changes only)
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Tinson Lai tinsonl@nvidia.com

Summary by CodeRabbit

  • New Features
    • Added a Tavily Search provider profile (bearer auth) for the expected Tavily REST endpoint.
    • Introduced credentials add to register provider credentials/config (supports --from-existing and repeatable --credential/--config).
    • Added persisted “extra providers” that automatically attach on the next sandbox rebuild (with CLI visibility updates).
  • Bug Fixes
    • Strengthened secret detection/redaction for TAVILY_API_KEY and Tavily-style tokens.
    • Improved credentials reset cleanup and ensured provider-delete stderr is redacted.
  • Documentation
    • Updated command reference for $$nemoclaw credentials add <PROVIDER> (and nemohermes), plus Tavily quickstart guidance.
  • Tests
    • Expanded coverage for credentials add/reset, Tavily profile validation, extra-provider persistence, and secret redaction.

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a Tavily provider profile, a credentials:add CLI path, and extra-provider state wiring through registry, onboarding, and reset flows. Tests and docs expand for the new credential registration and redaction behavior.

Changes

credentials:add and Tavily provider support

Layer / File(s) Summary
Tavily profile and secret handling
nemoclaw-blueprint/provider-profiles/tavily.yaml, nemoclaw-blueprint/policies/presets/tavily.yaml, src/lib/credentials/store.ts, src/lib/security/redact.ts, src/lib/security/secret-patterns.ts, test/secret-redaction.test.ts, test/credentials.test.ts, test/deepagents-code-tui-startup-check.test.ts, test/langchain-deepagents-code-image.test.ts, test/e2e/e2e-cloud-experimental/checks/*, docs/get-started/quickstart-langchain-deepagents-code.mdx, src/lib/cli/public-display-defaults.ts, test/validate-blueprint.test.ts
Adds the Tavily provider profile and expands credential allowlists, redaction, token-prefix matching, validation, quickstart guidance, and public display ordering for TAVILY_API_KEY and tvly- values.
credentials:add command surface
src/commands/credentials/add.ts, src/commands/credentials.ts, src/lib/credentials/command-support.ts, docs/reference/commands.mdx, docs/reference/commands-nemohermes.mdx, test/cli/credentials-command.test.ts, test/package-contract/cli/credentials-cli-command.test.ts, src/commands/credentials.test.ts
Defines the new CLI command, updates help output, and extends docs and command-loading tests for the new subcommand.
credentials:add action flow
src/lib/actions/credentials-add.ts, test/cli/credentials-command.test.ts, test/package-contract/cli/credentials-cli-command.test.ts
Implements credential and config validation, gateway recovery, bundled profile import, provider creation, success reporting, and failure redaction for credentials:add, with matching behavioral tests.
extra-provider registry and onboarding
src/lib/actions/global.ts, src/lib/onboard.ts, src/lib/onboard/sandbox-create-plan.ts, src/lib/state/extra-providers.ts, src/lib/state/registry.ts, src/commands/credentials/reset.ts, test/registry.test.ts, test/onboard.test.ts, test/package-contract/cli/credentials-cli-command.test.ts
Adds persisted extra-provider state, threads it into sandbox creation, and removes local state during credential reset.

Sequence Diagram(s)

sequenceDiagram
  participant CredentialsAddCommand
  participant runCredentialsAddAction
  participant recordExtraProvider
  participant registry
  participant createSandbox
  CredentialsAddCommand->>runCredentialsAddAction: provider, type, credential/config inputs
  runCredentialsAddAction->>recordExtraProvider: record added provider
  recordExtraProvider->>registry: addExtraProvider(name)
  createSandbox->>registry: listExtraProviders()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

feature, area: providers

Suggested reviewers

  • cv
  • ericksoa
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly states the main change: adding a credentials subcommand to register provider credentials.
Linked Issues check ✅ Passed The PR satisfies #5965 and #1720 by adding credentials:add, Tavily profile support, and sandbox provider attachment.
Out of Scope Changes check ✅ Passed The code, docs, and tests all support credentials registration and sandbox provider attachment; no unrelated changes stand out.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/credentials-add

Comment @coderabbitai help to get the list of available commands.

@github-code-quality

github-code-quality Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in the branch is 96%. Coverage data for the branch is not yet available.

Show a code coverage summary of the most covered files.
File b435eb7 +/-
nemoclaw/src/se...cret-scanner.ts 100%
nemoclaw/src/commands/slash.ts 100%
nemoclaw/src/li...bprocess-env.ts 100%
nemoclaw/src/bl...eprint/state.ts 98%
nemoclaw/src/onboard/config.ts 98%
nemoclaw/src/bl...int/snapshot.ts 97%
nemoclaw/src/bl...print/runner.ts 95%
nemoclaw/src/co...ration-state.ts 94%
nemoclaw/src/bl...ate-networks.ts 94%
nemoclaw/src/index.ts 94%

TypeScript / code-coverage/cli

The overall coverage in the branch is 67%. Coverage data for the branch is not yet available.

Show a code coverage summary of the most covered files.
File b435eb7 +/-
src/lib/actions...dbox/rebuild.ts 80%
src/lib/actions...all/run-plan.ts 80%
src/lib/state/o...oard-session.ts 79%
src/lib/state/sandbox.ts 72%
src/lib/onboard/preflight.ts 69%
src/lib/onboard...er-gpu-patch.ts 59%
src/lib/actions...licy-channel.ts 58%
src/lib/policy/index.ts 53%
src/lib/shields/index.ts 51%
src/lib/onboard.ts 20%

Updated June 30, 2026 18:22 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: ubuntu-repo-cloud-langchain-deepagents-code, cloud-onboard, sandbox-rebuild, network-policy, credential-sanitization, credential-migration
Optional E2E: onboard-resume, onboard-repair, double-onboard

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • ubuntu-repo-cloud-langchain-deepagents-code (high): Required because this PR affects Deep Agents Code user flows, Tavily opt-in policy, and changed live check scripts for headless inference and TUI startup. This typed target runs the Deep Agents cloud onboarding plus terminal-agent and deepagents-code-policy suites, including the cloud-experimental Deep Agents checks.
  • cloud-onboard (high): Required because src/lib/onboard.ts and the sandbox create plan now attach extra gateway providers during hosted sandbox creation. A full hosted onboard verifies the installer/onboard/credential/gateway/sandbox path still works with real hosted inference.
  • sandbox-rebuild (high): Required because the new credentials add flow tells users to rebuild to attach the provider, and the create plan/registry extraProviders path can affect rebuild-time provider attachment and sandbox lifecycle behavior.
  • network-policy (medium): Required because the Tavily preset changes egress policy binary allowlists and endpoint behavior, which is a security boundary.
  • credential-sanitization (high): Required because the PR changes credential command behavior, credential env allowlists, redaction patterns, and secret-shaped Tavily token handling; this verifies real install/onboard does not expose credentials or secret artifacts in the sandbox.
  • credential-migration (high): Required because provider credential registration/listing/reset paths and known credential keys changed; this exercises real gateway credential registration/list behavior across a hosted onboard path.

Optional E2E

  • onboard-resume (medium): Optional confidence for adjacent onboarding state behavior. The special resume rule does not appear to be triggered because this PR does not change src/lib/onboard/machine resume orchestration/state transitions, but provider attachment changes are close to create/recreate paths.
  • onboard-repair (medium): Optional confidence for repair after interrupted onboarding or partial gateway/provider state; not merge-blocking under the resume rule for this PR.
  • double-onboard (medium): Optional adjacent coverage for idempotent onboarding when registry extraProviders and gateway provider state may already exist.

New E2E recommendations

  • credentials add provider injection (high): Existing E2E coverage exercises credential migration/sanitization and the Deep Agents Tavily policy opt-in, but there is no existing live test that runs nemoclaw credentials add tavily-search --type tavily --credential TAVILY_API_KEY, verifies the bundled provider profile import, rebuilds a sandbox, and confirms the extra provider is attached without exposing the raw key.
    • Suggested test: Add a live E2E for credentials-add-extra-provider-rebuild using a safe test provider/Tavily fixture: register provider from env, assert credentials list, rebuild, inspect openshell sandbox create/sandbox provider attachment evidence, and reset to ensure registry extraProviders cleanup.
  • credentials reset local state cleanup (medium): The reset command now treats already-absent gateway providers as local-state cleanup unless the argument looks like a known env key. Existing E2Es do not appear to validate this gateway-absent plus registry-extraProviders cleanup path.
    • Suggested test: Add an E2E or live-lite job that plants an extra provider in registry state, simulates or creates an absent gateway provider, runs credentials reset <provider> --yes, and verifies the provider is removed from local registry state without leaking stderr secrets.

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Vitest E2E Scenario Recommendation

Required Vitest E2E scenarios: ubuntu-repo-cloud-openclaw, diagnostics-vitest, credential-sanitization-vitest
Optional Vitest E2E scenarios: ubuntu-repo-cloud-langchain-deepagents-code, credential-migration-vitest

Dispatch required Vitest E2E scenarios:

  • gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw
  • gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=diagnostics-vitest
  • gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=credential-sanitization-vitest

Workflow run

Full Vitest E2E advisor summary

Vitest E2E Scenario Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required Vitest E2E scenarios

  • ubuntu-repo-cloud-openclaw: Core repo-current OpenClaw onboarding exercises the changed sandbox creation path, registry state, gateway/provider plumbing, and baseline credential-bearing sandbox lifecycle after adding extra provider attachment support.
    • Dispatch: gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw
  • diagnostics-vitest: The PR changes credentials reset/list support, gateway provider state cleanup, registry extra-provider removal, and redacted diagnostics; this live job directly covers gateway-backed credentials list/reset and redaction behavior against a real onboarded sandbox.
    • Dispatch: gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=diagnostics-vitest
  • credential-sanitization-vitest: The PR changes secret-pattern and redaction handling for Tavily-style credentials plus credential boundary code; this live job is the focused Vitest coverage for install/onboard credential sanitization and sandbox secret-boundary checks.
    • Dispatch: gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=credential-sanitization-vitest

Optional Vitest E2E scenarios

  • ubuntu-repo-cloud-langchain-deepagents-code: Adjacent coverage for the Deep Agents Code onboarding affected by the Tavily provider profile and documentation flow; useful because the new credential provider is primarily documented for the Deep Agents harness, but it is not the smallest proof of the generic credentials/onboarding code change.
    • Dispatch: gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-langchain-deepagents-code
  • credential-migration-vitest: Optional adjacent credential-store coverage because KNOWN_CREDENTIAL_ENV_KEYS changed to include Tavily; this job validates the legacy credential migration and gateway credential-list path, though it does not specifically exercise the new credentials add command.
    • Dispatch: gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=credential-migration-vitest

Relevant changed files

  • nemoclaw-blueprint/provider-profiles/tavily.yaml
  • src/commands/credentials.ts
  • src/commands/credentials/add.ts
  • src/commands/credentials/reset.ts
  • src/lib/actions/credentials-add.ts
  • src/lib/actions/global.ts
  • src/lib/credentials/command-support.ts
  • src/lib/credentials/store.ts
  • src/lib/onboard.ts
  • src/lib/onboard/sandbox-create-plan.ts
  • src/lib/security/redact.ts
  • src/lib/security/secret-patterns.ts
  • src/lib/state/registry.ts

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor (Nemotron Ultra) — Blocked

Merge posture: Do not merge until addressed
Primary next action: Fix PRA-5: Tavily provider profile grants Node.js interpreters to sandboxes; then add or justify PRA-T1.
Open items: 3 required · 18 warnings · 3 suggestions · 8 test follow-ups
Since last review: 1 prior item resolved · 15 still apply · 4 new items found

Action checklist

  • PRA-5 Fix: Tavily provider profile grants Node.js interpreters to sandboxes in nemoclaw-blueprint/provider-profiles/tavily.yaml:19
  • PRA-6 Fix: Tavily policy preset mirrors overbroad provider profile binaries in nemoclaw-blueprint/policies/presets/tavily.yaml:13
  • PRA-7 Fix: New Tavily provider profile introduces overbroad binary allowances in nemoclaw-blueprint/provider-profiles/tavily.yaml:1
  • PRA-1 Resolve or justify: Source-of-truth review needed: normalizeExtraProviders silent default for non-array input
  • PRA-2 Resolve or justify: Source-of-truth review needed: extraProviders cache without gateway reconciliation
  • PRA-3 Resolve or justify: Source-of-truth review needed: Hermes tool gateway provider deduplication gap
  • PRA-4 Resolve or justify: Source-of-truth review needed: TAVILY_API_KEY in KNOWN_CREDENTIAL_ENV_KEYS but not LEGACY_CREDENTIAL_ENV_ALIASES
  • PRA-8 Resolve or justify: Config values passed to OpenShell without shell metacharacter validation in src/lib/actions/credentials-add.ts:90
  • PRA-9 Resolve or justify: normalizeExtraProviders silently defaults non-array input to undefined in src/lib/state/extra-providers.ts:19
  • PRA-10 Resolve or justify: extraProviders cache never reconciled with gateway provider list in src/lib/state/registry.ts:557
  • PRA-11 Resolve or justify: Extra providers deduplication misses Hermes tool gateway provider in src/lib/onboard/sandbox-create-plan.ts:271
  • PRA-12 Resolve or justify: ensureBundledProviderProfile silent failure on missing profile in src/lib/actions/credentials-add.ts:51
  • PRA-13 Resolve or justify: credentials-add.ts placed in actions/ instead of credentials/ namespace in src/lib/actions/credentials-add.ts:1
  • PRA-T1 Add or justify test follow-up: Runtime validation
  • PRA-T2 Add or justify test follow-up: Runtime validation
  • PRA-T3 Add or justify test follow-up: Runtime validation
  • PRA-T4 Add or justify test follow-up: Runtime validation
  • PRA-T5 Add or justify test follow-up: Runtime validation
  • PRA-T6 Add or justify test follow-up: Blueprint validation test asserts overbroad Tavily binary list
  • PRA-T7 Add or justify test follow-up: Missing test for Hermes tool gateway provider deduplication
  • PRA-T8 Add or justify test follow-up: Repeated inline TAVILY_API_KEY test constants should use shared helper
  • PRA-22 In-scope improvement: Move credentials-add.ts to credentials/ namespace for consistency in src/lib/actions/credentials-add.ts:1
  • PRA-23 In-scope improvement: extraProviders registry field may be unnecessary — gateway could be single source of truth in src/lib/state/extra-providers.ts:1
  • PRA-24 In-scope improvement: Extract command display metadata to command files in src/lib/cli/public-display-defaults.ts:1

Findings index

ID Severity Category Location Required action
PRA-1 Resolve/justify architecture Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-2 Resolve/justify architecture Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-3 Resolve/justify architecture Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-4 Resolve/justify architecture Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-5 Required security nemoclaw-blueprint/provider-profiles/tavily.yaml:19 Restrict binaries to minimal set: only /usr/bin/curl and /usr/local/bin/curl. Remove all node entries. Align with principle of least privilege for a simple REST search API.
PRA-6 Required security nemoclaw-blueprint/policies/presets/tavily.yaml:13 Restrict policy preset binaries to minimal set matching corrected provider profile (likely only curl entries).
PRA-7 Required security nemoclaw-blueprint/provider-profiles/tavily.yaml:1 Fix tavily.yaml before merge — remove node binary entries, keep only curl.
PRA-8 Resolve/justify security src/lib/actions/credentials-add.ts:90 Add validation rejecting shell metacharacters in config values. Allowlist safe characters: alphanumeric, underscore, hyphen, dot, slash, colon, equals.
PRA-9 Resolve/justify architecture src/lib/state/extra-providers.ts:19 Add validation: if extraProviders exists but is not an array, log warning or throw. At minimum, document the silent default behavior in code comments.
PRA-10 Resolve/justify architecture src/lib/state/registry.ts:557 Add reconciliation: on sandbox create/rebuild, query gateway provider list and merge with extraProviders, or remove extraProviders persistence if gateway is authoritative.
PRA-11 Resolve/justify architecture src/lib/onboard/sandbox-create-plan.ts:271 Also filter extraProviders against getHermesToolGatewayProviderName(sandboxName) to avoid duplicate --provider flags.
PRA-12 Resolve/justify architecture src/lib/actions/credentials-add.ts:51 At minimum add debug log when profile missing or import skipped. Ideally throw on missing profile since installer should guarantee it.
PRA-13 Resolve/justify architecture src/lib/actions/credentials-add.ts:1 Move src/lib/actions/credentials-add.ts to src/lib/credentials/credentials-add.ts and update imports in src/commands/credentials/add.ts and src/lib/actions/global.ts.
PRA-14 Resolve/justify correctness src/lib/credentials/command-support.ts:48 No action needed. Close PRA-15 as false positive.
PRA-15 Resolve/justify tests test/validate-blueprint.test.ts:515 Change test to assert minimal binary list matching corrected tavily.yaml (e.g., only ['/usr/local/bin/curl', '/usr/bin/curl']).
PRA-16 Resolve/justify tests src/lib/onboard/sandbox-create-plan.test.ts:297 Add test case where extraProviders includes a name matching getHermesToolGatewayProviderName(sandboxName) to verify deduplication works.
PRA-17 Resolve/justify tests test/package-contract/cli/credentials-cli-command.test.ts:298 Extract to module-level constants (e.g., TEST_TAVILY_KEY = 'tvly-test-12345', LEAKED_TAVILY_KEY = 'tvly-leaked-secret-9999') and use beforeEach/afterEach for env var management.
PRA-18 Resolve/justify architecture test/package-contract/cli/credentials-cli-command.test.ts:1 Split test file by command into separate files (credentials-list.test.ts, credentials-add.test.ts, credentials-reset.test.ts) for maintainability.
PRA-19 Resolve/justify acceptance docs/get-started/quickstart-langchain-deepagents-code.mdx:87 Add cross-reference: 'See `nemoclaw credentials add --help` for all options.' after the credentials add example.
PRA-20 Resolve/justify architecture src/lib/state/registry.ts:255 Re-evaluate whether extraProviders registry field is necessary or if OpenShell gateway provider list should be single source of truth (PRA-21). If extraProviders stays, ensure registry.ts growth is justified or further extract.

🚨 Required before merge

Address these before merging unless a maintainer explicitly overrides the advisor with rationale.

PRA-5 Required — Tavily provider profile grants Node.js interpreters to sandboxes

  • Location: nemoclaw-blueprint/provider-profiles/tavily.yaml:19
  • Category: security
  • Problem: Tavily provider profile lists 4 binaries: 2×Node.js (/usr/local/bin/node, /usr/bin/node) and 2×curl. Tavily is a REST API client (api.tavily.com:443) needing only HTTPS/curl. This expands sandbox attack surface enabling arbitrary code execution if provider or its config is compromised.
  • Impact: Sandboxes with Tavily provider gain Node.js interpreters, violating least privilege. Attacker compromising Tavily provider or its config could execute arbitrary code in sandbox.
  • Required action: Restrict binaries to minimal set: only /usr/bin/curl and /usr/local/bin/curl. Remove all node entries. Align with principle of least privilege for a simple REST search API.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: diff nemoclaw-blueprint/provider-profiles/tavily.yaml nemoclaw-blueprint/provider-profiles/brave.yaml — compare binaries lists; Tavily should only need curl for HTTPS REST calls
  • Missing regression test: Blueprint validation test (test/validate-blueprint.test.ts:515-523) should assert minimal binary list (e.g., only ['/usr/local/bin/curl', '/usr/bin/curl']), similar to npm_registry test at validate-blueprint.test.ts:478-488. Current test asserts overbroad list with node and must be corrected.
  • Done when: The required change is committed and verification passes: diff nemoclaw-blueprint/provider-profiles/tavily.yaml nemoclaw-blueprint/provider-profiles/brave.yaml — compare binaries lists; Tavily should only need curl for HTTPS REST calls.
  • Evidence: tavily.yaml lines 19-24 show 4 binaries including node; brave.yaml has 4 (node×2, curl×2) but Tavily is a simpler REST search API requiring no interpreter

PRA-6 Required — Tavily policy preset mirrors overbroad provider profile binaries

  • Location: nemoclaw-blueprint/policies/presets/tavily.yaml:13
  • Category: security
  • Problem: Tavily policy preset duplicates the same 4-binary overbroad allowlist with node entries. The preset controls what binaries can make egress calls to api.tavily.com, and granting node interpreters here compounds the provider profile issue.
  • Impact: Policy preset independently grants node interpreters for Tavily egress, compounding the attack surface expansion from the provider profile.
  • Required action: Restrict policy preset binaries to minimal set matching corrected provider profile (likely only curl entries).
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: diff nemoclaw-blueprint/policies/presets/tavily.yaml nemoclaw-blueprint/policies/presets/brave.yaml — compare binaries lists after provider profile fix
  • Missing regression test: Blueprint validation test should assert tavily preset binaries match corrected provider profile (curl only).
  • Done when: The required change is committed and verification passes: diff nemoclaw-blueprint/policies/presets/tavily.yaml nemoclaw-blueprint/policies/presets/brave.yaml — compare binaries lists after provider profile fix.
  • Evidence: tavily.yaml preset lines 13-18 show 4 binaries including node; brave preset has 3 (node×2, curl×1)

PRA-7 Required — New Tavily provider profile introduces overbroad binary allowances

  • Location: nemoclaw-blueprint/provider-profiles/tavily.yaml:1
  • Category: security
  • Problem: The newly added tavily.yaml provider profile grants 2×node + 2×curl binaries for a REST-only search API, establishing an insecure default that violates least privilege from day one.
  • Impact: Any sandbox using the Tavily provider gets Node.js interpreters, enabling arbitrary code execution if the provider or its config is compromised.
  • Required action: Fix tavily.yaml before merge — remove node binary entries, keep only curl.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: diff nemoclaw-blueprint/provider-profiles/tavily.yaml nemoclaw-blueprint/provider-profiles/brave.yaml — compare binaries lists; Tavily should only need curl for HTTPS REST calls
  • Missing regression test: Blueprint validation test (test/validate-blueprint.test.ts:515-523) should assert minimal binary list (e.g., only ['/usr/local/bin/curl', '/usr/bin/curl']).
  • Done when: The required change is committed and verification passes: diff nemoclaw-blueprint/provider-profiles/tavily.yaml nemoclaw-blueprint/provider-profiles/brave.yaml — compare binaries lists; Tavily should only need curl for HTTPS REST calls.
  • Evidence: tavily.yaml lines 19-24 show 4 binaries including node; Tavily is a simpler REST search API requiring no interpreter
Review findings by urgency: 3 required fixes, 18 items to resolve/justify, 3 in-scope improvements

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

PRA-1 Resolve/justify — Source-of-truth review needed: normalizeExtraProviders silent default for non-array input

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Test: corrupted registry (extraProviders: "not-an-array") → load returns [] with warning log, not silent undefined
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: extra-providers.ts:15-19 returns undefined for non-array input without warning

PRA-2 Resolve/justify — Source-of-truth review needed: extraProviders cache without gateway reconciliation

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Test: gateway has provider not in registry extraProviders → rebuild attaches it; registry has provider not in gateway → rebuild doesn't duplicate
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: registry.ts load/save normalizeExtraProviders but no gateway query at create time; sandbox-create-plan.ts uses registry.listExtraProviders()

PRA-3 Resolve/justify — Source-of-truth review needed: Hermes tool gateway provider deduplication gap

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Test: extraProviders includes Hermes tool gateway provider name → not duplicated in createArgs (PRA-16)
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: sandbox-create-plan.ts:268 adds Hermes provider, then 271-275 adds extraProviders filtered only against messagingProviders

PRA-4 Resolve/justify — Source-of-truth review needed: TAVILY_API_KEY in KNOWN_CREDENTIAL_ENV_KEYS but not LEGACY_CREDENTIAL_ENV_ALIASES

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Test: legacy file with Tavily alias (if any) → migrates to process.env correctly
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: store.ts:43 includes TAVILY_API_KEY; store.ts:51 LEGACY_CREDENTIAL_ENV_ALIASES only has NVIDIA_INFERENCE_API_KEY

PRA-8 Resolve/justify — Config values passed to OpenShell without shell metacharacter validation

  • Location: src/lib/actions/credentials-add.ts:90
  • Category: security
  • Problem: Config values in `--config KEY=VALUE` are passed directly to `openshell provider create --config` without validating against shell metacharacters. An attacker controlling config input could inject shell metacharacters (e.g., `; rm -rf /`, `$(reboot)`, backticks) that may be interpreted by the shell when OpenShell processes the config.
  • Impact: Potential command injection via config values if OpenShell's provider create command processes config values through a shell context.
  • Recommended action: Add validation rejecting shell metacharacters in config values. Allowlist safe characters: alphanumeric, underscore, hyphen, dot, slash, colon, equals.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Test with `--config key='$(reboot)'` — should be rejected with clear error before reaching OpenShell CLI
  • Missing regression test: Unit test in test/cli/credentials-command.test.ts: credentials add rejects --config values containing shell metacharacters (; $ ` | & < > ( ) { } [ ] \ ' ")
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Test with `--config key='$(reboot)'` — should be rejected with clear error before reaching OpenShell CLI.
  • Evidence: credentials-add.ts lines 90-103 validate config key pattern and secret patterns but no shell metacharacter allowlist on the value

PRA-9 Resolve/justify — normalizeExtraProviders silently defaults non-array input to undefined

  • Location: src/lib/state/extra-providers.ts:19
  • Category: architecture
  • Problem: When registry extraProviders field is corrupted (non-array), normalizeExtraProviders returns undefined instead of validating or warning. This masks data corruption and could cause silent loss of provider attachments on rebuild.
  • Impact: Corrupted registry state fails silently; extra providers not attached on rebuild with no user-visible error.
  • Recommended action: Add validation: if extraProviders exists but is not an array, log warning or throw. At minimum, document the silent default behavior in code comments.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Manually corrupt sandboxes.json extraProviders to a string/object; run nemoclaw <sandbox> rebuild; observe silent loss of providers
  • Missing regression test: Test: corrupted registry (extraProviders: "not-an-array") → load returns [] not crash; logs warning
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Manually corrupt sandboxes.json extraProviders to a string/object; run nemoclaw <sandbox> rebuild; observe silent loss of providers.
  • Evidence: extra-providers.ts:15-19 returns undefined for non-array input without warning

PRA-10 Resolve/justify — extraProviders cache never reconciled with gateway provider list

  • Location: src/lib/state/registry.ts:557
  • Category: architecture
  • Problem: Registry persists extraProviders but never queries the OpenShell gateway to reconcile. If gateway has providers not in registry (or vice versa), sandbox rebuild attaches stale/missing providers.
  • Impact: Provider drift between registry and gateway causes missing or duplicate --provider flags on sandbox create/rebuild.
  • Recommended action: Add reconciliation: on sandbox create/rebuild, query gateway provider list and merge with extraProviders, or remove extraProviders persistence if gateway is authoritative.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Register provider via OpenShell CLI directly (bypassing NemoClaw); run nemoclaw <sandbox> rebuild; observe provider not attached
  • Missing regression test: Test: gateway has provider not in registry extraProviders → rebuild attaches it; registry has provider not in gateway → rebuild doesn't duplicate
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Register provider via OpenShell CLI directly (bypassing NemoClaw); run nemoclaw <sandbox> rebuild; observe provider not attached.
  • Evidence: registry.ts load/save normalizeExtraProviders but no gateway query at create time

PRA-11 Resolve/justify — Extra providers deduplication misses Hermes tool gateway provider

  • Location: src/lib/onboard/sandbox-create-plan.ts:271
  • Category: architecture
  • Problem: Extra providers are deduplicated against messagingProviders only, but not against the Hermes tool gateway provider name (getHermesToolGatewayProviderName). This can cause duplicate --provider flags for the same Hermes provider.
  • Impact: Duplicate --provider flags passed to openshell sandbox create; unclear if OpenShell handles gracefully or errors.
  • Recommended action: Also filter extraProviders against getHermesToolGatewayProviderName(sandboxName) to avoid duplicate --provider flags.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Add extraProviders entry matching '<sandbox>-hermes-tool-gateway'; run prepareSandboxCreatePlan; verify not duplicated in createArgs
  • Missing regression test: Test case where extraProviders includes a name matching getHermesToolGatewayProviderName(sandboxName) to verify deduplication works (PRA-16).
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Add extraProviders entry matching '<sandbox>-hermes-tool-gateway'; run prepareSandboxCreatePlan; verify not duplicated in createArgs.
  • Evidence: sandbox-create-plan.ts:268 adds Hermes provider, then 271-275 adds extraProviders filtered only against messagingProviders

PRA-12 Resolve/justify — ensureBundledProviderProfile silent failure on missing profile

  • Location: src/lib/actions/credentials-add.ts:51
  • Category: architecture
  • Problem: If bundled provider profile file doesn't exist (installer didn't ship it), ensureBundledProviderProfile returns null silently. No debug log, no error — provider creation proceeds without the profile, likely failing later with opaque error.
  • Impact: Missing provider profile fails silently; user gets confusing 'Could not register provider' error instead of actionable 'profile missing, update OpenShell' guidance.
  • Recommended action: At minimum add debug log when profile missing or import skipped. Ideally throw on missing profile since installer should guarantee it.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Remove tavily.yaml from provider-profiles; run credentials add --type tavily; observe silent skip vs actionable error
  • Missing regression test: Test: missing bundled profile → actionable error message mentioning 'Update OpenShell with scripts/install-openshell.sh'
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Remove tavily.yaml from provider-profiles; run credentials add --type tavily; observe silent skip vs actionable error.
  • Evidence: credentials-add.ts:51 returns null if !fs.existsSync(profilePath); no logging

PRA-13 Resolve/justify — credentials-add.ts placed in actions/ instead of credentials/ namespace

  • Location: src/lib/actions/credentials-add.ts:1
  • Category: architecture
  • Problem: The new credentials-add.ts action lives in src/lib/actions/ but logically belongs in src/lib/credentials/ alongside store.ts and command-support.ts. This creates namespace inconsistency and makes the module harder to find.
  • Impact: Code organization inconsistency; future maintainers may not find credentials-related actions in the expected location.
  • Recommended action: Move src/lib/actions/credentials-add.ts to src/lib/credentials/credentials-add.ts and update imports in src/commands/credentials/add.ts and src/lib/actions/global.ts.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -r 'credentials-add' src/ — should only reference src/lib/credentials/ after move
  • Missing regression test: None — this is a code organization fix; verify imports resolve after move
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -r 'credentials-add' src/ — should only reference src/lib/credentials/ after move.
  • Evidence: File at src/lib/actions/credentials-add.ts; imports from ../credentials/command-support and ../security/redact suggest credentials/ is the logical home

PRA-14 Resolve/justify — Unused 'kind' parameter in gateway recovery functions - FALSE POSITIVE

  • Location: src/lib/credentials/command-support.ts:48
  • Category: correctness
  • Problem: Previous review claimed 'kind' parameter is unused. However, kind is used in credentialsGatewayRecoveryFailureLines to differentiate 'query' vs 'reach' error messages, and is passed through recoverGatewayOrExit call sites.
  • Impact: None — the parameter IS used correctly. This finding from previous review is invalid.
  • Recommended action: No action needed. Close PRA-15 as false positive.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'kind' src/lib/credentials/command-support.ts — shows usage in credentialsGatewayRecoveryFailureLines and recoverGatewayOrExit
  • Missing regression test: N/A — false positive
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'kind' src/lib/credentials/command-support.ts — shows usage in credentialsGatewayRecoveryFailureLines and recoverGatewayOrExit.
  • Evidence: command-support.ts:32 uses kind to select 'query' vs 'reach' in error message; line 41 passes kind to credentialsGatewayRecoveryFailureLines

PRA-15 Resolve/justify — Blueprint validation test asserts overbroad Tavily binary list

  • Location: test/validate-blueprint.test.ts:515
  • Category: tests
  • Problem: test/validate-blueprint.test.ts asserts Tavily provider profile binaries include node entries (['/usr/local/bin/node', '/usr/bin/node', '/usr/local/bin/curl', '/usr/bin/curl']). This test must be updated when tavily.yaml is fixed to assert only curl binaries.
  • Impact: Test will fail after fixing tavily.yaml; currently passes incorrectly, giving false confidence in overbroad binary list.
  • Recommended action: Change test to assert minimal binary list matching corrected tavily.yaml (e.g., only ['/usr/local/bin/curl', '/usr/bin/curl']).
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Run test/validate-blueprint.test.ts after fixing tavily.yaml — should fail until test updated
  • Missing regression test: Test should assert profile.binaries equals ['/usr/local/bin/curl', '/usr/bin/curl'] (sorted), matching npm_registry test pattern at lines 478-488
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Run test/validate-blueprint.test.ts after fixing tavily.yaml — should fail until test updated.
  • Evidence: validate-blueprint.test.ts:515-523 asserts overbroad list with node

PRA-16 Resolve/justify — Missing test for Hermes tool gateway provider deduplication

  • Location: src/lib/onboard/sandbox-create-plan.test.ts:297
  • Category: tests
  • Problem: No test covers the case where extraProviders includes a name matching getHermesToolGatewayProviderName(sandboxName). Without this test, the deduplication gap (PRA-12) cannot be verified fixed.
  • Impact: Regression risk: Hermes provider duplication could be reintroduced without detection.
  • Recommended action: Add test case where extraProviders includes a name matching getHermesToolGatewayProviderName(sandboxName) to verify deduplication works.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Run sandbox-create-plan.test.ts with extraProviders: ['<sandbox>-hermes-tool-gateway', 'tavily-search']; verify createArgs has only one Hermes provider entry
  • Missing regression test: Test: extraProviders includes Hermes tool gateway provider name → not duplicated in createArgs
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Run sandbox-create-plan.test.ts with extraProviders: ['<sandbox>-hermes-tool-gateway', 'tavily-search']; verify createArgs has only one Hermes provider entry.
  • Evidence: sandbox-create-plan.test.ts has tests for messaging provider dedup but not Hermes dedup

PRA-17 Resolve/justify — Repeated inline TAVILY_API_KEY test constants should use shared helper

  • Location: test/package-contract/cli/credentials-cli-command.test.ts:298
  • Category: tests
  • Problem: Test file repeats 'tvly-test-12345', 'tvly-leaked-secret-9999', 'tvly-leaked-12345', 'tvly-secret-shaped-12345' inline across 7+ test cases. These should be module-level constants with beforeEach/afterEach for env var management.
  • Impact: Test maintenance burden; risk of inconsistent test values; harder to update secret patterns.
  • Recommended action: Extract to module-level constants (e.g., TEST_TAVILY_KEY = 'tvly-test-12345', LEAKED_TAVILY_KEY = 'tvly-leaked-secret-9999') and use beforeEach/afterEach for env var management.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'tvly-' test/package-contract/cli/credentials-cli-command.test.ts — shows 7+ inline occurrences
  • Missing regression test: Refactor only — no new test needed; verify all existing tests pass after extraction
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'tvly-' test/package-contract/cli/credentials-cli-command.test.ts — shows 7+ inline occurrences.
  • Evidence: Lines 294, 301, 306, 359, 395, 396, 431, 463, 464, 550, 565, 572, 579, 655, 701, 716, 723 contain inline tvly- values

PRA-18 Resolve/justify — Test file grew to 700+ lines covering all credentials subcommands

  • Location: test/package-contract/cli/credentials-cli-command.test.ts:1
  • Category: architecture
  • Problem: credentials-cli-command.test.ts now covers list, add, reset subcommands in a single 700+ line file. This reduces maintainability and makes it harder to run focused tests.
  • Impact: Slower test runs for unrelated changes; harder to navigate; higher chance of test interference.
  • Recommended action: Split test file by command into separate files (credentials-list.test.ts, credentials-add.test.ts, credentials-reset.test.ts) for maintainability.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: wc -l test/package-contract/cli/credentials-cli-command.test.ts — shows 700+ lines
  • Missing regression test: Refactor only — verify all tests pass after split
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: wc -l test/package-contract/cli/credentials-cli-command.test.ts — shows 700+ lines.
  • Evidence: File grew from ~200 to 700+ lines in this PR (per diff stat: +504 lines)

PRA-19 Resolve/justify — Quickstart missing cross-reference to credentials add --help

  • Location: docs/get-started/quickstart-langchain-deepagents-code.mdx:87
  • Category: acceptance
  • Problem: Quickstart shows 'nemo-deepagents credentials add tavily-search --type tavily --credential TAVILY_API_KEY' but doesn't reference 'credentials add --help' for all options.
  • Impact: Users may not discover --config, --from-existing, and other flags without checking help separately.
  • Recommended action: Add cross-reference: 'See `nemoclaw credentials add --help` for all options.' after the credentials add example.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read quickstart section 'Optional Web Search' — no 'see also' reference to credentials add --help
  • Missing regression test: Doc linting could verify cross-references; manual check sufficient
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read quickstart section 'Optional Web Search' — no 'see also' reference to credentials add --help.
  • Evidence: quickstart-langchain-deepagents-code.mdx lines 80-95 show example without help reference

PRA-20 Resolve/justify — Registry monolith grew +37 lines for extraProviders despite extraction

  • Location: src/lib/state/registry.ts:255
  • Category: architecture
  • Problem: src/lib/state/registry.ts grew by 37 lines (585→622) to add extraProviders persistence (listExtraProviders, addExtraProvider, removeExtraProvider). This continues monolith growth instead of extracting or offsetting.
  • Impact: Registry remains a large monolith; extraProviders logic adds to cognitive complexity without clear boundary.
  • Recommended action: Re-evaluate whether extraProviders registry field is necessary or if OpenShell gateway provider list should be single source of truth (PRA-21). If extraProviders stays, ensure registry.ts growth is justified or further extract.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: wc -l src/lib/state/registry.ts — 622 lines; grep -n 'extraProviders' shows 5 new functions
  • Missing regression test: Architecture review — no automated test; evaluate if gateway can be single source of truth
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: wc -l src/lib/state/registry.ts — 622 lines; grep -n 'extraProviders' shows 5 new functions.
  • Evidence: registry.ts added listExtraProviders, addExtraProvider, removeExtraProvider (+37 lines); monolith hotspot flagged in drift context

PRA-21 Resolve/justify — TAVILY_API_KEY added to KNOWN_CREDENTIAL_ENV_KEYS but not LEGACY_CREDENTIAL_ENV_ALIASES

  • Location: src/lib/credentials/store.ts:43
  • Category: architecture
  • Problem: TAVILY_API_KEY is in KNOWN_CREDENTIAL_ENV_KEYS for sanitization/rotation but missing from LEGACY_CREDENTIAL_ENV_ALIASES. If any legacy credential file used an alias for Tavily, it won't be migrated.
  • Impact: Legacy credentials migration (if Tavily ever had aliases) would skip Tavily keys, leaving them in legacy file.
  • Recommended action: Add TAVILY_API_KEY to LEGACY_CREDENTIAL_ENV_ALIASES if any legacy aliases exist; otherwise document why none needed.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check if any legacy credentials.json in wild contains Tavily aliases; grep LEGACY_CREDENTIAL_ENV_ALIASES in store.ts
  • Missing regression test: Test: legacy file with Tavily alias (if any) → migrates to process.env correctly
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check if any legacy credentials.json in wild contains Tavily aliases; grep LEGACY_CREDENTIAL_ENV_ALIASES in store.ts.
  • Evidence: store.ts:43 KNOWN_CREDENTIAL_ENV_KEYS includes TAVILY_API_KEY; store.ts:51 LEGACY_CREDENTIAL_ENV_ALIASES only has NVIDIA_INFERENCE_API_KEY

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

PRA-22 Improvement — Move credentials-add.ts to credentials/ namespace for consistency

  • Location: src/lib/actions/credentials-add.ts:1
  • Category: architecture
  • Problem: The new credentials-add.ts action lives in src/lib/actions/ but logically belongs in src/lib/credentials/ alongside store.ts and command-support.ts.
  • Impact: Code organization inconsistency; future maintainers may not find credentials-related actions in the expected location.
  • Suggested action: Move src/lib/actions/credentials-add.ts to src/lib/credentials/credentials-add.ts and update imports in src/commands/credentials/add.ts and src/lib/actions/global.ts.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: After move, grep -r 'from.*credentials-add' src/ — should reference src/lib/credentials/credentials-add
  • Missing regression test: Verify imports resolve and tests pass after move
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: File at src/lib/actions/credentials-add.ts; imports from ../credentials/command-support suggest credentials/ is logical home

PRA-23 Improvement — extraProviders registry field may be unnecessary — gateway could be single source of truth

  • Location: src/lib/state/extra-providers.ts:1
  • Category: architecture
  • Problem: The extraProviders registry field duplicates provider state that exists in the OpenShell gateway. If gateway reconciliation is added (PRA-2/PRA-11), this module and its persistence can be removed entirely.
  • Impact: Unnecessary state duplication; drift risk; extra code to maintain.
  • Suggested action: Evaluate removal: if gateway reconciliation is implemented, delete extra-providers.ts and related registry functions. If not, document why gateway cannot be queried at sandbox create time.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check if 'openshell provider list --names' returns all providers needed for sandbox attach; if yes, extraProviders is redundant
  • Missing regression test: If removed: test that sandbox rebuild attaches all gateway providers without registry extraProviders
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: extra-providers.ts (44 lines) + registry.ts extraProviders functions (37 lines) = 81 lines for state that exists in gateway

PRA-24 Improvement — Extract command display metadata to command files

  • Location: src/lib/cli/public-display-defaults.ts:1
  • Category: architecture
  • Problem: public-display-defaults.ts is a 567-line hotspot that centralizes command display metadata. This creates a monolith that must be updated for every new command. Oclif supports decorators or static fields on command classes for this metadata.
  • Impact: Monolith growth; every new command requires editing this central file; coupling between commands and display logic.
  • Suggested action: Extract command display metadata to command files using oclif decorators or static properties (description, flags, examples, groups).
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: wc -l src/lib/cli/public-display-defaults.ts — 567 lines; grep -c 'group:' shows many entries
  • Missing regression test: Verify 'nemoclaw --help' output unchanged after extraction
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: PR added 8 lines to this file for credentials:add display; monolith growth continues
Simplification opportunities: 3 possible cuts, net -581 lines possible

These are safe simplification checks only. Do not remove validation, security controls, data-loss prevention, or required tests.

  • PRA-22 shrink (src/lib/actions/credentials-add.ts:1): src/lib/actions/credentials-add.ts (210 lines in actions/)
    • Replacement: src/lib/credentials/credentials-add.ts (same content, updated imports)
    • Net: 0 lines
    • Safety boundary: Only file location and import paths change; no logic modification. All tests must pass after move.
  • PRA-23 yagni (src/lib/state/extra-providers.ts:1): src/lib/state/extra-providers.ts (44 lines) + registry.ts extraProviders functions (37 lines)
    • Replacement: Query OpenShell gateway provider list at sandbox create/rebuild time
    • Net: -81 lines
    • Safety boundary: Must verify gateway provider list includes all needed providers (inference, messaging, custom) before removal. Credential isolation boundary must not weaken.
  • PRA-24 shrink (src/lib/cli/public-display-defaults.ts:1): src/lib/cli/public-display-defaults.ts (567 lines) — move display metadata to command classes
    • Replacement: Oclif command decorators/static fields (description, summary, examples, flags, group, order)
    • Net: -500 lines
    • Safety boundary: Help output must remain identical; oclif must still render groups/descriptions correctly
Test follow-ups to resolve or justify

If these cover changed behavior, prefer adding them in this PR; otherwise state why existing coverage is enough or link the follow-up.

  • PRA-T1 Runtime validation — E2E test: credentials add tavily-search --type tavily --credential TAVILY_API_KEY → nemoclaw <sandbox> rebuild → dcode can reach api.tavily.com via gateway injection. Runtime/sandbox/infrastructure paths need behavioral runtime validation: Tavily credentials registration → rebuild → sandbox can reach api.tavily.com with injected cred; extraProviders attaches on rebuild; config shell metacharacter rejection; Hermes deduplication. Unit tests cover CLI parsing but not end-to-end sandbox behavior.
  • PRA-T2 Runtime validation — Integration test: credentials add then rebuild attaches provider to sandbox (verify openshell provider list shows it). Runtime/sandbox/infrastructure paths need behavioral runtime validation: Tavily credentials registration → rebuild → sandbox can reach api.tavily.com with injected cred; extraProviders attaches on rebuild; config shell metacharacter rejection; Hermes deduplication. Unit tests cover CLI parsing but not end-to-end sandbox behavior.
  • PRA-T3 Runtime validation — Unit test: extraProviders dedupes against Hermes tool gateway provider (PRA-16). Runtime/sandbox/infrastructure paths need behavioral runtime validation: Tavily credentials registration → rebuild → sandbox can reach api.tavily.com with injected cred; extraProviders attaches on rebuild; config shell metacharacter rejection; Hermes deduplication. Unit tests cover CLI parsing but not end-to-end sandbox behavior.
  • PRA-T4 Runtime validation — Unit test: credentials add rejects --config values containing shell metacharacters (; $ ` | & < > ( ) { } [ ] \ ' "). Runtime/sandbox/infrastructure paths need behavioral runtime validation: Tavily credentials registration → rebuild → sandbox can reach api.tavily.com with injected cred; extraProviders attaches on rebuild; config shell metacharacter rejection; Hermes deduplication. Unit tests cover CLI parsing but not end-to-end sandbox behavior.
  • PRA-T5 Runtime validation — Unit test: blueprint validation asserts minimal Tavily binaries ['/usr/local/bin/curl', '/usr/bin/curl'] (PRA-8). Runtime/sandbox/infrastructure paths need behavioral runtime validation: Tavily credentials registration → rebuild → sandbox can reach api.tavily.com with injected cred; extraProviders attaches on rebuild; config shell metacharacter rejection; Hermes deduplication. Unit tests cover CLI parsing but not end-to-end sandbox behavior.
  • PRA-T6 Blueprint validation test asserts overbroad Tavily binary list — Change test to assert minimal binary list matching corrected tavily.yaml (e.g., only ['/usr/local/bin/curl', '/usr/bin/curl']).
  • PRA-T7 Missing test for Hermes tool gateway provider deduplication — Add test case where extraProviders includes a name matching getHermesToolGatewayProviderName(sandboxName) to verify deduplication works.
  • PRA-T8 Repeated inline TAVILY_API_KEY test constants should use shared helper — Extract to module-level constants (e.g., TEST_TAVILY_KEY = 'tvly-test-12345', LEAKED_TAVILY_KEY = 'tvly-leaked-secret-9999') and use beforeEach/afterEach for env var management.
Since last review details

Current findings, using the urgency labels above:

PRA-1 Resolve/justify — Source-of-truth review needed: normalizeExtraProviders silent default for non-array input

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Test: corrupted registry (extraProviders: "not-an-array") → load returns [] with warning log, not silent undefined
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: extra-providers.ts:15-19 returns undefined for non-array input without warning

PRA-2 Resolve/justify — Source-of-truth review needed: extraProviders cache without gateway reconciliation

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Test: gateway has provider not in registry extraProviders → rebuild attaches it; registry has provider not in gateway → rebuild doesn't duplicate
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: registry.ts load/save normalizeExtraProviders but no gateway query at create time; sandbox-create-plan.ts uses registry.listExtraProviders()

PRA-3 Resolve/justify — Source-of-truth review needed: Hermes tool gateway provider deduplication gap

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Test: extraProviders includes Hermes tool gateway provider name → not duplicated in createArgs (PRA-16)
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: sandbox-create-plan.ts:268 adds Hermes provider, then 271-275 adds extraProviders filtered only against messagingProviders

PRA-4 Resolve/justify — Source-of-truth review needed: TAVILY_API_KEY in KNOWN_CREDENTIAL_ENV_KEYS but not LEGACY_CREDENTIAL_ENV_ALIASES

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Test: legacy file with Tavily alias (if any) → migrates to process.env correctly
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: store.ts:43 includes TAVILY_API_KEY; store.ts:51 LEGACY_CREDENTIAL_ENV_ALIASES only has NVIDIA_INFERENCE_API_KEY

PRA-5 Required — Tavily provider profile grants Node.js interpreters to sandboxes

  • Location: nemoclaw-blueprint/provider-profiles/tavily.yaml:19
  • Category: security
  • Problem: Tavily provider profile lists 4 binaries: 2×Node.js (/usr/local/bin/node, /usr/bin/node) and 2×curl. Tavily is a REST API client (api.tavily.com:443) needing only HTTPS/curl. This expands sandbox attack surface enabling arbitrary code execution if provider or its config is compromised.
  • Impact: Sandboxes with Tavily provider gain Node.js interpreters, violating least privilege. Attacker compromising Tavily provider or its config could execute arbitrary code in sandbox.
  • Required action: Restrict binaries to minimal set: only /usr/bin/curl and /usr/local/bin/curl. Remove all node entries. Align with principle of least privilege for a simple REST search API.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: diff nemoclaw-blueprint/provider-profiles/tavily.yaml nemoclaw-blueprint/provider-profiles/brave.yaml — compare binaries lists; Tavily should only need curl for HTTPS REST calls
  • Missing regression test: Blueprint validation test (test/validate-blueprint.test.ts:515-523) should assert minimal binary list (e.g., only ['/usr/local/bin/curl', '/usr/bin/curl']), similar to npm_registry test at validate-blueprint.test.ts:478-488. Current test asserts overbroad list with node and must be corrected.
  • Done when: The required change is committed and verification passes: diff nemoclaw-blueprint/provider-profiles/tavily.yaml nemoclaw-blueprint/provider-profiles/brave.yaml — compare binaries lists; Tavily should only need curl for HTTPS REST calls.
  • Evidence: tavily.yaml lines 19-24 show 4 binaries including node; brave.yaml has 4 (node×2, curl×2) but Tavily is a simpler REST search API requiring no interpreter

PRA-6 Required — Tavily policy preset mirrors overbroad provider profile binaries

  • Location: nemoclaw-blueprint/policies/presets/tavily.yaml:13
  • Category: security
  • Problem: Tavily policy preset duplicates the same 4-binary overbroad allowlist with node entries. The preset controls what binaries can make egress calls to api.tavily.com, and granting node interpreters here compounds the provider profile issue.
  • Impact: Policy preset independently grants node interpreters for Tavily egress, compounding the attack surface expansion from the provider profile.
  • Required action: Restrict policy preset binaries to minimal set matching corrected provider profile (likely only curl entries).
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: diff nemoclaw-blueprint/policies/presets/tavily.yaml nemoclaw-blueprint/policies/presets/brave.yaml — compare binaries lists after provider profile fix
  • Missing regression test: Blueprint validation test should assert tavily preset binaries match corrected provider profile (curl only).
  • Done when: The required change is committed and verification passes: diff nemoclaw-blueprint/policies/presets/tavily.yaml nemoclaw-blueprint/policies/presets/brave.yaml — compare binaries lists after provider profile fix.
  • Evidence: tavily.yaml preset lines 13-18 show 4 binaries including node; brave preset has 3 (node×2, curl×1)

PRA-7 Required — New Tavily provider profile introduces overbroad binary allowances

  • Location: nemoclaw-blueprint/provider-profiles/tavily.yaml:1
  • Category: security
  • Problem: The newly added tavily.yaml provider profile grants 2×node + 2×curl binaries for a REST-only search API, establishing an insecure default that violates least privilege from day one.
  • Impact: Any sandbox using the Tavily provider gets Node.js interpreters, enabling arbitrary code execution if the provider or its config is compromised.
  • Required action: Fix tavily.yaml before merge — remove node binary entries, keep only curl.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: diff nemoclaw-blueprint/provider-profiles/tavily.yaml nemoclaw-blueprint/provider-profiles/brave.yaml — compare binaries lists; Tavily should only need curl for HTTPS REST calls
  • Missing regression test: Blueprint validation test (test/validate-blueprint.test.ts:515-523) should assert minimal binary list (e.g., only ['/usr/local/bin/curl', '/usr/bin/curl']).
  • Done when: The required change is committed and verification passes: diff nemoclaw-blueprint/provider-profiles/tavily.yaml nemoclaw-blueprint/provider-profiles/brave.yaml — compare binaries lists; Tavily should only need curl for HTTPS REST calls.
  • Evidence: tavily.yaml lines 19-24 show 4 binaries including node; Tavily is a simpler REST search API requiring no interpreter

PRA-8 Resolve/justify — Config values passed to OpenShell without shell metacharacter validation

  • Location: src/lib/actions/credentials-add.ts:90
  • Category: security
  • Problem: Config values in `--config KEY=VALUE` are passed directly to `openshell provider create --config` without validating against shell metacharacters. An attacker controlling config input could inject shell metacharacters (e.g., `; rm -rf /`, `$(reboot)`, backticks) that may be interpreted by the shell when OpenShell processes the config.
  • Impact: Potential command injection via config values if OpenShell's provider create command processes config values through a shell context.
  • Recommended action: Add validation rejecting shell metacharacters in config values. Allowlist safe characters: alphanumeric, underscore, hyphen, dot, slash, colon, equals.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Test with `--config key='$(reboot)'` — should be rejected with clear error before reaching OpenShell CLI
  • Missing regression test: Unit test in test/cli/credentials-command.test.ts: credentials add rejects --config values containing shell metacharacters (; $ ` | & < > ( ) { } [ ] \ ' ")
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Test with `--config key='$(reboot)'` — should be rejected with clear error before reaching OpenShell CLI.
  • Evidence: credentials-add.ts lines 90-103 validate config key pattern and secret patterns but no shell metacharacter allowlist on the value

PRA-9 Resolve/justify — normalizeExtraProviders silently defaults non-array input to undefined

  • Location: src/lib/state/extra-providers.ts:19
  • Category: architecture
  • Problem: When registry extraProviders field is corrupted (non-array), normalizeExtraProviders returns undefined instead of validating or warning. This masks data corruption and could cause silent loss of provider attachments on rebuild.
  • Impact: Corrupted registry state fails silently; extra providers not attached on rebuild with no user-visible error.
  • Recommended action: Add validation: if extraProviders exists but is not an array, log warning or throw. At minimum, document the silent default behavior in code comments.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Manually corrupt sandboxes.json extraProviders to a string/object; run nemoclaw <sandbox> rebuild; observe silent loss of providers
  • Missing regression test: Test: corrupted registry (extraProviders: "not-an-array") → load returns [] not crash; logs warning
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Manually corrupt sandboxes.json extraProviders to a string/object; run nemoclaw <sandbox> rebuild; observe silent loss of providers.
  • Evidence: extra-providers.ts:15-19 returns undefined for non-array input without warning

PRA-10 Resolve/justify — extraProviders cache never reconciled with gateway provider list

  • Location: src/lib/state/registry.ts:557
  • Category: architecture
  • Problem: Registry persists extraProviders but never queries the OpenShell gateway to reconcile. If gateway has providers not in registry (or vice versa), sandbox rebuild attaches stale/missing providers.
  • Impact: Provider drift between registry and gateway causes missing or duplicate --provider flags on sandbox create/rebuild.
  • Recommended action: Add reconciliation: on sandbox create/rebuild, query gateway provider list and merge with extraProviders, or remove extraProviders persistence if gateway is authoritative.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Register provider via OpenShell CLI directly (bypassing NemoClaw); run nemoclaw <sandbox> rebuild; observe provider not attached
  • Missing regression test: Test: gateway has provider not in registry extraProviders → rebuild attaches it; registry has provider not in gateway → rebuild doesn't duplicate
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Register provider via OpenShell CLI directly (bypassing NemoClaw); run nemoclaw <sandbox> rebuild; observe provider not attached.
  • Evidence: registry.ts load/save normalizeExtraProviders but no gateway query at create time

PRA-11 Resolve/justify — Extra providers deduplication misses Hermes tool gateway provider

  • Location: src/lib/onboard/sandbox-create-plan.ts:271
  • Category: architecture
  • Problem: Extra providers are deduplicated against messagingProviders only, but not against the Hermes tool gateway provider name (getHermesToolGatewayProviderName). This can cause duplicate --provider flags for the same Hermes provider.
  • Impact: Duplicate --provider flags passed to openshell sandbox create; unclear if OpenShell handles gracefully or errors.
  • Recommended action: Also filter extraProviders against getHermesToolGatewayProviderName(sandboxName) to avoid duplicate --provider flags.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Add extraProviders entry matching '<sandbox>-hermes-tool-gateway'; run prepareSandboxCreatePlan; verify not duplicated in createArgs
  • Missing regression test: Test case where extraProviders includes a name matching getHermesToolGatewayProviderName(sandboxName) to verify deduplication works (PRA-16).
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Add extraProviders entry matching '<sandbox>-hermes-tool-gateway'; run prepareSandboxCreatePlan; verify not duplicated in createArgs.
  • Evidence: sandbox-create-plan.ts:268 adds Hermes provider, then 271-275 adds extraProviders filtered only against messagingProviders

PRA-12 Resolve/justify — ensureBundledProviderProfile silent failure on missing profile

  • Location: src/lib/actions/credentials-add.ts:51
  • Category: architecture
  • Problem: If bundled provider profile file doesn't exist (installer didn't ship it), ensureBundledProviderProfile returns null silently. No debug log, no error — provider creation proceeds without the profile, likely failing later with opaque error.
  • Impact: Missing provider profile fails silently; user gets confusing 'Could not register provider' error instead of actionable 'profile missing, update OpenShell' guidance.
  • Recommended action: At minimum add debug log when profile missing or import skipped. Ideally throw on missing profile since installer should guarantee it.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Remove tavily.yaml from provider-profiles; run credentials add --type tavily; observe silent skip vs actionable error
  • Missing regression test: Test: missing bundled profile → actionable error message mentioning 'Update OpenShell with scripts/install-openshell.sh'
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Remove tavily.yaml from provider-profiles; run credentials add --type tavily; observe silent skip vs actionable error.
  • Evidence: credentials-add.ts:51 returns null if !fs.existsSync(profilePath); no logging

PRA-13 Resolve/justify — credentials-add.ts placed in actions/ instead of credentials/ namespace

  • Location: src/lib/actions/credentials-add.ts:1
  • Category: architecture
  • Problem: The new credentials-add.ts action lives in src/lib/actions/ but logically belongs in src/lib/credentials/ alongside store.ts and command-support.ts. This creates namespace inconsistency and makes the module harder to find.
  • Impact: Code organization inconsistency; future maintainers may not find credentials-related actions in the expected location.
  • Recommended action: Move src/lib/actions/credentials-add.ts to src/lib/credentials/credentials-add.ts and update imports in src/commands/credentials/add.ts and src/lib/actions/global.ts.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -r 'credentials-add' src/ — should only reference src/lib/credentials/ after move
  • Missing regression test: None — this is a code organization fix; verify imports resolve after move
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -r 'credentials-add' src/ — should only reference src/lib/credentials/ after move.
  • Evidence: File at src/lib/actions/credentials-add.ts; imports from ../credentials/command-support and ../security/redact suggest credentials/ is the logical home

PRA-14 Resolve/justify — Unused 'kind' parameter in gateway recovery functions - FALSE POSITIVE

  • Location: src/lib/credentials/command-support.ts:48
  • Category: correctness
  • Problem: Previous review claimed 'kind' parameter is unused. However, kind is used in credentialsGatewayRecoveryFailureLines to differentiate 'query' vs 'reach' error messages, and is passed through recoverGatewayOrExit call sites.
  • Impact: None — the parameter IS used correctly. This finding from previous review is invalid.
  • Recommended action: No action needed. Close PRA-15 as false positive.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'kind' src/lib/credentials/command-support.ts — shows usage in credentialsGatewayRecoveryFailureLines and recoverGatewayOrExit
  • Missing regression test: N/A — false positive
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'kind' src/lib/credentials/command-support.ts — shows usage in credentialsGatewayRecoveryFailureLines and recoverGatewayOrExit.
  • Evidence: command-support.ts:32 uses kind to select 'query' vs 'reach' in error message; line 41 passes kind to credentialsGatewayRecoveryFailureLines

PRA-15 Resolve/justify — Blueprint validation test asserts overbroad Tavily binary list

  • Location: test/validate-blueprint.test.ts:515
  • Category: tests
  • Problem: test/validate-blueprint.test.ts asserts Tavily provider profile binaries include node entries (['/usr/local/bin/node', '/usr/bin/node', '/usr/local/bin/curl', '/usr/bin/curl']). This test must be updated when tavily.yaml is fixed to assert only curl binaries.
  • Impact: Test will fail after fixing tavily.yaml; currently passes incorrectly, giving false confidence in overbroad binary list.
  • Recommended action: Change test to assert minimal binary list matching corrected tavily.yaml (e.g., only ['/usr/local/bin/curl', '/usr/bin/curl']).
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Run test/validate-blueprint.test.ts after fixing tavily.yaml — should fail until test updated
  • Missing regression test: Test should assert profile.binaries equals ['/usr/local/bin/curl', '/usr/bin/curl'] (sorted), matching npm_registry test pattern at lines 478-488
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Run test/validate-blueprint.test.ts after fixing tavily.yaml — should fail until test updated.
  • Evidence: validate-blueprint.test.ts:515-523 asserts overbroad list with node

PRA-16 Resolve/justify — Missing test for Hermes tool gateway provider deduplication

  • Location: src/lib/onboard/sandbox-create-plan.test.ts:297
  • Category: tests
  • Problem: No test covers the case where extraProviders includes a name matching getHermesToolGatewayProviderName(sandboxName). Without this test, the deduplication gap (PRA-12) cannot be verified fixed.
  • Impact: Regression risk: Hermes provider duplication could be reintroduced without detection.
  • Recommended action: Add test case where extraProviders includes a name matching getHermesToolGatewayProviderName(sandboxName) to verify deduplication works.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Run sandbox-create-plan.test.ts with extraProviders: ['<sandbox>-hermes-tool-gateway', 'tavily-search']; verify createArgs has only one Hermes provider entry
  • Missing regression test: Test: extraProviders includes Hermes tool gateway provider name → not duplicated in createArgs
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Run sandbox-create-plan.test.ts with extraProviders: ['<sandbox>-hermes-tool-gateway', 'tavily-search']; verify createArgs has only one Hermes provider entry.
  • Evidence: sandbox-create-plan.test.ts has tests for messaging provider dedup but not Hermes dedup

PRA-17 Resolve/justify — Repeated inline TAVILY_API_KEY test constants should use shared helper

  • Location: test/package-contract/cli/credentials-cli-command.test.ts:298
  • Category: tests
  • Problem: Test file repeats 'tvly-test-12345', 'tvly-leaked-secret-9999', 'tvly-leaked-12345', 'tvly-secret-shaped-12345' inline across 7+ test cases. These should be module-level constants with beforeEach/afterEach for env var management.
  • Impact: Test maintenance burden; risk of inconsistent test values; harder to update secret patterns.
  • Recommended action: Extract to module-level constants (e.g., TEST_TAVILY_KEY = 'tvly-test-12345', LEAKED_TAVILY_KEY = 'tvly-leaked-secret-9999') and use beforeEach/afterEach for env var management.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'tvly-' test/package-contract/cli/credentials-cli-command.test.ts — shows 7+ inline occurrences
  • Missing regression test: Refactor only — no new test needed; verify all existing tests pass after extraction
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'tvly-' test/package-contract/cli/credentials-cli-command.test.ts — shows 7+ inline occurrences.
  • Evidence: Lines 294, 301, 306, 359, 395, 396, 431, 463, 464, 550, 565, 572, 579, 655, 701, 716, 723 contain inline tvly- values

PRA-18 Resolve/justify — Test file grew to 700+ lines covering all credentials subcommands

  • Location: test/package-contract/cli/credentials-cli-command.test.ts:1
  • Category: architecture
  • Problem: credentials-cli-command.test.ts now covers list, add, reset subcommands in a single 700+ line file. This reduces maintainability and makes it harder to run focused tests.
  • Impact: Slower test runs for unrelated changes; harder to navigate; higher chance of test interference.
  • Recommended action: Split test file by command into separate files (credentials-list.test.ts, credentials-add.test.ts, credentials-reset.test.ts) for maintainability.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: wc -l test/package-contract/cli/credentials-cli-command.test.ts — shows 700+ lines
  • Missing regression test: Refactor only — verify all tests pass after split
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: wc -l test/package-contract/cli/credentials-cli-command.test.ts — shows 700+ lines.
  • Evidence: File grew from ~200 to 700+ lines in this PR (per diff stat: +504 lines)

PRA-19 Resolve/justify — Quickstart missing cross-reference to credentials add --help

  • Location: docs/get-started/quickstart-langchain-deepagents-code.mdx:87
  • Category: acceptance
  • Problem: Quickstart shows 'nemo-deepagents credentials add tavily-search --type tavily --credential TAVILY_API_KEY' but doesn't reference 'credentials add --help' for all options.
  • Impact: Users may not discover --config, --from-existing, and other flags without checking help separately.
  • Recommended action: Add cross-reference: 'See `nemoclaw credentials add --help` for all options.' after the credentials add example.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read quickstart section 'Optional Web Search' — no 'see also' reference to credentials add --help
  • Missing regression test: Doc linting could verify cross-references; manual check sufficient
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read quickstart section 'Optional Web Search' — no 'see also' reference to credentials add --help.
  • Evidence: quickstart-langchain-deepagents-code.mdx lines 80-95 show example without help reference

PRA-20 Resolve/justify — Registry monolith grew +37 lines for extraProviders despite extraction

  • Location: src/lib/state/registry.ts:255
  • Category: architecture
  • Problem: src/lib/state/registry.ts grew by 37 lines (585→622) to add extraProviders persistence (listExtraProviders, addExtraProvider, removeExtraProvider). This continues monolith growth instead of extracting or offsetting.
  • Impact: Registry remains a large monolith; extraProviders logic adds to cognitive complexity without clear boundary.
  • Recommended action: Re-evaluate whether extraProviders registry field is necessary or if OpenShell gateway provider list should be single source of truth (PRA-21). If extraProviders stays, ensure registry.ts growth is justified or further extract.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: wc -l src/lib/state/registry.ts — 622 lines; grep -n 'extraProviders' shows 5 new functions
  • Missing regression test: Architecture review — no automated test; evaluate if gateway can be single source of truth
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: wc -l src/lib/state/registry.ts — 622 lines; grep -n 'extraProviders' shows 5 new functions.
  • Evidence: registry.ts added listExtraProviders, addExtraProvider, removeExtraProvider (+37 lines); monolith hotspot flagged in drift context

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — Changes requested

Merge posture: Do not merge yet
Primary next action: Fix PRA-5: Tavily policy/profile still omit the Python runtime used by Deep Agents Code; then add or justify PRA-T1.
Open items: 1 required · 9 warnings · 0 suggestions · 8 test follow-ups
Since last review: 0 prior items resolved · 6 still apply · 0 new items found

Action checklist

  • PRA-5 Fix: Tavily policy/profile still omit the Python runtime used by Deep Agents Code in nemoclaw-blueprint/policies/presets/tavily.yaml:19
  • PRA-1 Resolve or justify: Source-of-truth review needed: Durable provider attachment for credentials add
  • PRA-2 Resolve or justify: Source-of-truth review needed: Rebuild-based provider attachment instead of sandbox-scoped attach
  • PRA-3 Resolve or justify: Source-of-truth review needed: Tolerant normalization of extraProviders registry state
  • PRA-4 Resolve or justify: Source-of-truth review needed: Tavily provider profile and policy as credential-injection source of truth
  • PRA-6 Resolve or justify: Provider create success can still hide failed durable attachment recording in src/lib/actions/credentials-add.ts:189
  • PRA-7 Resolve or justify: Extra providers are replayed globally instead of being scoped to a sandbox or gateway binding in src/lib/onboard.ts:2963
  • PRA-8 Resolve or justify: Malformed extraProviders registry state is silently dropped without a tested recovery contract in src/lib/state/extra-providers.ts:15
  • PRA-9 Resolve or justify: Bundled provider profile import already-exists tolerance is unpinned in src/lib/actions/credentials-add.ts:64
  • PRA-10 Resolve or justify: Runtime validation is still needed for Tavily provider attachment and secret isolation
  • PRA-T1 Add or justify test follow-up: Runtime validation
  • PRA-T2 Add or justify test follow-up: Runtime validation
  • PRA-T3 Add or justify test follow-up: Runtime validation
  • PRA-T4 Add or justify test follow-up: Runtime validation
  • PRA-T5 Add or justify test follow-up: Runtime validation
  • PRA-T6 Add or justify test follow-up: Bundled provider profile import already-exists tolerance is unpinned
  • PRA-T7 Add or justify test follow-up: Runtime validation is still needed for Tavily provider attachment and secret isolation
  • PRA-T8 Add or justify test follow-up: Acceptance clause

Findings index

ID Severity Category Location Required action
PRA-1 Resolve/justify architecture Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-2 Resolve/justify architecture Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-3 Resolve/justify architecture Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-4 Resolve/justify architecture Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-5 Required acceptance nemoclaw-blueprint/policies/presets/tavily.yaml:19 Make the Tavily preset and provider profile match the actual runtime boundary. If DCode Tavily calls are Python, restore the narrow Python allowlist needed for that path, preferably `/opt/venv/bin/python3*` unless system Python is required. Remove node/curl unless repository-owned evidence proves Tavily uses them. If node/curl are intentionally the new source of truth, update the quickstart and tests to prove that runtime change.
PRA-6 Resolve/justify correctness src/lib/actions/credentials-add.ts:189 Treat a false `recordExtraProvider(provider)` as a command failure unless the provider was already durably recorded. For a newly created provider whose durable record fails, either best-effort delete the OpenShell provider and report rollback status, or return a non-zero result explaining that provider creation succeeded but durable attachment recording failed.
PRA-7 Resolve/justify security src/lib/onboard.ts:2963 Prefer scoping extra-provider intent to the selected sandbox or to the gateway binding that owns the provider. If the global replay is the accepted interim behavior until OpenShell exposes post-create attach/detach, add an explicit repository-owned contract test and user-facing note covering multi-sandbox and gateway-port behavior, including reset as the supported scoping control.
PRA-8 Resolve/justify architecture src/lib/state/extra-providers.ts:15 Either fail closed on a non-empty malformed `extraProviders` field, or explicitly document and test the tolerant contract: malformed entries are dropped, valid entries are preserved, saved registry state normalizes on write, and invalid names never reach sandbox create args.
PRA-9 Resolve/justify tests src/lib/actions/credentials-add.ts:64 Add a local package-contract test for `provider profile import` returning non-zero with an `already exists` diagnostic and assert `provider create` still runs. If OpenShell exposes a precise idempotent exit code or diagnostic shape, narrow the tolerance to that contract.
PRA-10 Resolve/justify tests Add or identify a repository-owned runtime/integration validation independent of external job status. It should register a Tavily provider with an env-name-only key, rebuild a Deep Agents Code sandbox with the Tavily preset, verify the provider is attached, verify Python/DCode Tavily traffic can reach `api.tavily.com`, verify raw `tvly-` material is absent from sandbox env/files/logs, and verify reset removes durable replay.

🚨 Required before merge

Address these before merging unless a maintainer explicitly overrides the advisor with rationale.

PRA-5 Required — Tavily policy/profile still omit the Python runtime used by Deep Agents Code

  • Location: nemoclaw-blueprint/policies/presets/tavily.yaml:19
  • Category: acceptance
  • Problem: The Tavily preset and new provider profile allow only node and curl binaries, but the repository still identifies Deep Agents Code Tavily traffic as Python-based: the quickstart says OpenShell attributes harness calls to sandbox `python3`, the Deep Agents requirements lock contains `tavily-python`, and the agent policy comments describe Python module traffic attribution. This leaves the linked Tavily acceptance path only statically wired, not usable by the actual DCode client.
  • Impact: Users can successfully run `credentials add` and rebuild, yet DCode's Python Tavily client may still be denied egress or miss credential injection. That leaves issue [Linux][CLI&UX] nemoclaw credentials has no add/set command — no path to register Tavily API key, blocking dcode web search entirely #5965 unresolved and may push users back toward unsafe in-sandbox `.env`/environment-secret workarounds.
  • Required action: Make the Tavily preset and provider profile match the actual runtime boundary. If DCode Tavily calls are Python, restore the narrow Python allowlist needed for that path, preferably `/opt/venv/bin/python3*` unless system Python is required. Remove node/curl unless repository-owned evidence proves Tavily uses them. If node/curl are intentionally the new source of truth, update the quickstart and tests to prove that runtime change.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Read `nemoclaw-blueprint/policies/presets/tavily.yaml`, `nemoclaw-blueprint/provider-profiles/tavily.yaml`, `docs/get-started/quickstart-langchain-deepagents-code.mdx` around Optional Web Search, and `agents/langchain-deepagents-code/requirements.lock` for `tavily-python`; confirm the binary allowlist matches the documented and packaged Tavily execution path.
  • Missing regression test: Update `test/tavily-preset.test.ts` and `test/validate-blueprint.test.ts` to pin the Tavily preset/profile binary allowlist to the actual Deep Agents Code Tavily runtime; add an integration validation that DCode/Python can reach `api.tavily.com` only with the Tavily preset and provider attached.
  • Done when: The required change is committed and verification passes: Read `nemoclaw-blueprint/policies/presets/tavily.yaml`, `nemoclaw-blueprint/provider-profiles/tavily.yaml`, `docs/get-started/quickstart-langchain-deepagents-code.mdx` around Optional Web Search, and `agents/langchain-deepagents-code/requirements.lock` for `tavily-python`; confirm the binary allowlist matches the documented and packaged Tavily execution path.
  • Evidence: `tavily.yaml` lists `/usr/local/bin/node`, `/usr/bin/node`, `/usr/local/bin/curl`, and `/usr/bin/curl`; `provider-profiles/tavily.yaml` mirrors the same list. The quickstart line says calls are attributed to sandbox `python3`, and `agents/langchain-deepagents-code/requirements.lock` includes `tavily-python`.
Review findings by urgency: 1 required fix, 9 items to resolve/justify, 0 in-scope improvements

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

PRA-1 Resolve/justify — Source-of-truth review needed: Durable provider attachment for credentials add

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Missing: a command test where provider create succeeds and `recordExtraProvider` returns false should fail or roll back instead of printing success.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: `src/lib/actions/credentials-add.ts` calls `recordExtraProvider(provider)` after provider create and ignores its return value.

PRA-2 Resolve/justify — Source-of-truth review needed: Rebuild-based provider attachment instead of sandbox-scoped attach

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Missing: multi-sandbox and gateway-port tests that define exactly where extra providers replay and how reset removes them.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: `registry.listExtraProviders()` is passed into every sandbox create plan without sandbox name or gateway binding filters.

PRA-3 Resolve/justify — Source-of-truth review needed: Tolerant normalization of extraProviders registry state

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Missing: malformed non-array and mixed valid/invalid array tests for `extraProviders` load/save behavior.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: `normalizeExtraProviders()` returns `undefined` for non-arrays and filters invalid values without diagnostics.

PRA-4 Resolve/justify — Source-of-truth review needed: Tavily provider profile and policy as credential-injection source of truth

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as missing.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Missing: a static and runtime test proving the allowed binaries correspond to the real DCode Tavily client.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: Quickstart text and agent policy comments identify Python attribution; requirements include `tavily-python`; Tavily YAML files allow node/curl only.

PRA-6 Resolve/justify — Provider create success can still hide failed durable attachment recording

  • Location: src/lib/actions/credentials-add.ts:189
  • Category: correctness
  • Problem: `runCredentialsAddAction()` calls `recordExtraProvider(provider)` after a successful OpenShell `provider create`, but it ignores the boolean return value. A registry write failure or validation mismatch can therefore produce a success message that tells the user to rebuild even though NemoClaw did not record the provider for future sandbox create args.
  • Impact: The gateway can hold a secret-bearing provider while NemoClaw's durable replay state is missing it. Rebuilds will not attach the provider as documented, and the user receives a false success signal in a credentials lifecycle path.
  • Recommended action: Treat a false `recordExtraProvider(provider)` as a command failure unless the provider was already durably recorded. For a newly created provider whose durable record fails, either best-effort delete the OpenShell provider and report rollback status, or return a non-zero result explaining that provider creation succeeded but durable attachment recording failed.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect `src/lib/actions/credentials-add.ts` around the success branch and confirm whether the return value of `recordExtraProvider(provider)` affects `exitCode` or rollback.
  • Missing regression test: Add a package-contract test where `provider create` returns status 0 and the injected `recordExtraProvider` hook returns false; assert `credentials add` does not print the normal success/rebuild instructions and either rolls back with `provider delete` or exits non-zero with the durable-recording failure.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect `src/lib/actions/credentials-add.ts` around the success branch and confirm whether the return value of `recordExtraProvider(provider)` affects `exitCode` or rollback.
  • Evidence: The success branch is `if (result.status === 0) { recordExtraProvider(provider); return ok([...]) }`; existing tests cover gateway rejection not recording, but not record failure after gateway success.

PRA-7 Resolve/justify — Extra providers are replayed globally instead of being scoped to a sandbox or gateway binding

  • Location: src/lib/onboard.ts:2963
  • Category: security
  • Problem: The new `extraProviders` state is stored once at the top level of `sandboxes.json` and fed into every later sandbox create plan via `registry.listExtraProviders()`. The docs disclose that registered providers attach to every sandbox built or rebuilt after the call, but the implementation still broadens credential availability beyond the target sandbox.
  • Impact: A provider registered for one sandbox can be attached to unrelated later sandboxes on the same registry/gateway, weakening least privilege for non-inference credentials such as Tavily or other third-party service keys.
  • Recommended action: Prefer scoping extra-provider intent to the selected sandbox or to the gateway binding that owns the provider. If the global replay is the accepted interim behavior until OpenShell exposes post-create attach/detach, add an explicit repository-owned contract test and user-facing note covering multi-sandbox and gateway-port behavior, including reset as the supported scoping control.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Trace `registry.listExtraProviders()` in `src/lib/onboard.ts` to `prepareSandboxCreatePlan()` and confirm no sandbox name or gateway binding filters the replayed providers.
  • Missing regression test: Add a test that creates or simulates two sandboxes on one gateway after `credentials add`, proves which sandbox create args receive the extra provider, and proves `credentials reset <provider> --yes` removes it from subsequent create args; add a separate case for different gateway ports if top-level state remains global.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Trace `registry.listExtraProviders()` in `src/lib/onboard.ts` to `prepareSandboxCreatePlan()` and confirm no sandbox name or gateway binding filters the replayed providers.
  • Evidence: `src/lib/state/registry.ts` adds top-level `extraProviders?: string[]`; `src/lib/onboard.ts` passes `extraProviders: registry.listExtraProviders()` without sandbox/gateway scope; command reference says providers attach to every sandbox built or rebuilt after the call.

PRA-8 Resolve/justify — Malformed extraProviders registry state is silently dropped without a tested recovery contract

  • Location: src/lib/state/extra-providers.ts:15
  • Category: architecture
  • Problem: `normalizeExtraProviders()` returns `undefined` for non-arrays and filters invalid entries out of arrays without surfacing a warning or failing closed. The add/remove paths validate names, but the load path can silently erase corrupt or hand-edited state.
  • Impact: A malformed `extraProviders` field can make provider attachment disappear during a registry read/write cycle with no diagnostic, causing credential replay to fail in a security-sensitive lifecycle path and making corruption hard to diagnose.
  • Recommended action: Either fail closed on a non-empty malformed `extraProviders` field, or explicitly document and test the tolerant contract: malformed entries are dropped, valid entries are preserved, saved registry state normalizes on write, and invalid names never reach sandbox create args.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read `src/lib/state/extra-providers.ts` and `test/registry.test.ts`; confirm whether malformed non-array values and mixed valid/invalid arrays have explicit expected behavior.
  • Missing regression test: Add registry tests for `extraProviders: 'bad'`, `extraProviders: ['tavily-search', '../bad', '', 'custom-provider']`, and a subsequent save/load; assert the chosen contract either fails closed or preserves valid names while normalizing invalid entries predictably.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read `src/lib/state/extra-providers.ts` and `test/registry.test.ts`; confirm whether malformed non-array values and mixed valid/invalid arrays have explicit expected behavior.
  • Evidence: Current registry tests cover empty list, add/remove, and round-trip only. `normalizeExtraProviders()` silently returns `undefined` for non-arrays and filters invalid array values.

PRA-9 Resolve/justify — Bundled provider profile import already-exists tolerance is unpinned

  • Location: src/lib/actions/credentials-add.ts:64
  • Category: tests
  • Problem: The bundled provider profile import path treats any non-zero diagnostic containing `already exists` as success. That may be the intended OpenShell idempotency contract, but the changed tests do not pin this branch, and the regex is broad enough to mask incompatible-profile or schema-drift diagnostics that also include that phrase.
  • Impact: NemoClaw could proceed to `provider create` using a stale or incompatible OpenShell provider profile while reporting the Tavily flow as healthy, undermining credential injection source-of-truth guarantees.
  • Recommended action: Add a local package-contract test for `provider profile import` returning non-zero with an `already exists` diagnostic and assert `provider create` still runs. If OpenShell exposes a precise idempotent exit code or diagnostic shape, narrow the tolerance to that contract.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Search `test/package-contract/cli/credentials-cli-command.test.ts` for a profile-import non-zero `already exists` case; the existing tests cover profile import success and failure, but not this tolerated non-zero branch.
  • Missing regression test: Add a test where the first OpenShell call is `provider profile import --file <tavily.yaml>` returning `{status: 1, stderr: 'profile tavily already exists'}` and assert the second call is `provider create --name tavily-search --type tavily --credential TAVILY_API_KEY` with diagnostics redacted.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Search `test/package-contract/cli/credentials-cli-command.test.ts` for a profile-import non-zero `already exists` case; the existing tests cover profile import success and failure, but not this tolerated non-zero branch.
  • Evidence: `ensureBundledProviderProfile()` returns null for `/already exists/i` on the combined stdout/stderr diagnostic; no matching test case was found in the package-contract test file.

PRA-10 Resolve/justify — Runtime validation is still needed for Tavily provider attachment and secret isolation

  • Location: not file-specific
  • Category: tests
  • Problem: The PR adds extensive static and mocked coverage for CLI validation, YAML shape, redaction, registry persistence, and sandbox create args, but the critical behavior crosses the OpenShell gateway, network policy, provider profile, sandbox rebuild, and DCode runtime boundary.
  • Impact: Static tests can pass while the actual sandbox still cannot use Tavily, attaches the provider incorrectly, or leaks raw `tvly-` material into env/config/logs/files. This is the main acceptance and security boundary for issues [Linux][CLI&UX] nemoclaw credentials has no add/set command — no path to register Tavily API key, blocking dcode web search entirely #5965 and feat(cli): support attaching arbitrary OpenShell providers for non-inference credentials #1720.
  • Recommended action: Add or identify a repository-owned runtime/integration validation independent of external job status. It should register a Tavily provider with an env-name-only key, rebuild a Deep Agents Code sandbox with the Tavily preset, verify the provider is attached, verify Python/DCode Tavily traffic can reach `api.tavily.com`, verify raw `tvly-` material is absent from sandbox env/files/logs, and verify reset removes durable replay.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Compare changed tests in `test/package-contract/cli/credentials-cli-command.test.ts`, `test/tavily-preset.test.ts`, `test/validate-blueprint.test.ts`, and e2e shell checks; confirm whether any repository-owned test exercises a real rebuilt sandbox with an attached Tavily provider.
  • Missing regression test: Add a behavior-specific integration test named along the lines of `credentials add Tavily attaches on Deep Agents rebuild without exposing raw tvly secret`, plus a negative case `credentials reset Tavily removes extra-provider replay from subsequent rebuilds`.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Compare changed tests in `test/package-contract/cli/credentials-cli-command.test.ts`, `test/tavily-preset.test.ts`, `test/validate-blueprint.test.ts`, and e2e shell checks; confirm whether any repository-owned test exercises a real rebuilt sandbox with an attached Tavily provider.
  • Evidence: Existing tests assert CLI arg shape, redaction patterns, YAML fields, and create-plan `--provider` ordering. None prove live OpenShell provider attachment, credential injection, or in-sandbox absence of the raw Tavily key.

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

  • None.
Test follow-ups to resolve or justify

If these cover changed behavior, prefer adding them in this PR; otherwise state why existing coverage is enough or link the follow-up.

  • PRA-T1 Runtime validation — credentials add Tavily attaches on Deep Agents rebuild without exposing raw tvly secret. The changed behavior crosses credentials, OpenShell provider profiles, sandbox create/rebuild, network policy, and Deep Agents Code runtime boundaries. Static and mocked tests cover validation and arg shape, but not the live provider attachment and secret-isolation contract.
  • PRA-T2 Runtime validation — Deep Agents Code Python Tavily client reaches api.tavily.com only after tavily policy preset and provider attachment. The changed behavior crosses credentials, OpenShell provider profiles, sandbox create/rebuild, network policy, and Deep Agents Code runtime boundaries. Static and mocked tests cover validation and arg shape, but not the live provider attachment and secret-isolation contract.
  • PRA-T3 Runtime validation — credentials reset Tavily removes extra-provider replay from subsequent sandbox rebuilds. The changed behavior crosses credentials, OpenShell provider profiles, sandbox create/rebuild, network policy, and Deep Agents Code runtime boundaries. Static and mocked tests cover validation and arg shape, but not the live provider attachment and secret-isolation contract.
  • PRA-T4 Runtime validation — credentials add fails or rolls back when provider create succeeds but durable extra-provider recording returns false. The changed behavior crosses credentials, OpenShell provider profiles, sandbox create/rebuild, network policy, and Deep Agents Code runtime boundaries. Static and mocked tests cover validation and arg shape, but not the live provider attachment and secret-isolation contract.
  • PRA-T5 Runtime validation — provider profile import non-zero already-exists diagnostic still proceeds to provider create with redacted diagnostics. The changed behavior crosses credentials, OpenShell provider profiles, sandbox create/rebuild, network policy, and Deep Agents Code runtime boundaries. Static and mocked tests cover validation and arg shape, but not the live provider attachment and secret-isolation contract.
  • PRA-T6 Bundled provider profile import already-exists tolerance is unpinned — Add a local package-contract test for `provider profile import` returning non-zero with an `already exists` diagnostic and assert `provider create` still runs. If OpenShell exposes a precise idempotent exit code or diagnostic shape, narrow the tolerance to that contract.
  • PRA-T7 Runtime validation is still needed for Tavily provider attachment and secret isolation — Add or identify a repository-owned runtime/integration validation independent of external job status. It should register a Tavily provider with an env-name-only key, rebuild a Deep Agents Code sandbox with the Tavily preset, verify the provider is attached, verify Python/DCode Tavily traffic can reach `api.tavily.com`, verify raw `tvly-` material is absent from sandbox env/files/logs, and verify reset removes durable replay.
  • PRA-T8 Acceptance clause[Linux][CLI&UX] nemoclaw credentials has no add/set command — no path to register Tavily API key, blocking dcode web search entirely #5965: "`dcode` (deepagents-code) blocks any API key that appears in environment variables or `/sandbox/.deepagents/.env`, directing users to use `nemoclaw credentials` to register provider keys." — add test evidence or identify existing coverage. The PR adds `credentials add` and Tavily redaction/known-key handling, but it does not add a runtime proof that the new credentials path is accepted by DCode without raw in-sandbox env or `.env` secrets.
Since last review details

Current findings, using the urgency labels above:

PRA-1 Resolve/justify — Source-of-truth review needed: Durable provider attachment for credentials add

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Missing: a command test where provider create succeeds and `recordExtraProvider` returns false should fail or roll back instead of printing success.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: `src/lib/actions/credentials-add.ts` calls `recordExtraProvider(provider)` after provider create and ignores its return value.

PRA-2 Resolve/justify — Source-of-truth review needed: Rebuild-based provider attachment instead of sandbox-scoped attach

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Missing: multi-sandbox and gateway-port tests that define exactly where extra providers replay and how reset removes them.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: `registry.listExtraProviders()` is passed into every sandbox create plan without sandbox name or gateway binding filters.

PRA-3 Resolve/justify — Source-of-truth review needed: Tolerant normalization of extraProviders registry state

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Missing: malformed non-array and mixed valid/invalid array tests for `extraProviders` load/save behavior.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: `normalizeExtraProviders()` returns `undefined` for non-arrays and filters invalid values without diagnostics.

PRA-4 Resolve/justify — Source-of-truth review needed: Tavily provider profile and policy as credential-injection source of truth

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as missing.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Missing: a static and runtime test proving the allowed binaries correspond to the real DCode Tavily client.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: Quickstart text and agent policy comments identify Python attribution; requirements include `tavily-python`; Tavily YAML files allow node/curl only.

PRA-5 Required — Tavily policy/profile still omit the Python runtime used by Deep Agents Code

  • Location: nemoclaw-blueprint/policies/presets/tavily.yaml:19
  • Category: acceptance
  • Problem: The Tavily preset and new provider profile allow only node and curl binaries, but the repository still identifies Deep Agents Code Tavily traffic as Python-based: the quickstart says OpenShell attributes harness calls to sandbox `python3`, the Deep Agents requirements lock contains `tavily-python`, and the agent policy comments describe Python module traffic attribution. This leaves the linked Tavily acceptance path only statically wired, not usable by the actual DCode client.
  • Impact: Users can successfully run `credentials add` and rebuild, yet DCode's Python Tavily client may still be denied egress or miss credential injection. That leaves issue [Linux][CLI&UX] nemoclaw credentials has no add/set command — no path to register Tavily API key, blocking dcode web search entirely #5965 unresolved and may push users back toward unsafe in-sandbox `.env`/environment-secret workarounds.
  • Required action: Make the Tavily preset and provider profile match the actual runtime boundary. If DCode Tavily calls are Python, restore the narrow Python allowlist needed for that path, preferably `/opt/venv/bin/python3*` unless system Python is required. Remove node/curl unless repository-owned evidence proves Tavily uses them. If node/curl are intentionally the new source of truth, update the quickstart and tests to prove that runtime change.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Read `nemoclaw-blueprint/policies/presets/tavily.yaml`, `nemoclaw-blueprint/provider-profiles/tavily.yaml`, `docs/get-started/quickstart-langchain-deepagents-code.mdx` around Optional Web Search, and `agents/langchain-deepagents-code/requirements.lock` for `tavily-python`; confirm the binary allowlist matches the documented and packaged Tavily execution path.
  • Missing regression test: Update `test/tavily-preset.test.ts` and `test/validate-blueprint.test.ts` to pin the Tavily preset/profile binary allowlist to the actual Deep Agents Code Tavily runtime; add an integration validation that DCode/Python can reach `api.tavily.com` only with the Tavily preset and provider attached.
  • Done when: The required change is committed and verification passes: Read `nemoclaw-blueprint/policies/presets/tavily.yaml`, `nemoclaw-blueprint/provider-profiles/tavily.yaml`, `docs/get-started/quickstart-langchain-deepagents-code.mdx` around Optional Web Search, and `agents/langchain-deepagents-code/requirements.lock` for `tavily-python`; confirm the binary allowlist matches the documented and packaged Tavily execution path.
  • Evidence: `tavily.yaml` lists `/usr/local/bin/node`, `/usr/bin/node`, `/usr/local/bin/curl`, and `/usr/bin/curl`; `provider-profiles/tavily.yaml` mirrors the same list. The quickstart line says calls are attributed to sandbox `python3`, and `agents/langchain-deepagents-code/requirements.lock` includes `tavily-python`.

PRA-6 Resolve/justify — Provider create success can still hide failed durable attachment recording

  • Location: src/lib/actions/credentials-add.ts:189
  • Category: correctness
  • Problem: `runCredentialsAddAction()` calls `recordExtraProvider(provider)` after a successful OpenShell `provider create`, but it ignores the boolean return value. A registry write failure or validation mismatch can therefore produce a success message that tells the user to rebuild even though NemoClaw did not record the provider for future sandbox create args.
  • Impact: The gateway can hold a secret-bearing provider while NemoClaw's durable replay state is missing it. Rebuilds will not attach the provider as documented, and the user receives a false success signal in a credentials lifecycle path.
  • Recommended action: Treat a false `recordExtraProvider(provider)` as a command failure unless the provider was already durably recorded. For a newly created provider whose durable record fails, either best-effort delete the OpenShell provider and report rollback status, or return a non-zero result explaining that provider creation succeeded but durable attachment recording failed.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect `src/lib/actions/credentials-add.ts` around the success branch and confirm whether the return value of `recordExtraProvider(provider)` affects `exitCode` or rollback.
  • Missing regression test: Add a package-contract test where `provider create` returns status 0 and the injected `recordExtraProvider` hook returns false; assert `credentials add` does not print the normal success/rebuild instructions and either rolls back with `provider delete` or exits non-zero with the durable-recording failure.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect `src/lib/actions/credentials-add.ts` around the success branch and confirm whether the return value of `recordExtraProvider(provider)` affects `exitCode` or rollback.
  • Evidence: The success branch is `if (result.status === 0) { recordExtraProvider(provider); return ok([...]) }`; existing tests cover gateway rejection not recording, but not record failure after gateway success.

PRA-7 Resolve/justify — Extra providers are replayed globally instead of being scoped to a sandbox or gateway binding

  • Location: src/lib/onboard.ts:2963
  • Category: security
  • Problem: The new `extraProviders` state is stored once at the top level of `sandboxes.json` and fed into every later sandbox create plan via `registry.listExtraProviders()`. The docs disclose that registered providers attach to every sandbox built or rebuilt after the call, but the implementation still broadens credential availability beyond the target sandbox.
  • Impact: A provider registered for one sandbox can be attached to unrelated later sandboxes on the same registry/gateway, weakening least privilege for non-inference credentials such as Tavily or other third-party service keys.
  • Recommended action: Prefer scoping extra-provider intent to the selected sandbox or to the gateway binding that owns the provider. If the global replay is the accepted interim behavior until OpenShell exposes post-create attach/detach, add an explicit repository-owned contract test and user-facing note covering multi-sandbox and gateway-port behavior, including reset as the supported scoping control.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Trace `registry.listExtraProviders()` in `src/lib/onboard.ts` to `prepareSandboxCreatePlan()` and confirm no sandbox name or gateway binding filters the replayed providers.
  • Missing regression test: Add a test that creates or simulates two sandboxes on one gateway after `credentials add`, proves which sandbox create args receive the extra provider, and proves `credentials reset <provider> --yes` removes it from subsequent create args; add a separate case for different gateway ports if top-level state remains global.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Trace `registry.listExtraProviders()` in `src/lib/onboard.ts` to `prepareSandboxCreatePlan()` and confirm no sandbox name or gateway binding filters the replayed providers.
  • Evidence: `src/lib/state/registry.ts` adds top-level `extraProviders?: string[]`; `src/lib/onboard.ts` passes `extraProviders: registry.listExtraProviders()` without sandbox/gateway scope; command reference says providers attach to every sandbox built or rebuilt after the call.

PRA-8 Resolve/justify — Malformed extraProviders registry state is silently dropped without a tested recovery contract

  • Location: src/lib/state/extra-providers.ts:15
  • Category: architecture
  • Problem: `normalizeExtraProviders()` returns `undefined` for non-arrays and filters invalid entries out of arrays without surfacing a warning or failing closed. The add/remove paths validate names, but the load path can silently erase corrupt or hand-edited state.
  • Impact: A malformed `extraProviders` field can make provider attachment disappear during a registry read/write cycle with no diagnostic, causing credential replay to fail in a security-sensitive lifecycle path and making corruption hard to diagnose.
  • Recommended action: Either fail closed on a non-empty malformed `extraProviders` field, or explicitly document and test the tolerant contract: malformed entries are dropped, valid entries are preserved, saved registry state normalizes on write, and invalid names never reach sandbox create args.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read `src/lib/state/extra-providers.ts` and `test/registry.test.ts`; confirm whether malformed non-array values and mixed valid/invalid arrays have explicit expected behavior.
  • Missing regression test: Add registry tests for `extraProviders: 'bad'`, `extraProviders: ['tavily-search', '../bad', '', 'custom-provider']`, and a subsequent save/load; assert the chosen contract either fails closed or preserves valid names while normalizing invalid entries predictably.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read `src/lib/state/extra-providers.ts` and `test/registry.test.ts`; confirm whether malformed non-array values and mixed valid/invalid arrays have explicit expected behavior.
  • Evidence: Current registry tests cover empty list, add/remove, and round-trip only. `normalizeExtraProviders()` silently returns `undefined` for non-arrays and filters invalid array values.

PRA-9 Resolve/justify — Bundled provider profile import already-exists tolerance is unpinned

  • Location: src/lib/actions/credentials-add.ts:64
  • Category: tests
  • Problem: The bundled provider profile import path treats any non-zero diagnostic containing `already exists` as success. That may be the intended OpenShell idempotency contract, but the changed tests do not pin this branch, and the regex is broad enough to mask incompatible-profile or schema-drift diagnostics that also include that phrase.
  • Impact: NemoClaw could proceed to `provider create` using a stale or incompatible OpenShell provider profile while reporting the Tavily flow as healthy, undermining credential injection source-of-truth guarantees.
  • Recommended action: Add a local package-contract test for `provider profile import` returning non-zero with an `already exists` diagnostic and assert `provider create` still runs. If OpenShell exposes a precise idempotent exit code or diagnostic shape, narrow the tolerance to that contract.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Search `test/package-contract/cli/credentials-cli-command.test.ts` for a profile-import non-zero `already exists` case; the existing tests cover profile import success and failure, but not this tolerated non-zero branch.
  • Missing regression test: Add a test where the first OpenShell call is `provider profile import --file <tavily.yaml>` returning `{status: 1, stderr: 'profile tavily already exists'}` and assert the second call is `provider create --name tavily-search --type tavily --credential TAVILY_API_KEY` with diagnostics redacted.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Search `test/package-contract/cli/credentials-cli-command.test.ts` for a profile-import non-zero `already exists` case; the existing tests cover profile import success and failure, but not this tolerated non-zero branch.
  • Evidence: `ensureBundledProviderProfile()` returns null for `/already exists/i` on the combined stdout/stderr diagnostic; no matching test case was found in the package-contract test file.

PRA-10 Resolve/justify — Runtime validation is still needed for Tavily provider attachment and secret isolation

  • Location: not file-specific
  • Category: tests
  • Problem: The PR adds extensive static and mocked coverage for CLI validation, YAML shape, redaction, registry persistence, and sandbox create args, but the critical behavior crosses the OpenShell gateway, network policy, provider profile, sandbox rebuild, and DCode runtime boundary.
  • Impact: Static tests can pass while the actual sandbox still cannot use Tavily, attaches the provider incorrectly, or leaks raw `tvly-` material into env/config/logs/files. This is the main acceptance and security boundary for issues [Linux][CLI&UX] nemoclaw credentials has no add/set command — no path to register Tavily API key, blocking dcode web search entirely #5965 and feat(cli): support attaching arbitrary OpenShell providers for non-inference credentials #1720.
  • Recommended action: Add or identify a repository-owned runtime/integration validation independent of external job status. It should register a Tavily provider with an env-name-only key, rebuild a Deep Agents Code sandbox with the Tavily preset, verify the provider is attached, verify Python/DCode Tavily traffic can reach `api.tavily.com`, verify raw `tvly-` material is absent from sandbox env/files/logs, and verify reset removes durable replay.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Compare changed tests in `test/package-contract/cli/credentials-cli-command.test.ts`, `test/tavily-preset.test.ts`, `test/validate-blueprint.test.ts`, and e2e shell checks; confirm whether any repository-owned test exercises a real rebuilt sandbox with an attached Tavily provider.
  • Missing regression test: Add a behavior-specific integration test named along the lines of `credentials add Tavily attaches on Deep Agents rebuild without exposing raw tvly secret`, plus a negative case `credentials reset Tavily removes extra-provider replay from subsequent rebuilds`.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Compare changed tests in `test/package-contract/cli/credentials-cli-command.test.ts`, `test/tavily-preset.test.ts`, `test/validate-blueprint.test.ts`, and e2e shell checks; confirm whether any repository-owned test exercises a real rebuilt sandbox with an attached Tavily provider.
  • Evidence: Existing tests assert CLI arg shape, redaction patterns, YAML fields, and create-plan `--provider` ordering. None prove live OpenShell provider attachment, credential injection, or in-sandbox absence of the raw Tavily key.

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
test/cli/credentials-command.test.ts (2)

36-47: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid pinning these tests to Oclif’s exact validation text.

These assertions lock the suite to framework wording ("Missing 1 required arg", "Missing required flag type") instead of the command contract, so a harmless @oclif/core wording change would fail the tests even if credentials add still rejects the inputs correctly. Prefer asserting the non-zero exit plus the presence of the missing arg/flag context. As per path instructions, "Review tests for behavioral confidence rather than implementation lock-in" and "Prefer observable outcomes through the public boundary over source-text, private-shape, or mock-call assertions."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/cli/credentials-command.test.ts` around lines 36 - 47, Update the
credentials add CLI tests in credentials-command.test.ts so they verify behavior
rather than Oclif’s exact wording: keep asserting the non-zero exit code from
run(), but replace the string-matching on “Missing 1 required arg” and “Missing
required flag type” with broader checks that the output indicates the provider
argument and type flag are missing. Use the existing run() helper and the
credentials add command cases as the public contract, not framework-specific
validation text.

Source: Path instructions


26-62: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the new NemoClaw-specific failure branches.

The new suite exercises framework validation, but it never hits the custom credentials add branches for bridge-provider rejection or the already exists reset hint in src/commands/credentials/add.ts. Those are the new behaviors called out in the PR goals and are the ones most likely to regress independently of Oclif. As per path instructions, "Review tests for behavioral confidence rather than implementation lock-in" and "Require negative-path tests that prove the boundary rejects bypasses...", and the PR objectives explicitly call out bridge-name rejection plus the already exists guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/cli/credentials-command.test.ts` around lines 26 - 62, The current tests
cover oclif validation but miss the custom negative paths in credentials add.
Add focused cases against the Add command that assert bridge-provider names are
rejected and that the already-exists branch returns the reset hint guidance; use
the existing run helper and keep assertions on the observable CLI output rather
than implementation details.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/credentials/add.ts`:
- Around line 52-124: The CredentialsAddCommand.run method is doing
orchestration work instead of staying a thin parser/delegator. Move the gateway
recovery, OpenShell argv assembly, provider creation call, and stderr-to-UX
mapping out of run() into a dedicated action under src/lib/actions/**, and have
run() only parse flags/args and translate them into typed inputs for that
action. Keep the logic around provider, credentials, config, fromExisting, and
runOpenshellProviderCommand in the action layer, and preserve the same
success/error behavior there.

---

Nitpick comments:
In `@test/cli/credentials-command.test.ts`:
- Around line 36-47: Update the credentials add CLI tests in
credentials-command.test.ts so they verify behavior rather than Oclif’s exact
wording: keep asserting the non-zero exit code from run(), but replace the
string-matching on “Missing 1 required arg” and “Missing required flag type”
with broader checks that the output indicates the provider argument and type
flag are missing. Use the existing run() helper and the credentials add command
cases as the public contract, not framework-specific validation text.
- Around line 26-62: The current tests cover oclif validation but miss the
custom negative paths in credentials add. Add focused cases against the Add
command that assert bridge-provider names are rejected and that the
already-exists branch returns the reset hint guidance; use the existing run
helper and keep assertions on the observable CLI output rather than
implementation details.
🪄 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: Enterprise

Run ID: 5216d902-3365-420f-aeb1-2344d88b9b65

📥 Commits

Reviewing files that changed from the base of the PR and between c6113be and bcb80a0.

📒 Files selected for processing (8)
  • nemoclaw-blueprint/provider-profiles/tavily.yaml
  • src/commands/credentials.ts
  • src/commands/credentials/add.ts
  • src/lib/credentials/command-support.ts
  • src/lib/credentials/store.ts
  • test/cli/credentials-command.test.ts
  • test/credentials.test.ts
  • test/validate-blueprint.test.ts

Comment thread src/commands/credentials/add.ts
@laitingsheng laitingsheng added NV QA Bugs found by the NVIDIA QA Team area: cli Command line interface, flags, terminal UX, or output bug-fix PR fixes a bug or regression labels Jun 29, 2026
…derr

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/package-contract/cli/credentials-cli-command.test.ts (1)

316-329: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid pinning this package-contract test to the internal timeout value.

timeout: 30_000 is an action detail, not part of the CLI contract, so harmless timeout tuning will break this test without changing behavior. Match the forwarded argv and user-visible outcome, or relax opts to a partial assertion.

♻️ Suggested assertion change
-      expect(calls).toEqual([
+      expect(calls).toMatchObject([
         {
           args: [
             "provider",
             "create",
             "--name",
             "tavily-search",
             "--type",
             "tavily",
             "--credential",
             "TAVILY_API_KEY",
           ],
-          opts: { ignoreError: true, stdio: ["ignore", "pipe", "pipe"], timeout: 30_000 },
+          opts: { ignoreError: true, stdio: ["ignore", "pipe", "pipe"] },
         },
       ]);

As per path instructions, "Prefer observable outcomes through the public boundary over source-text, private-shape, or mock-call assertions."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/package-contract/cli/credentials-cli-command.test.ts` around lines 316 -
329, The package-contract test is asserting an internal action detail (`timeout:
30_000`) instead of the public CLI behavior, which makes it brittle. Update
`credentials-cli-command.test.ts` to verify only the observable contract from
the `calls` recorded by the CLI wrapper, keeping the forwarded argv and
user-visible outcome while relaxing `opts` to ignore implementation-specific
fields like timeout in the `expect(calls)` assertion.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/actions/credentials-add.ts`:
- Around line 62-65: The validation branch in credentials-add.ts is echoing the
raw `credential` value back in the `fail(...)` message, which can leak secrets;
update the error handling in the `ENV_NAME_PATTERN` check to return a generic
invalid-credential message without interpolating user input, and add a
regression test around the `credentials-add` flow to verify a secret-like
`--credential` value is never reflected in stderr.

---

Nitpick comments:
In `@test/package-contract/cli/credentials-cli-command.test.ts`:
- Around line 316-329: The package-contract test is asserting an internal action
detail (`timeout: 30_000`) instead of the public CLI behavior, which makes it
brittle. Update `credentials-cli-command.test.ts` to verify only the observable
contract from the `calls` recorded by the CLI wrapper, keeping the forwarded
argv and user-visible outcome while relaxing `opts` to ignore
implementation-specific fields like timeout in the `expect(calls)` assertion.
🪄 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: Enterprise

Run ID: f6728c3c-6fcd-4f73-8ec2-18fcdbbe33e0

📥 Commits

Reviewing files that changed from the base of the PR and between bcb80a0 and 2d7ca8d.

📒 Files selected for processing (8)
  • src/commands/credentials/add.ts
  • src/lib/actions/credentials-add.ts
  • src/lib/cli/public-display-defaults.ts
  • src/lib/credentials/command-support.ts
  • src/lib/security/redact.ts
  • test/cli/credentials-command.test.ts
  • test/package-contract/cli/credentials-cli-command.test.ts
  • test/secret-redaction.test.ts
✅ Files skipped from review due to trivial changes (1)
  • src/lib/credentials/command-support.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/cli/credentials-command.test.ts

Comment thread src/lib/actions/credentials-add.ts Outdated
Signed-off-by: Tinson Lai <tinsonl@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/reference/commands.mdx`:
- Around line 1984-1986: The commands reference for runCredentialsAddAction
needs to reflect the actual CLI constraint between --credential and
--from-existing. Update the documentation near the options table to state that
callers must provide either repeatable --credential <ENV_NAME> values or
--from-existing, but not both, and that invoking the command with neither is
invalid. Use the runCredentialsAddAction behavior as the source of truth so the
documented rules match the implemented validation.
🪄 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: Enterprise

Run ID: 65a92292-7c0f-4735-9b55-97ebcf0ce5f5

📥 Commits

Reviewing files that changed from the base of the PR and between 2d7ca8d and fc6a3f6.

📒 Files selected for processing (2)
  • docs/reference/commands.mdx
  • src/lib/credentials/command-support.ts
✅ Files skipped from review due to trivial changes (1)
  • src/lib/credentials/command-support.ts

Comment thread docs/reference/commands.mdx
…act tvly

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@github-actions

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/security/secret-patterns.ts`:
- Around line 39-40: The Tavily secret pattern was added in the Node redaction
path, but the shell fallback still misses the new tvly- prefix. Update the
fallback redaction logic in debug.sh by adding tvly- to EXPECTED_SHELL_PREFIXES
(or the equivalent shell-side prefix list) so both paths redact the same Tavily
tokens. Keep the change aligned with the secret-patterns.ts Tavily rule to
ensure the Node and shell behavior stay equivalent.
🪄 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: Enterprise

Run ID: 59899b34-e093-447c-866b-f1fa953e7315

📥 Commits

Reviewing files that changed from the base of the PR and between fc6a3f6 and bb95543.

📒 Files selected for processing (9)
  • docs/reference/commands-nemohermes.mdx
  • docs/reference/commands.mdx
  • nemoclaw-blueprint/provider-profiles/tavily.yaml
  • src/lib/actions/credentials-add.ts
  • src/lib/security/secret-patterns.ts
  • test/cli/credentials-command.test.ts
  • test/package-contract/cli/credentials-cli-command.test.ts
  • test/secret-redaction.test.ts
  • test/validate-blueprint.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • test/cli/credentials-command.test.ts
  • test/secret-redaction.test.ts
  • nemoclaw-blueprint/provider-profiles/tavily.yaml
  • docs/reference/commands.mdx
  • test/package-contract/cli/credentials-cli-command.test.ts
  • src/lib/actions/credentials-add.ts

Comment thread src/lib/security/secret-patterns.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/onboard.ts (1)

2955-2965: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Do not attach the global extra-provider list to every sandbox.

registry.listExtraProviders() is global state, and this call now feeds all of it into sandbox creation. Because prepareSandboxCreatePlan() turns each entry into --provider, registering one provider makes it available to every sandbox created through this path, which is a least-privilege regression for secret-backed providers. Persist provider attachments per sandbox, or require explicit per-sandbox selection before passing them into create.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard.ts` around lines 2955 - 2965, The sandbox creation path is
currently passing the global extra-provider registry into
`sandboxCreatePlan.prepareSandboxCreatePlan()` via
`registry.listExtraProviders()`, which makes every registered provider available
to every sandbox. Remove this global attachment from the
`prepareSandboxCreatePlan` call in `onboard.ts`, and instead source providers
from per-sandbox state or an explicit sandbox-specific selection so only
intended providers are converted into `--provider` entries.
🧹 Nitpick comments (1)
test/langchain-deepagents-code-image.test.ts (1)

1202-1202: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer a behavior assertion over the regex fingerprint literal.

This hard-codes TOKEN_PREFIX_PATTERNS’ internal shape again instead of proving the public effect. Assert that a tvly-... token is actually redacted/detected in the exercised boundary, rather than matching the exact regex source string. As per path instructions, "Prefer observable outcomes through the public boundary over source-text, private-shape, or mock-call assertions."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/langchain-deepagents-code-image.test.ts` at line 1202, The test is
asserting the internal regex source for TOKEN_PREFIX_PATTERNS instead of the
observable redaction behavior. Update the assertion in the
langchain-deepagents-code-image test to verify that a real tvly-... token is
detected/redacted through the public boundary exercised by the test, using the
existing redaction flow rather than matching the literal pattern string.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/credentials/reset.ts`:
- Around line 69-70: The local cleanup in resetCredentials only happens when the
gateway delete succeeds with a zero status, so stale extraProviders can remain
when the provider was already removed elsewhere. Update the delete-result
handling in resetCredentials to also treat the gateway’s “not found/already
absent” response as a successful cleanup path, and call removeExtraProvider(key)
for that case as well; keep the existing success handling around result.status
and the provider key flow unchanged.

In `@src/lib/onboard/sandbox-create-plan.test.ts`:
- Around line 238-315: The current tests only cover prepareSandboxCreatePlan’s
internal provider rewrite and do not verify the public entrypoints that are
supposed to reach it. Add a test around the top-level onboard/rebuild flow that
exercises persisted extraProviders coming from registry state and asserts they
are carried through to the final create args, using the relevant entrypoint
functions rather than calling prepareSandboxCreatePlan directly. Also add
coverage that the old provider-attachment path is no longer reachable or has
been removed, so a wiring regression above this helper will fail.

In `@test/registry.test.ts`:
- Around line 523-535: The current `removeExtraProvider` test only validates the
in-memory result and misses the disk serialization behavior. Update the
`removeExtraProvider clears the entry and drops the field when empty` case in
`registry.test.ts` to assert the persisted registry data no longer contains
`extraProviders` after the last removal, using the `regFile` JSON readback or
the `serializeRegistryForDisk()` behavior to confirm the field is deleted rather
than left as an empty array.

---

Outside diff comments:
In `@src/lib/onboard.ts`:
- Around line 2955-2965: The sandbox creation path is currently passing the
global extra-provider registry into
`sandboxCreatePlan.prepareSandboxCreatePlan()` via
`registry.listExtraProviders()`, which makes every registered provider available
to every sandbox. Remove this global attachment from the
`prepareSandboxCreatePlan` call in `onboard.ts`, and instead source providers
from per-sandbox state or an explicit sandbox-specific selection so only
intended providers are converted into `--provider` entries.

---

Nitpick comments:
In `@test/langchain-deepagents-code-image.test.ts`:
- Line 1202: The test is asserting the internal regex source for
TOKEN_PREFIX_PATTERNS instead of the observable redaction behavior. Update the
assertion in the langchain-deepagents-code-image test to verify that a real
tvly-... token is detected/redacted through the public boundary exercised by the
test, using the existing redaction flow rather than matching the literal pattern
string.
🪄 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: Enterprise

Run ID: 40c639ee-35fb-40f3-b120-6fc3c91bde5b

📥 Commits

Reviewing files that changed from the base of the PR and between bb95543 and 797c0ad.

📒 Files selected for processing (12)
  • src/commands/credentials/reset.ts
  • src/lib/actions/credentials-add.ts
  • src/lib/actions/global.ts
  • src/lib/onboard.ts
  • src/lib/onboard/sandbox-create-plan.test.ts
  • src/lib/onboard/sandbox-create-plan.ts
  • src/lib/security/secret-patterns.ts
  • src/lib/state/registry.ts
  • test/deepagents-code-tui-startup-check.test.ts
  • test/langchain-deepagents-code-image.test.ts
  • test/package-contract/cli/credentials-cli-command.test.ts
  • test/registry.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/lib/security/secret-patterns.ts
  • src/lib/actions/credentials-add.ts
  • test/package-contract/cli/credentials-cli-command.test.ts

Comment thread src/commands/credentials/reset.ts Outdated
Comment thread src/lib/onboard/sandbox-create-plan.test.ts
Comment thread test/registry.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/credentials/reset.ts`:
- Around line 77-85: The reset flow in credentials reset.ts incorrectly treats
uppercase provider names as env vars via looksLikeEnvName, which can skip
cleanup for valid providers like TAVILY_SEARCH when the gateway says they are
already absent. Update the alreadyAbsent handling in the reset command so it
distinguishes real env-var keys from provider names using a more reliable check
than the current regex, and ensure forgetExtraProvider(key) still runs for
deleted providers. Keep the cleanup path and the user-facing logs in the reset
command consistent with the provider state before returning.

In `@test/registry.test.ts`:
- Around line 532-535: The registry “round-trip” test only inspects the saved
JSON and never verifies deserialization through the public API. Update the test
around registry.addExtraProvider and registry.listExtraProviders() so it reopens
or rehydrates the registry state from disk after writing, then assert the
provider is still present via listExtraProviders() instead of checking
raw.extraProviders directly.
- Around line 538-577: This test is asserting the helper directly instead of the
integration path that loads persisted providers from the registry. Update the
test to exercise the public create/rebuild boundary that wires registry state
into sandbox creation, using the real flow that calls prepareSandboxCreatePlan
rather than passing registry.listExtraProviders() in manually. Keep the
assertion on the observable provider args, but verify them through the caller
responsible for propagating registry providers into the plan.
🪄 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: Enterprise

Run ID: 34497390-603b-441e-bd0c-af07508f8e4e

📥 Commits

Reviewing files that changed from the base of the PR and between bb95543 and 7d76a6c.

📒 Files selected for processing (16)
  • docs/get-started/quickstart-langchain-deepagents-code.mdx
  • src/commands/credentials.test.ts
  • src/commands/credentials/reset.ts
  • src/lib/actions/credentials-add.ts
  • src/lib/actions/global.ts
  • src/lib/onboard.ts
  • src/lib/onboard/sandbox-create-plan.test.ts
  • src/lib/onboard/sandbox-create-plan.ts
  • src/lib/security/secret-patterns.ts
  • src/lib/state/registry.ts
  • test/deepagents-code-tui-startup-check.test.ts
  • test/e2e/e2e-cloud-experimental/checks/07-deepagents-code-headless-inference.sh
  • test/e2e/e2e-cloud-experimental/checks/10-deepagents-code-tui-startup.sh
  • test/langchain-deepagents-code-image.test.ts
  • test/package-contract/cli/credentials-cli-command.test.ts
  • test/registry.test.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • test/langchain-deepagents-code-image.test.ts
  • test/deepagents-code-tui-startup-check.test.ts
  • src/lib/onboard/sandbox-create-plan.test.ts
  • src/lib/onboard/sandbox-create-plan.ts
  • src/lib/actions/credentials-add.ts
  • src/lib/state/registry.ts
  • src/lib/onboard.ts

Comment thread src/commands/credentials/reset.ts Outdated
Comment thread test/registry.test.ts Outdated
Comment thread test/registry.test.ts Outdated
laitingsheng and others added 2 commits June 29, 2026 16:56
…vider grammar

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/actions/credentials-add.ts (1)

106-110: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Don’t echo the rejected --credential prefix.

The KEY=VALUE branch still reflects user input before =, which can leak secret-like values in logs. Use a fixed example instead.

Proposed fix
     if (credential.includes("=")) {
       return fail([
         `  --credential expects an env variable name, not 'KEY=VALUE'.`,
-        `  Export the value first (e.g. \`export ${credential.split("=", 1)[0]}=...\`)`,
-        `  and re-run with \`--credential ${credential.split("=", 1)[0]}\`.`,
+        "  Export the value first (e.g. `export TAVILY_API_KEY=...`)",
+        "  and re-run with `--credential TAVILY_API_KEY`.",
       ]);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/actions/credentials-add.ts` around lines 106 - 110, The
`credential.includes("=")` validation branch in `credentials-add.ts` is echoing
back user-provided content from `--credential`, which can leak sensitive values.
Update the `fail(...)` संदेश in this branch to avoid using
`credential.split("=", 1)[0]` in any output and replace it with a fixed,
non-user-derived example in both the export and re-run guidance. Keep the change
localized to the `credential.includes("=")` check and its `fail` message.
🧹 Nitpick comments (1)
src/lib/actions/credentials-add.ts (1)

4-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move bundled-profile filesystem probing out of the action.

This action now owns repo-file discovery via fs.existsSync/ROOT. Keep this module orchestration-only and put bundled profile lookup/import support behind a src/lib/credentials/** or adapter helper.

As per path instructions, actions should orchestrate while adapters own host/process/network boundaries, and credential-command support belongs under src/lib/credentials/**.

Also applies to: 45-60

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/actions/credentials-add.ts` around lines 4 - 5, The bundled-profile
filesystem probing currently lives in the credentials-add action, which should
stay orchestration-only. Move the ROOT/fs.existsSync-based repo/file discovery
and bundled profile import support into a helper under src/lib/credentials/** or
an adapter, and have the action call that helper instead. Keep the action
focused on wiring command flow, and update the referenced imports/usages in
credentials-add so host filesystem concerns are no longer handled there.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/lib/actions/credentials-add.ts`:
- Around line 106-110: The `credential.includes("=")` validation branch in
`credentials-add.ts` is echoing back user-provided content from `--credential`,
which can leak sensitive values. Update the `fail(...)` संदेश in this branch to
avoid using `credential.split("=", 1)[0]` in any output and replace it with a
fixed, non-user-derived example in both the export and re-run guidance. Keep the
change localized to the `credential.includes("=")` check and its `fail` message.

---

Nitpick comments:
In `@src/lib/actions/credentials-add.ts`:
- Around line 4-5: The bundled-profile filesystem probing currently lives in the
credentials-add action, which should stay orchestration-only. Move the
ROOT/fs.existsSync-based repo/file discovery and bundled profile import support
into a helper under src/lib/credentials/** or an adapter, and have the action
call that helper instead. Keep the action focused on wiring command flow, and
update the referenced imports/usages in credentials-add so host filesystem
concerns are no longer handled there.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 369615bf-dfe4-40e4-8bd6-eef9cf424501

📥 Commits

Reviewing files that changed from the base of the PR and between 56bd636 and cc1c958.

📒 Files selected for processing (2)
  • src/lib/actions/credentials-add.ts
  • test/package-contract/cli/credentials-cli-command.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/package-contract/cli/credentials-cli-command.test.ts

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

E2E Target Recommendation

Required E2E targets: ubuntu-repo-cloud-langchain-deepagents-code
Optional E2E targets: ubuntu-repo-cloud-openclaw

Dispatch required E2E targets:

  • gh workflow run e2e.yaml --ref <pr-head-ref> --field targets=ubuntu-repo-cloud-langchain-deepagents-code

Workflow run

Full E2E target advisor summary

E2E Target Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E targets

  • ubuntu-repo-cloud-langchain-deepagents-code: Primary live-supported typed target for LangChain Deep Agents Code. It onboards the Deep Agents sandbox and runs the cloud-experimental Deep Agents checks, including the changed headless inference and TUI startup scripts, while also exercising the Tavily opt-in policy/profile and onboarding create-plan provider attachment path touched by this PR.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field targets=ubuntu-repo-cloud-langchain-deepagents-code

Optional E2E targets

  • ubuntu-repo-cloud-openclaw: Adjacent canonical Ubuntu/OpenClaw cloud onboarding lane. Useful as an extra check for the generic credentials, registry extra-provider state, and sandbox create-plan changes outside the Deep Agents-specific path.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field targets=ubuntu-repo-cloud-openclaw

Relevant changed files

  • nemoclaw-blueprint/policies/presets/tavily.yaml
  • nemoclaw-blueprint/provider-profiles/tavily.yaml
  • src/commands/credentials.ts
  • src/commands/credentials/add.ts
  • src/commands/credentials/reset.ts
  • src/lib/actions/credentials-add.ts
  • src/lib/actions/global.ts
  • src/lib/credentials/command-support.ts
  • src/lib/credentials/store.ts
  • src/lib/onboard.ts
  • src/lib/onboard/sandbox-create-plan.ts
  • src/lib/security/redact.ts
  • src/lib/security/secret-patterns.ts
  • src/lib/state/extra-providers.ts
  • src/lib/state/registry.ts
  • test/e2e/e2e-cloud-experimental/checks/07-deepagents-code-headless-inference.sh
  • test/e2e/e2e-cloud-experimental/checks/10-deepagents-code-tui-startup.sh

@github-actions

Copy link
Copy Markdown
Contributor

Vitest E2E Target Results — ❌ Some jobs failed

Run: 28464821224
Workflow ref: feat/credentials-add
Requested targets: (selector rejected by workflow validation)
Requested jobs: (selector rejected by workflow validation)
Summary: 0 passed, 1 failed, 0 cancelled, 0 skipped

Job Result
generate-matrix ❌ failure

Failed jobs: generate-matrix. Check run artifacts for logs.

@github-actions

Copy link
Copy Markdown
Contributor

Vitest E2E Target Results — ✅ All selected jobs passed

Run: 28465069138
Workflow ref: feat/credentials-add
Requested targets: ubuntu-repo-cloud-langchain-deepagents-code
Requested jobs: (default — all default-enabled free-standing jobs; explicit-only jobs openshell-gateway-auth-contract, jetson-nvmap-gpu, and sandbox-rlimits-connect are skipped unless selected)
Summary: 1 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
live ✅ success

…vily binary allowlist

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@github-actions

Copy link
Copy Markdown
Contributor

Vitest E2E Target Results — ✅ All requested jobs passed

Run: 28465065066
Workflow ref: feat/credentials-add
Requested targets: (default — all supported)
Requested jobs: cloud-onboard,credential-sanitization,network-policy
Summary: 3 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
cloud-onboard ✅ success
credential-sanitization ✅ success
network-policy ✅ success

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@cv cv merged commit acacf93 into main Jul 1, 2026
43 checks passed
@cv cv deleted the feat/credentials-add branch July 1, 2026 07:01
apurvvkumaria added a commit that referenced this pull request Jul 1, 2026
<!-- markdownlint-disable MD041 -->
## Summary

Restore the managed Deep Agents Code Python interpreter to the Tavily
policy and provider-profile allowlists. PR #5969 removed Python while
tightening the Tavily boundary, which left `policy-add tavily`
successful but Python-originated Tavily requests blocked by OpenShell
with HTTP 403.

## Changes

- Add `/opt/venv/bin/python3*` to
`nemoclaw-blueprint/policies/presets/tavily.yaml`.
- Add the same managed interpreter path to
`nemoclaw-blueprint/provider-profiles/tavily.yaml` so both enforcement
layers agree.
- Document why the interpreter is required for OpenShell process
attribution.
- Update the exact preset and provider-profile allowlist contracts.
- Keep system Python paths excluded so the restored access remains
limited to the managed Deep Agents environment.

## Type of Change

- [x] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates
<!-- Check all that apply. For any "covered by existing tests", "not
applicable", or waiver entry, add a brief justification on the same line
or in the Changes section. -->
- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [ ] Docs updated for user-facing behavior changes
- [x] Docs not applicable — justification: existing Deep Agents
documentation already states that OpenShell attributes Tavily calls to
the managed Python interpreter; this restores the documented behavior.
- [x] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [ ] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification: pending maintainer
review; the change restores only `/opt/venv/bin/python3*`, not system
Python paths.
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification
<!-- Check each item you ran and confirmed. Leave unchecked items you
skipped. Doc-only changes do not require npm test unless you ran it. -->
- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [ ] Git hooks passed during commit and push, or `npx prek run
--from-ref main --to-ref HEAD` passes — the diff-scoped fallback passed
formatting, schema, repository, and secret-scan hooks; its broad
integration lane requires Linux utilities/semantics unavailable on this
macOS host, so CI remains authoritative.
- [x] Targeted tests pass for changed behavior
- [ ] Full `npm test` passes (broad runtime changes only)
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Apurv Kumaria
<36614+apurvvkumaria@users.noreply.github.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Updated Tavily-related runtime permissions to allow the managed Python
interpreter, which should reduce permission-related failures when
running Python-based tasks.
* Kept existing allowed binaries unchanged while extending support for
the additional Python runtime path.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Apurv Kumaria <36614+apurvvkumaria@users.noreply.github.com>
Co-authored-by: Apurv Kumaria <36614+apurvvkumaria@users.noreply.github.com>
ericksoa pushed a commit that referenced this pull request Jul 2, 2026
## Summary
- Add the `v0.0.72` release-note section with links to the deeper docs
pages for installer recovery, command diagnostics, inference, policy,
and sandbox repair changes.
- Document the custom preset `allowed_ips` guard for user-authored
policy files.

## Related Issue
None.

## Source summary
- #6132 -> `docs/about/release-notes.mdx`: Summarizes installer and
upgrade recovery before generic onboarding, with links to quickstart and
lifecycle docs.
- #6087 -> `docs/network-policy/customize-network-policy.mdx`: Documents
that user-authored custom presets reject `allowed_ips` for ordinary
endpoints; also summarized in release notes.
- #5975 -> `docs/about/release-notes.mdx`: Summarizes safer curl-based
inference probes that keep API keys out of process arguments.
- #6044 -> `docs/about/release-notes.mdx`: Summarizes compact `channels
status` configuration reporting.
- #6096 -> `docs/about/release-notes.mdx`: Summarizes OpenClaw EC2
metadata discovery disablement and links to security guidance.
- #5980 and #5991 -> `docs/about/release-notes.mdx`: Summarizes `exec`
multiline argument rejection and recovery guidance.
- #6023 -> `docs/about/release-notes.mdx`: Summarizes
registered-provider diagnostics for `inference set` failures.
- #6074 -> `docs/about/release-notes.mdx`: Summarizes the refreshed
NVIDIA Endpoints featured-model selection behavior.
- #5969 -> `docs/about/release-notes.mdx`: Summarizes `credentials add`
provider credential registration.
- #6060 -> `docs/about/release-notes.mdx`: Summarizes mutable OpenClaw
config permission restoration after `exec`.
- #6134 -> `docs/about/release-notes.mdx`: Summarizes restored Tavily
access for managed Python workflows.
- #6089 -> `docs/about/release-notes.mdx`: Summarizes Hermes runtime
version-scheme comparison during upgrade checks.
- #6131 -> `docs/about/release-notes.mdx`: Summarizes OpenClaw gateway
watchdog recovery behavior.
- #5976 and #5990 -> `docs/about/release-notes.mdx`: Summarizes prompt
stdin EOF cancellation behavior during onboarding.
- #5540 -> `docs/about/release-notes.mdx`: Summarizes clarified
host-level and per-sandbox status command scope.
- #5978 and #6018 -> `docs/about/release-notes.mdx`: Summarizes
policy-denial log breadcrumbs in connect shells.

## Testing
- `npm run docs:sync-agent-variants`
- `npm run docs`
- Commit hooks passed during `git commit`, including commitlint and
gitleaks.
- Pre-push hook passed during `git push`, including TypeScript CLI and
package/tag version sync.

## Checklist
- [x] Documentation updated.
- [x] `npm run docs` completed with 0 errors and 1 existing Fern
warning.
- [x] No source code or generated build artifacts committed.

Signed-off-by: Miyoung Choi <miyoungc@nvidia.com>

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Added release notes for v0.0.72 covering improved installer recovery,
clearer CLI diagnostics, safer inference setup and provider switching,
better credential handling, stronger policy boundaries, and more robust
runtime repair behavior.
* Updated network policy guidance to clarify when `allowed_ips` can be
used, including a specific exception for the sandbox-to-host bridge
endpoint.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command line interface, flags, terminal UX, or output bug-fix PR fixes a bug or regression NV QA Bugs found by the NVIDIA QA Team v0.0.72 Release target

Projects

None yet

3 participants