Skip to content

feat(sandbox): install provider access tools#3747

Open
cheese-head wants to merge 5 commits into
NVIDIA:mainfrom
cheese-head:policy-access/sandbox-provider-tools
Open

feat(sandbox): install provider access tools#3747
cheese-head wants to merge 5 commits into
NVIDIA:mainfrom
cheese-head:policy-access/sandbox-provider-tools

Conversation

@cheese-head

@cheese-head cheese-head commented May 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Installs the provider-oriented CLI tools that provider profiles expose, and makes the NemoClaw OpenClaw plugin available from the sandbox image. This keeps the sandbox image aligned with the provider profile policy surface while OpenShell approval still controls network and credential use.

Changes

  • Add scripts/install-provider-tools.sh for pinned gh, glab, jq, claude, codex, opencode, and copilot setup.
  • Wire provider tool installation into Dockerfile.base, Dockerfile, and optimized sandbox build context staging.
  • Copy the NemoClaw OpenClaw plugin into /usr/local/share/nemoclaw/openclaw-plugins/nemoclaw.
  • Update generated OpenClaw config to load the NemoClaw plugin by default.
  • Expand the GitHub preset binary allowlist to match the installed GitHub provider tooling.
  • Handle empty or malformed NemoClaw plugin config files gracefully.

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)

Verification

  • npx prek run --all-files passes
  • npm test passes
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • make 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: Patrick Riel priel@nvidia.com

Summary by CodeRabbit

  • New Features

    • Added resource access management tools: list presets, request access, and check request status.
    • Extended provider integrations: Brave Search, Homebrew, Discord, HuggingFace, Jira, npm, PyPI, Slack, Telegram, and local inference.
    • Improved GitHub CLI (gh) support.
  • Documentation

    • Added NemoClaw-OpenShell integration guide.
  • Bug Fixes

    • Enhanced configuration file error handling for empty or malformed JSON.
  • Chores

    • Optimized Docker image builds with provider tools installation.
    • Improved plugin initialization configuration.

Review Change Stack

@copy-pr-bot

copy-pr-bot Bot commented May 18, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR adds provider profile support to NemoClaw, enabling agents to request dynamic resource access through OpenShell's policy service. It includes ten provider profile definitions, a complete access-client module for request submission, three new OpenClaw plugin tools, policy integration for provider profile loading, and onboarding provisioning with comprehensive test coverage.

Changes

NemoClaw OpenShell Access Integration

Layer / File(s) Summary
Provider Profiles and Resource Definitions
nemoclaw-blueprint/provider-profiles/*.yaml, nemoclaw-blueprint/policies/presets/github.yaml, docs/reference/nemoclaw-openshell-integration.md
Adds ten provider profiles (Brave, Brew, Discord, HuggingFace, Jira, local-inference, npm, PyPI, Slack, Telegram), expands GitHub preset to include /usr/bin/gh and /usr/local/bin/gh binaries alongside existing binaries, and documents the end-to-end NemoClaw↔OpenShell adapter integration flow with Mermaid diagrams and tool contract descriptions.
Access Client Request/Response Implementation
nemoclaw/src/access-client.ts
Implements core access-client module with exported types (AccessStatus, AccessRequestResponse, AccessPresetInfo), built-in preset catalog with endpoint/binary definitions, OpenShell provider profile loading and in-memory caching with time-based invalidation, preset name normalization with GitHub host aliasing, L7 rule derivation from access mode, HTTP/HTTP-proxy transport with JSON serialization and timeout handling, response parsing with status/rejection message mapping, and public API functions (createAccessRequest, getAccessRequest, waitAccessRequest, listAccessPresets, clearAccessPresetCache).
Plugin Tool Registration and Orchestration
nemoclaw/src/index.ts
Extends OpenClawPluginApi interface with registerTool method, adds PluginToolResult and PluginToolDefinition exported types, implements tool helpers for request payload construction and status polling, and registers three new tools: request_resource_access (creates request and waits for terminal status), list_resource_access_presets (returns preset list with optional provider data), and check_resource_access (fetches request status by id).
Policy System Provider Profile Integration
src/lib/policy/index.ts
Extends preset system to load OpenShell provider profiles from NEMOCLAW_OPENSHELL_PROVIDER_PROFILES_JSON env var or binary invocation, converts provider profiles to preset YAML with endpoint/binary mappings, filters profiles with valid endpoints, merges provider presets with built-in YAML presets by name, updates listPresets() and loadPreset() to include provider-backed presets, and exports profile loading and conversion helpers plus cache-clearing utilities.
Provider Profile Onboarding and Provisioning
src/lib/onboard/provider-profiles.ts, scripts/install-provider-tools.sh, src/lib/onboard.ts
Implements onboarding module to locate local YAML provider profiles, query existing OpenShell profiles via CLI, compute missing profiles, lint and import missing profiles with timeout/error handling, return structured statuses (missing-directory/unsupported/already-present/imported), adds install-provider-tools script with pinned versions for gh/glab/jq/npm, and integrates onboarding into main flow immediately after gateway step.
Docker Image and Build Context Updates
Dockerfile.base, Dockerfile, src/lib/sandbox/build-context.ts, scripts/generate-openclaw-config.py
Updates Dockerfile.base to add gh/glab to apt install list and execute provider-tools script, updates Dockerfile to copy install script and run it, creates NemoClaw OpenClaw plugin directory and copies built contents, changes /sandbox/.nemoclaw/config.json initialization from touch to valid JSON object {}, stages install-provider-tools.sh in optimized sandbox build context, and updates config generation to default-enable NemoClaw plugin with correct plugin load paths.
Configuration and Core Onboarding Wiring
nemoclaw/src/onboard/config.ts, nemoclaw/src/onboard/config.test.ts
Updates loadOnboardConfig() to trim whitespace, return null for empty files, and gracefully handle invalid JSON by catching parse errors instead of throwing, with corresponding test coverage for empty and malformed file cases.
Comprehensive Test Coverage
nemoclaw/src/access-client.test.ts, nemoclaw/src/register.test.ts, test/policies.test.ts, test/provider-profile-onboard.test.ts, test/sandbox-build-context.test.ts, test/generate-openclaw-config.test.ts, test/e2e/nemoclaw-policy-local-runner.mjs, test/e2e/test-nemoclaw-policy-local-plugin.sh, test/validate-blueprint.test.ts
Adds access-client tests covering HTTP protocol enforcement, preset listing with provider profiles, profile filtering, provider-backed request submission, and status conversion; plugin register tests validating tool registration and execution; policy tests for provider preset listing and loading; provider profile onboarding tests; build context and config generation tests; e2e runner script and shell test for full policy-local flow validation; and updated GitHub preset validation tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Suggested labels

Docker, OpenShell

Suggested reviewers

  • ericksoa

Poem

🐰 A rabbit hops through access gates so bright,
With profiles galore and policy in sight,
Ten new resources spring forth with care,
Provider tools dance through the azure air, 🚀
OpenShell whispers approval's delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.56% 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 'feat(sandbox): install provider access tools' is clear, concise, and accurately describes the primary change: installing provider-oriented CLI tools in the sandbox image.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch policy-access/sandbox-provider-tools

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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

@cheese-head cheese-head changed the title Policy access/sandbox provider tools feat(sandbox): install provider access tools May 18, 2026
@wscurran

Copy link
Copy Markdown
Contributor

✨ Thanks for submitting this detailed PR to enhance the sandbox environment by installing provider-oriented CLI tools and making the NemoClaw OpenClaw plugin available. This change aims to improve the usability and functionality of the sandbox image by aligning it with the provider profile policy surface.

@wscurran wscurran added the integration: openclaw OpenClaw integration behavior label May 18, 2026
@cheese-head cheese-head marked this pull request as ready for review May 21, 2026 19:21

@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: 7

🧹 Nitpick comments (4)
Dockerfile (1)

62-66: Run the container E2E suite for plugin/tooling runtime validation.

Given this changes runtime image composition and plugin load paths, run cloud-e2e, sandbox-survival-e2e, hermes-e2e, and rebuild-openclaw-e2e.

As per coding guidelines, Dockerfile changes “affect the sandbox container image” and are “only testable with a real container build,” with those E2E jobs recommended.

Also applies to: 254-255

🤖 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 `@Dockerfile` around lines 62 - 66, This change modifies the runtime image by
installing provider tools via the RUN step that executes
/usr/local/lib/nemoclaw/install-provider-tools.sh, so validate the new image by
running the recommended container E2E suites: cloud-e2e, sandbox-survival-e2e,
hermes-e2e, and rebuild-openclaw-e2e (also verify the other related RUN change
at the second occurrence of the same install step). Ensure each job runs against
a real container build of this Dockerfile and report any failures back with the
failing job logs and the exact container image digest.
Dockerfile.base (1)

75-77: Run the sandbox image E2E matrix for this Docker base-layer change.

Please run cloud-e2e, sandbox-survival-e2e, hermes-e2e, and rebuild-openclaw-e2e before merge to validate runtime behavior after base image toolchain changes.

As per coding guidelines, Dockerfile.base “affects the sandbox container image” and these changes are “only testable with a real container build,” with the listed E2E jobs recommended.

🤖 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 `@Dockerfile.base` around lines 75 - 77, This change adds COPY and execution of
scripts/install-provider-tools.sh in Dockerfile.base (the COPY line and the RUN
invoking /usr/local/lib/nemoclaw/install-provider-tools.sh), which modifies the
sandbox base image toolchain; before merging, build the container and run the
full sandbox image E2E matrix: execute cloud-e2e, sandbox-survival-e2e,
hermes-e2e, and rebuild-openclaw-e2e against the new base image build to
validate runtime behavior and ensure the provider tools script causes no
regressions.
docs/reference/nemoclaw-openshell-integration.md (1)

1-73: ⚡ Quick win

Complete required docs page structure for a new docs/** page.

This page is missing required frontmatter fields, a brief opening introduction, and a bottom Next Steps section with related links.

As per coding guidelines, for docs/** new pages: "Frontmatter includes title, description, keywords, topics, tags, content type, difficulty, audience, and status fields", "Page starts with a one- or two-sentence introduction", and "A 'Next Steps' section at the bottom links to related pages."

🤖 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 `@docs/reference/nemoclaw-openshell-integration.md` around lines 1 - 73, The
page "NemoClaw OpenShell Integration" is missing required frontmatter, a short
intro, and a "Next Steps" section; add YAML frontmatter containing title,
description, keywords, topics, tags, content_type, difficulty, audience, and
status at the top of the file, prepend a one- or two-sentence introduction
paragraph under the main heading summarizing the page purpose, and append a
"Next Steps" section at the bottom linking to related docs (e.g., pages about
onboarding, policy.local, adapter contracts or provider profiles) and any
follow-up actions; ensure the existing mermaid diagram and section headings
(Flow, Agent Tools, Adapter Contract, Provider Profiles) remain unchanged and
the new frontmatter fields match the repository's docs schema.
src/lib/onboard.ts (1)

9751-9751: Run onboarding E2E coverage for this integration point.

Since this changes src/lib/onboard.ts core flow, please run the recommended onboarding E2E jobs before merge to validate gateway/sandbox lifecycle behavior with provider profile provisioning.

As per coding guidelines src/lib/onboard.ts: “This file contains core onboarding logic... E2E test recommendation: cloud-e2e, sandbox-operations-e2e, rebuild-openclaw-e2e, channels-stop-start-e2e, messaging-compatible-endpoint-e2e, hermes-discord-e2e, hermes-slack-e2e, openshell-gateway-upgrade-e2e.”

🤖 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` at line 9751, This change touches the core onboarding
flow at ensureProviderProfilesAvailable() in src/lib/onboard.ts, so before
merging run the recommended E2E suites to validate gateway/sandbox lifecycle and
provider profile provisioning: cloud-e2e, sandbox-operations-e2e,
rebuild-openclaw-e2e, channels-stop-start-e2e,
messaging-compatible-endpoint-e2e, hermes-discord-e2e, hermes-slack-e2e, and
openshell-gateway-upgrade-e2e; if any test fails, capture logs around
ensureProviderProfilesAvailable() and provider profile provisioning, fix the
failing behavior, and re-run until all pass.
🤖 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/nemoclaw-openshell-integration.md`:
- Line 1: Add the required SPDX license header to the top of the Markdown file
docs/reference/nemoclaw-openshell-integration.md by inserting the SPDX copyright
and license lines as a header block (e.g., an HTML comment or plain text)
including "SPDX-FileCopyrightText: <YEAR> <OWNER>" and "SPDX-License-Identifier:
Apache-2.0" as the first lines of the file so the document complies with the
repository rule for *.{md,mdx} files.

In `@nemoclaw-blueprint/provider-profiles/brew.yaml`:
- Around line 12-32: The entries for public hosts (host: github.com, ghcr.io,
pkg-containers.githubusercontent.com, objects.githubusercontent.com,
raw.githubusercontent.com) currently set tls: skip; remove or change those tls
settings so normal TLS verification is enforced (e.g., delete the tls: skip
lines or set tls: verify) for each listed host in the provider profile to
restore proper transport security and policy guarantees.

In `@nemoclaw-blueprint/provider-profiles/npm.yaml`:
- Around line 11-16: Replace the insecure "tls: skip" setting for the external
registry entry (the block containing host: registry.yarnpkg.com and tls: skip)
so TLS verification is enabled by default; remove the tls: skip line or change
it to an affirmative verification setting (e.g., tls: verify or tls: true) in
the provider profile that contains the npm registry configuration to ensure
package registry traffic performs TLS certificate validation.

In `@nemoclaw/src/access-client.ts`:
- Around line 396-401: The loop over listProviderProfilePresets() currently
keeps the built-in preset on name collisions and only copies provider_profile,
which loses provider-sourced endpoints/binaries; change the collision branch in
that loop (the byName.set call where existing is checked) to merge the records
so the incoming preset's provider-sourced fields override the existing entry
(e.g., preserve any other existing metadata but copy
preset.endpoint/binaries/provider_profile/etc. from preset), ensuring
provider-supplied endpoints/binaries are used for overlapping names; locate this
logic around listProviderProfilePresets(), the byName map, and the
existing/preset variables and update the merge strategy accordingly.

In `@src/lib/onboard.ts`:
- Around line 1795-1805: The new helper function ensureProviderProfilesAvailable
in src/lib/onboard.ts causes net growth and breaks the onboard-entrypoint
budget; remove this local helper and either inline its minimal behavior at the
original call site or move it into a separate smaller module so onboard.ts
shrinks. Specifically, replace calls to ensureProviderProfilesAvailable with the
explicit call to
providerProfileOnboard.ensureNemoClawProviderProfiles(runOpenshell, { log: note
}) followed by the same branching on result.status and the
policies.clearProviderProfileCache() call (or export a tiny helper from a new
module and import it), preserving use of symbols
providerProfileOnboard.ensureNemoClawProviderProfiles, runOpenshell, note, and
policies.clearProviderProfileCache.

In `@src/lib/onboard/provider-profiles.ts`:
- Around line 116-123: The current branch always returns status:"unsupported"
whenever `list.status !== 0`; change this so you only return the unsupported
payload when the `list.stdout`/`list.stderr` explicitly indicate the
"unsupported command" signals from the `list-profiles` invocation, and for any
other non-zero exit produce a thrown Error (or rethrow) that includes
`list.status`, `list.stdout`, and `list.stderr` so callers can see
runtime/transient failures; locate the check on `list.status` in
provider-profiles.ts and replace the unconditional return with an explicit
pattern-match for the unsupported-case and an error throw containing
stdout/stderr for all other failures.

In `@test/e2e/test-nemoclaw-policy-local-plugin.sh`:
- Around line 25-29: Before setting the global OpenShell setting, read and store
the current value of agent_policy_proposals_enabled (use "${OPENSHELL_BIN}
settings get --global --key agent_policy_proposals_enabled") into a variable
(e.g., prior_agent_policy_proposals_enabled), then keep that variable in the
script scope and in the cleanup function restore it by calling "${OPENSHELL_BIN}
settings set --global --key agent_policy_proposals_enabled --value
<stored_value> --yes"; ensure the cleanup function (cleanup) is registered to
run on exit (trap) so the original global setting is always reverted.

---

Nitpick comments:
In `@Dockerfile`:
- Around line 62-66: This change modifies the runtime image by installing
provider tools via the RUN step that executes
/usr/local/lib/nemoclaw/install-provider-tools.sh, so validate the new image by
running the recommended container E2E suites: cloud-e2e, sandbox-survival-e2e,
hermes-e2e, and rebuild-openclaw-e2e (also verify the other related RUN change
at the second occurrence of the same install step). Ensure each job runs against
a real container build of this Dockerfile and report any failures back with the
failing job logs and the exact container image digest.

In `@Dockerfile.base`:
- Around line 75-77: This change adds COPY and execution of
scripts/install-provider-tools.sh in Dockerfile.base (the COPY line and the RUN
invoking /usr/local/lib/nemoclaw/install-provider-tools.sh), which modifies the
sandbox base image toolchain; before merging, build the container and run the
full sandbox image E2E matrix: execute cloud-e2e, sandbox-survival-e2e,
hermes-e2e, and rebuild-openclaw-e2e against the new base image build to
validate runtime behavior and ensure the provider tools script causes no
regressions.

In `@docs/reference/nemoclaw-openshell-integration.md`:
- Around line 1-73: The page "NemoClaw OpenShell Integration" is missing
required frontmatter, a short intro, and a "Next Steps" section; add YAML
frontmatter containing title, description, keywords, topics, tags, content_type,
difficulty, audience, and status at the top of the file, prepend a one- or
two-sentence introduction paragraph under the main heading summarizing the page
purpose, and append a "Next Steps" section at the bottom linking to related docs
(e.g., pages about onboarding, policy.local, adapter contracts or provider
profiles) and any follow-up actions; ensure the existing mermaid diagram and
section headings (Flow, Agent Tools, Adapter Contract, Provider Profiles) remain
unchanged and the new frontmatter fields match the repository's docs schema.

In `@src/lib/onboard.ts`:
- Line 9751: This change touches the core onboarding flow at
ensureProviderProfilesAvailable() in src/lib/onboard.ts, so before merging run
the recommended E2E suites to validate gateway/sandbox lifecycle and provider
profile provisioning: cloud-e2e, sandbox-operations-e2e, rebuild-openclaw-e2e,
channels-stop-start-e2e, messaging-compatible-endpoint-e2e, hermes-discord-e2e,
hermes-slack-e2e, and openshell-gateway-upgrade-e2e; if any test fails, capture
logs around ensureProviderProfilesAvailable() and provider profile provisioning,
fix the failing behavior, and re-run until all pass.
🪄 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: ce55ad0d-df47-4506-866b-9bde0e16e1d0

📥 Commits

Reviewing files that changed from the base of the PR and between d7bae57 and faa6e8f.

📒 Files selected for processing (33)
  • Dockerfile
  • Dockerfile.base
  • docs/reference/nemoclaw-openshell-integration.md
  • nemoclaw-blueprint/policies/presets/github.yaml
  • nemoclaw-blueprint/provider-profiles/brave.yaml
  • nemoclaw-blueprint/provider-profiles/brew.yaml
  • nemoclaw-blueprint/provider-profiles/discord.yaml
  • nemoclaw-blueprint/provider-profiles/huggingface.yaml
  • nemoclaw-blueprint/provider-profiles/jira.yaml
  • nemoclaw-blueprint/provider-profiles/local-inference.yaml
  • nemoclaw-blueprint/provider-profiles/npm.yaml
  • nemoclaw-blueprint/provider-profiles/pypi.yaml
  • nemoclaw-blueprint/provider-profiles/slack.yaml
  • nemoclaw-blueprint/provider-profiles/telegram.yaml
  • nemoclaw/src/access-client.test.ts
  • nemoclaw/src/access-client.ts
  • nemoclaw/src/index.ts
  • nemoclaw/src/onboard/config.test.ts
  • nemoclaw/src/onboard/config.ts
  • nemoclaw/src/register.test.ts
  • scripts/generate-openclaw-config.py
  • scripts/install-provider-tools.sh
  • src/lib/onboard.ts
  • src/lib/onboard/provider-profiles.ts
  • src/lib/policy/index.ts
  • src/lib/sandbox/build-context.ts
  • test/e2e/nemoclaw-policy-local-runner.mjs
  • test/e2e/test-nemoclaw-policy-local-plugin.sh
  • test/generate-openclaw-config.test.ts
  • test/policies.test.ts
  • test/provider-profile-onboard.test.ts
  • test/sandbox-build-context.test.ts
  • test/validate-blueprint.test.ts

@@ -0,0 +1,72 @@
# NemoClaw OpenShell Integration

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add the required SPDX license header at the top of this page.

This new Markdown file is missing the required SPDX copyright and Apache-2.0 header.

As per coding guidelines, "**/*.{js,ts,tsx,jsx,sh,yaml,yml,json,md,mdx}: Every source file must include an SPDX license header for copyright and Apache-2.0 license".

🤖 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 `@docs/reference/nemoclaw-openshell-integration.md` at line 1, Add the required
SPDX license header to the top of the Markdown file
docs/reference/nemoclaw-openshell-integration.md by inserting the SPDX copyright
and license lines as a header block (e.g., an HTML comment or plain text)
including "SPDX-FileCopyrightText: <YEAR> <OWNER>" and "SPDX-License-Identifier:
Apache-2.0" as the first lines of the file so the document complies with the
repository rule for *.{md,mdx} files.

Comment on lines +12 to +32
tls: skip
- host: github.com
port: 443
access: full
tls: skip
- host: ghcr.io
port: 443
access: full
tls: skip
- host: pkg-containers.githubusercontent.com
port: 443
access: full
tls: skip
- host: objects.githubusercontent.com
port: 443
access: full
tls: skip
- host: raw.githubusercontent.com
port: 443
access: full
tls: skip

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not skip TLS verification on public endpoints.

Using tls: skip across public hosts weakens transport security and policy guarantees. Please require normal TLS verification unless there is a narrowly justified exception.

Suggested change
   - host: formulae.brew.sh
     port: 443
     access: full
-    tls: skip
   - host: github.com
     port: 443
     access: full
-    tls: skip
   - host: ghcr.io
     port: 443
     access: full
-    tls: skip
   - host: pkg-containers.githubusercontent.com
     port: 443
     access: full
-    tls: skip
   - host: objects.githubusercontent.com
     port: 443
     access: full
-    tls: skip
   - host: raw.githubusercontent.com
     port: 443
     access: full
-    tls: skip
🤖 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 `@nemoclaw-blueprint/provider-profiles/brew.yaml` around lines 12 - 32, The
entries for public hosts (host: github.com, ghcr.io,
pkg-containers.githubusercontent.com, objects.githubusercontent.com,
raw.githubusercontent.com) currently set tls: skip; remove or change those tls
settings so normal TLS verification is enforced (e.g., delete the tls: skip
lines or set tls: verify) for each listed host in the provider profile to
restore proper transport security and policy guarantees.

Comment on lines +11 to +16
access: full
tls: skip
- host: registry.yarnpkg.com
port: 443
access: full
tls: skip

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid tls: skip for package registry traffic.

This disables TLS verification for external registries and weakens secure-by-default behavior. Please keep TLS verification enabled.

Suggested change
   - host: registry.npmjs.org
     port: 443
     access: full
-    tls: skip
   - host: registry.yarnpkg.com
     port: 443
     access: full
-    tls: skip
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
access: full
tls: skip
- host: registry.yarnpkg.com
port: 443
access: full
tls: skip
access: full
- host: registry.yarnpkg.com
port: 443
access: full
🤖 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 `@nemoclaw-blueprint/provider-profiles/npm.yaml` around lines 11 - 16, Replace
the insecure "tls: skip" setting for the external registry entry (the block
containing host: registry.yarnpkg.com and tls: skip) so TLS verification is
enabled by default; remove the tls: skip line or change it to an affirmative
verification setting (e.g., tls: verify or tls: true) in the provider profile
that contains the npm registry configuration to ensure package registry traffic
performs TLS certificate validation.

Comment on lines +396 to +401
for (const preset of listProviderProfilePresets()) {
const existing = byName.get(preset.name);
byName.set(
preset.name,
existing ? { ...existing, provider_profile: preset.provider_profile } : preset,
);

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Provider-profile preset data is ignored on name collisions.

On Line 400, when names collide, the code keeps the built-in preset rule and only copies provider_profile. That means provider-sourced endpoints/binaries are not used in access proposals for overlapping presets (for example github), which breaks alignment with provider profile policy surface.

Suggested fix
-    byName.set(
-      preset.name,
-      existing ? { ...existing, provider_profile: preset.provider_profile } : preset,
-    );
+    byName.set(
+      preset.name,
+      existing
+        ? {
+            ...existing,
+            ...preset,
+            rule: preset.rule,
+            provider_profile: preset.provider_profile,
+          }
+        : preset,
+    );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (const preset of listProviderProfilePresets()) {
const existing = byName.get(preset.name);
byName.set(
preset.name,
existing ? { ...existing, provider_profile: preset.provider_profile } : preset,
);
for (const preset of listProviderProfilePresets()) {
const existing = byName.get(preset.name);
byName.set(
preset.name,
existing
? {
...existing,
...preset,
rule: preset.rule,
provider_profile: preset.provider_profile,
}
: preset,
);
🤖 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 `@nemoclaw/src/access-client.ts` around lines 396 - 401, The loop over
listProviderProfilePresets() currently keeps the built-in preset on name
collisions and only copies provider_profile, which loses provider-sourced
endpoints/binaries; change the collision branch in that loop (the byName.set
call where existing is checked) to merge the records so the incoming preset's
provider-sourced fields override the existing entry (e.g., preserve any other
existing metadata but copy preset.endpoint/binaries/provider_profile/etc. from
preset), ensuring provider-supplied endpoints/binaries are used for overlapping
names; locate this logic around listProviderProfilePresets(), the byName map,
and the existing/preset variables and update the merge strategy accordingly.

Comment thread src/lib/onboard.ts
Comment on lines +1795 to +1805
function ensureProviderProfilesAvailable(): void {
const result = providerProfileOnboard.ensureNemoClawProviderProfiles(runOpenshell, {
log: note,
});
if (result.status === "unsupported") {
note(` ${result.message}`);
} else if (result.status === "already-present" && result.skipped.length > 0) {
note(` NemoClaw provider profiles already registered: ${result.skipped.join(", ")}`);
}
policies.clearProviderProfileCache();
}

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

CI blocker: onboarding entrypoint budget fails with this helper addition.

Line 1795-1805 introduces net growth in src/lib/onboard.ts, and CI is already failing the onboard-entrypoint budget (+16 lines). Please move this logic out of onboard.ts (or inline minimally) to get back under budget.

Proposed trim (remove local helper, keep behavior at call site)
-function ensureProviderProfilesAvailable(): void {
-  const result = providerProfileOnboard.ensureNemoClawProviderProfiles(runOpenshell, {
-    log: note,
-  });
-  if (result.status === "unsupported") {
-    note(`  ${result.message}`);
-  } else if (result.status === "already-present" && result.skipped.length > 0) {
-    note(`  NemoClaw provider profiles already registered: ${result.skipped.join(", ")}`);
-  }
-  policies.clearProviderProfileCache();
-}
-    ensureProviderProfilesAvailable();
+    const providerProfilesResult = providerProfileOnboard.ensureNemoClawProviderProfiles(runOpenshell, { log: note });
+    if (providerProfilesResult.status === "unsupported") {
+      note(`  ${providerProfilesResult.message}`);
+    } else if (providerProfilesResult.status === "already-present" && providerProfilesResult.skipped.length > 0) {
+      note(`  NemoClaw provider profiles already registered: ${providerProfilesResult.skipped.join(", ")}`);
+    }
+    policies.clearProviderProfileCache();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function ensureProviderProfilesAvailable(): void {
const result = providerProfileOnboard.ensureNemoClawProviderProfiles(runOpenshell, {
log: note,
});
if (result.status === "unsupported") {
note(` ${result.message}`);
} else if (result.status === "already-present" && result.skipped.length > 0) {
note(` NemoClaw provider profiles already registered: ${result.skipped.join(", ")}`);
}
policies.clearProviderProfileCache();
}
Suggested change
function ensureProviderProfilesAvailable(): void {
const result = providerProfileOnboard.ensureNemoClawProviderProfiles(runOpenshell, {
log: note,
});
if (result.status === "unsupported") {
note(` ${result.message}`);
} else if (result.status === "already-present" && result.skipped.length > 0) {
note(` NemoClaw provider profiles already registered: ${result.skipped.join(", ")}`);
}
policies.clearProviderProfileCache();
}
const providerProfilesResult = providerProfileOnboard.ensureNemoClawProviderProfiles(runOpenshell, { log: note });
if (providerProfilesResult.status === "unsupported") {
note(` ${providerProfilesResult.message}`);
} else if (providerProfilesResult.status === "already-present" && providerProfilesResult.skipped.length > 0) {
note(` NemoClaw provider profiles already registered: ${providerProfilesResult.skipped.join(", ")}`);
}
policies.clearProviderProfileCache();
🤖 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 1795 - 1805, The new helper function
ensureProviderProfilesAvailable in src/lib/onboard.ts causes net growth and
breaks the onboard-entrypoint budget; remove this local helper and either inline
its minimal behavior at the original call site or move it into a separate
smaller module so onboard.ts shrinks. Specifically, replace calls to
ensureProviderProfilesAvailable with the explicit call to
providerProfileOnboard.ensureNemoClawProviderProfiles(runOpenshell, { log: note
}) followed by the same branching on result.status and the
policies.clearProviderProfileCache() call (or export a tiny helper from a new
module and import it), preserving use of symbols
providerProfileOnboard.ensureNemoClawProviderProfiles, runOpenshell, note, and
policies.clearProviderProfileCache.

Comment on lines +116 to +123
if (list.status !== 0) {
return {
status: "unsupported",
imported: [],
skipped: [],
message: "OpenShell provider profiles are not available; using local preset fallbacks.",
};
}

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not treat every list failure as “unsupported.”

Line 116 currently downgrades all list-profiles failures to unsupported, which can hide transient/runtime failures and silently skip provider-profile sync. Only return unsupported when the output matches unsupported-command signals; otherwise throw with stderr/stdout details.

Suggested fix
   if (list.status !== 0) {
-    return {
-      status: "unsupported",
-      imported: [],
-      skipped: [],
-      message: "OpenShell provider profiles are not available; using local preset fallbacks.",
-    };
+    if (isUnsupportedProviderProfileCommand(list)) {
+      return {
+        status: "unsupported",
+        imported: [],
+        skipped: [],
+        message: "OpenShell provider profiles are not available; using local preset fallbacks.",
+      };
+    }
+    const details =
+      outputText(list.stderr) || outputText(list.stdout) || "provider list-profiles failed";
+    throw new Error(`NemoClaw provider profile discovery failed: ${details.trim()}`);
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (list.status !== 0) {
return {
status: "unsupported",
imported: [],
skipped: [],
message: "OpenShell provider profiles are not available; using local preset fallbacks.",
};
}
if (list.status !== 0) {
if (isUnsupportedProviderProfileCommand(list)) {
return {
status: "unsupported",
imported: [],
skipped: [],
message: "OpenShell provider profiles are not available; using local preset fallbacks.",
};
}
const details =
outputText(list.stderr) || outputText(list.stdout) || "provider list-profiles failed";
throw new Error(`NemoClaw provider profile discovery failed: ${details.trim()}`);
}
🤖 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/provider-profiles.ts` around lines 116 - 123, The current
branch always returns status:"unsupported" whenever `list.status !== 0`; change
this so you only return the unsupported payload when the
`list.stdout`/`list.stderr` explicitly indicate the "unsupported command"
signals from the `list-profiles` invocation, and for any other non-zero exit
produce a thrown Error (or rethrow) that includes `list.status`, `list.stdout`,
and `list.stderr` so callers can see runtime/transient failures; locate the
check on `list.status` in provider-profiles.ts and replace the unconditional
return with an explicit pattern-match for the unsupported-case and an error
throw containing stdout/stderr for all other failures.

Comment on lines +25 to +29
"${OPENSHELL_BIN}" settings set --global \
--key agent_policy_proposals_enabled \
--value true \
--yes

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Restore global OpenShell settings in cleanup.

Line 25 updates agent_policy_proposals_enabled globally, but the previous value is never restored. This can leak state into later runs and cause flaky e2e behavior. Please capture the prior value before mutation and revert it in cleanup.

🤖 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/e2e/test-nemoclaw-policy-local-plugin.sh` around lines 25 - 29, Before
setting the global OpenShell setting, read and store the current value of
agent_policy_proposals_enabled (use "${OPENSHELL_BIN} settings get --global
--key agent_policy_proposals_enabled") into a variable (e.g.,
prior_agent_policy_proposals_enabled), then keep that variable in the script
scope and in the cleanup function restore it by calling "${OPENSHELL_BIN}
settings set --global --key agent_policy_proposals_enabled --value
<stored_value> --yes"; ensure the cleanup function (cleanup) is registered to
run on exit (trap) so the original global setting is always reverted.

@cv cv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed with the PR review advisor rubric (static patch review only; not evaluating CI/mergeability). Requesting changes as this PR is currently opened against main and includes the lower stack blockers:

  1. Provider profile import still treats every non-zero openshell provider list-profiles -o json as unsupported (src/lib/onboard/provider-profiles.ts:116). Real OpenShell/runtime failures should not be hidden as compatibility fallback.
  2. Provider profile endpoint ports are accepted with Number(endpoint.port) <= 0 in both the policy layer and plugin client paths, allowing NaN to be serialized into generated policy/proposals.
  3. The OpenClaw access client hardcodes proxy requests to http://policy.local:80..., ignoring configured OPENSHELL_POLICY_LOCAL_URL port/host in proxy mode.
  4. URL resource normalization only maps GitHub hosts to preset ids, so non-GitHub URLs can be transformed into unknown ids that list_presets never returns.

Incremental note for this PR: scripts/install-provider-tools.sh:35 installs third-party npm CLIs globally as root during the image build. Versions are pinned, which is good, but package lifecycle scripts still execute. Please either use --ignore-scripts where compatible or document/test why lifecycle scripts are required and trusted for these packages.

If this PR is intended to be reviewed as only the sandbox-tool layer, retarget it onto the parent PR after the lower-layer fixes land; as submitted against main, these blockers are in this PR's diff.

@wscurran wscurran added area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery feature PR adds or expands user-visible functionality and removed Sandbox labels Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery feature PR adds or expands user-visible functionality integration: openclaw OpenClaw integration behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants