Skip to content

feat(plugin): add provider-first access tools#3749

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

feat(plugin): add provider-first access tools#3749
cheese-head wants to merge 7 commits into
NVIDIA:mainfrom
cheese-head:policy-access/provider-first-tools

Conversation

@cheese-head

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

Copy link
Copy Markdown
Contributor

Summary

Adds provider-first access tools for OpenClaw so agents can request account-backed OpenShell providers before falling back to network-only access. This replaces the earlier one-tool-per-network-operation shape with openshell_provider_access and openshell_network_access.

Changes

  • Extend nemoclaw/src/access-client.ts with provider attachment listing, provider request proposals, provider status checks, and credential usage guidance.
  • Register openshell_provider_access and consolidated openshell_network_access in the OpenClaw plugin.
  • Update runtime context guidance to prefer provider access for authenticated or account-backed work.
  • Update OpenClaw plugin tests and e2e helpers for the consolidated tool contract.
  • Add docs/network-policy/provider-access-requests.md and update network policy/reference docs.

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

Release Notes

  • New Features

    • Agents can now request network resource access through presets and provider-backed credentials for integrated services.
    • Added support for multiple provider profiles (GitHub, Discord, Slack, PyPI, Telegram, Jira, npm, Brave, Homebrew, HuggingFace, local inference, and more).
    • Provider access management tools enable agents to list available providers, check request status, and submit access requests with optional approval polling.
  • Documentation

    • Added comprehensive guides for provider access workflows and network-only access patterns.
    • Expanded integration documentation covering credential handling and proxy usage.
  • Bug Fixes

    • Improved configuration file handling for empty or malformed JSON files.

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 a complete provider-backed and network-only access request system for NemoClaw agents through OpenShell. It defines 10 new provider profiles, creates a client library for access proposals and polling, registers two new OpenClaw tools, integrates provider profiles with policy management, updates Docker builds to stage tools, and includes comprehensive testing and documentation.

Changes

OpenShell Access Request System

Layer / File(s) Summary
Provider profile catalog
nemoclaw-blueprint/provider-profiles/*.yaml, nemoclaw-blueprint/policies/presets/github.yaml
Adds 10 new provider profiles (brave, brew, discord, huggingface, jira, local-inference, npm, pypi, slack, telegram) and expands GitHub preset with gh, curl, and node binaries.
Access client API
nemoclaw/src/access-client.ts, nemoclaw/src/access-client.test.ts
Core HTTP client for OpenShell policy.local: submits access/provider proposals, polls request state, lists presets/providers, handles proxy transport, merges credential placeholders with policy state, and exports typed contracts (AccessStatus, AccessRequestResponse, ProviderAccessInfo).
NemoClaw tool registration
nemoclaw/src/index.ts
Registers openshell_provider_access and openshell_network_access tools with list/check/request actions, parameter validation, credential usage guidance, and result formatting.
Hermes network access tool
agents/hermes/plugin/__init__.py, agents/hermes/plugin/plugin.yaml
Hermes-specific network access tool implementation with preset discovery, provider profile override support, chunked-transfer decoding, and session banner update.
Provider profile onboarding
src/lib/onboard/provider-profiles.ts, src/lib/onboard.ts
Ensures local NemoClaw profiles exist and imports missing ones into OpenShell; integrated into onboarding after gateway step.
Policy preset integration
src/lib/policy/index.ts
Merges OpenShell provider profiles with built-in presets, generates preset YAML from profiles, caches results, exports provider profile helpers.
Docker infrastructure
Dockerfile, Dockerfile.base, scripts/install-provider-tools.sh, src/lib/sandbox/build-context.ts, scripts/generate-openclaw-config.py
Stages provider tools installer script, creates nemoclaw plugin directory, initializes config.json as valid JSON, registers nemoclaw OpenClaw plugin with load path.
Documentation
docs/network-policy/provider-access-requests.md, docs/reference/nemoclaw-openshell-integration.md, docs/index.md, nemoclaw/src/runtime-context.ts
Comprehensive guides on provider vs. network-only access, request workflows, credential placeholders, proxy usage, and integration diagrams; workflow guidance in runtime context.
End-to-end testing
test/e2e/hermes-policy-local-runner.py, test/e2e/nemoclaw-policy-local-runner.mjs, test/e2e/test-hermes-policy-local-plugin.sh, test/e2e/test-nemoclaw-policy-local-plugin.sh
E2E runners and test scripts validating full preset listing → request submission → approval → status checking workflow for both Hermes and NemoClaw.
Integration & unit tests
nemoclaw/src/register.test.ts, test/generate-openclaw-config.test.ts, test/hermes-plugin-handlers.test.ts, test/policies.test.ts, test/provider-profile-onboard.test.ts, test/sandbox-build-context.test.ts, test/validate-blueprint.test.ts, nemoclaw/src/onboard/config.test.ts
Tool registration, policy.local API, provider profile handling, config generation, OpenClaw plugin validation, and profile onboarding scenario tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

OpenShell, NemoClaw CLI, Integration: Hermes, enhancement: testing

Suggested reviewers

  • ericksoa
  • kjw3

Poem

🐰 Whiskers twitch with delight
Provider profiles hop in, network access takes flight,
Access client bounds through policy gates,
Tool registration celebrates—credentials await!
OpenShell and sandbox dance in the night. 🌙

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.35% 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(plugin): add provider-first access tools' directly summarizes the main change: adding provider-first access tools to the OpenClaw plugin. It aligns with the PR's core objective of introducing openshell_provider_access and openshell_network_access tools.
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/provider-first-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.

🔧 Trivy (0.69.3)

Trivy execution failed: 2026-05-21T19:22:05Z FATAL Fatal error run error: fs scan error: scan error: scan failed: failed analysis: post analysis error: post analysis error: ansible scan error: fs filter error: fs filter error: walk error range error: stat gitleaks-report-19.json: no such file or directory: range error: stat gitleaks-report-19.json: no such file or directory


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

@wscurran

Copy link
Copy Markdown
Contributor

✨ Thanks for submitting this detailed PR to add provider-first access tools for OpenClaw. This change aims to improve the reliability and usability of the OpenClaw plugin by adding provider attachment listing, provider request proposals, and credential usage guidance.

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

Caution

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

⚠️ Outside diff range comments (1)
docs/reference/nemoclaw-openshell-integration.md (1)

221-221: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add a "Next Steps" section.

The coding guidelines for docs/** page structure require a "Next Steps" section at the bottom linking to related pages.

📚 Suggested section
 This keeps the agent experience smooth without weakening the sandbox access
 model.
+
+## Next Steps
+
+- [Approve Network Requests](../network-policy/approve-network-requests.md) — Operator workflow for approving provider and network access requests.
+- [Provider Access Requests](../network-policy/provider-access-requests.md) — Detailed guide on provider access request flows and credential placeholders.
+- [Network Policy Reference](../network-policy/index.md) — Complete network policy documentation.

As per coding guidelines, every documentation page should include a "Next Steps" section with related links.

🤖 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 221, Add a "Next
Steps" section to the bottom of the nemoclaw-openshell-integration.md page:
create a new header "## Next Steps" and list 3–5 related documentation links
(for example: Getting Started, Architecture Overview, CLI Reference,
Troubleshooting) with short one-line descriptions; ensure links use the repo's
relative doc paths and match existing link style used elsewhere in docs so the
section follows the docs/** page structure guidelines.
🧹 Nitpick comments (5)
Dockerfile (1)

32-33: Run the Docker image E2E matrix for these build-layer changes.

Given these runtime image changes, validate with the recommended jobs before merge to catch sandbox/runtime regressions.

$ gh workflow run nightly-e2e.yaml --ref <branch> -f jobs=cloud-e2e,sandbox-survival-e2e,hermes-e2e,rebuild-openclaw-e2e

As per coding guidelines, "Dockerfile: This file affects the sandbox container image... E2E test recommendation: cloud-e2e, sandbox-survival-e2e, hermes-e2e, rebuild-openclaw-e2e."

Also applies to: 62-67, 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 32 - 33, This change adds/updates the Docker COPY
step for scripts/install-provider-tools.sh in the Dockerfile and touches other
runtime layers (lines near the COPY and blocks at 62-67, 254-255), so before
merging run the recommended E2E matrix to validate sandbox/runtime behavior:
execute the GitHub Actions workflow with the cloud-e2e, sandbox-survival-e2e,
hermes-e2e, and rebuild-openclaw-e2e jobs using the command `gh workflow run
nightly-e2e.yaml --ref <branch> -f
jobs=cloud-e2e,sandbox-survival-e2e,hermes-e2e,rebuild-openclaw-e2e` and confirm
all jobs pass.
docs/network-policy/provider-access-requests.md (1)

33-40: ⚡ Quick win

Align section structure with docs page rules.

Several H2 sections start immediately with lists/tables, and the bottom section should be named Next Steps instead of Related Pages.

As per coding guidelines, "Sections use H2 and H3, each starting with an introductory sentence." and "A 'Next Steps' section at the bottom links to related pages."

Also applies to: 40-78, 78-117, 152-157

🤖 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/network-policy/provider-access-requests.md` around lines 33 - 40, The
H2/H3 sections (notably "Access Types" and "Provider Access Flow" and others
referenced) start directly with tables/lists and the bottom "Related Pages"
should be "Next Steps"; add a one-sentence introductory lead under each H2/H3
that briefly describes the purpose of the section before the table or list, and
rename the final "Related Pages" section to "Next Steps" while ensuring it
contains the same links — update section headers "Access Types", "Provider
Access Flow", and the final "Related Pages" heading accordingly across the
referenced blocks so every section begins with an introductory sentence and the
bottom section follows the "Next Steps" convention.
src/lib/onboard.ts (1)

9751-9751: Run the onboarding E2E matrix before merge.

This insertion changes core onboarding sequencing, so please run the recommended onboarding E2E jobs for this file before merging.

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, The change adds a call to
ensureProviderProfilesAvailable() in src/lib/onboard.ts which affects core
onboarding sequencing; before merging, run the full onboarding E2E matrix and
ensure all tests pass: 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, revert
or adjust the ensureProviderProfilesAvailable() insertion and re-run until the
suite is green.
src/lib/policy/index.ts (1)

84-100: ⚡ Quick win

Avoid shelling out to OpenShell when validating a local preset file.

loadPresetFromFile() now calls listPresets() for collision detection, and listPresets() can invoke openshell provider list-profiles. That makes a file-only validation path depend on an external binary and a 5s timeout. Use only the on-disk preset names here, or pass provider names in from a caller that already needed them.

Also applies to: 159-170, 1058-1059

🤖 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/policy/index.ts` around lines 84 - 100, listPresets() currently calls
listOpenShellProviderPresets() (via mergeProviderProfilePresets) which shells
out to openshell; change listPresets() to avoid invoking external providers
during local file validation by returning only on-disk presets or by adding an
optional parameter to listPresets(e.g., includeProviderPresets = false) so
callers like loadPresetFromFile() can request only disk-based names; update
mergeProviderProfilePresets or its usage so provider presets are merged only
when explicitly requested (e.g., from CLI code paths that already need provider
info) and remove the implicit call to listOpenShellProviderPresets() in the
default listPresets() path.
agents/hermes/plugin/__init__.py (1)

671-707: Run the Hermes E2Es for this tool registration change.

This adds a new Hermes tool that exercises policy.local and sandbox wiring, so I'd validate it with the Hermes E2E jobs before merge.

As per coding guidelines, agents/hermes/**: This directory contains the Hermes agent. Changes affect multi-agent onboarding, health probes, and inference routing.

🤖 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 `@agents/hermes/plugin/__init__.py` around lines 671 - 707, Run the Hermes
end-to-end test suite to validate the new tool registration for
openshell_network_access: execute the Hermes E2E jobs (multi-agent onboarding,
health probes, inference routing) and verify policy.local and sandbox wiring
behave correctly when ctx.register_tool registers the openshell_network_access
function with handler _handle_network_access; check that schema fields (action,
user_intent, resource, access, reason, duration, request_id, task_id,
wait_timeout_ms) accept expected values and enforce defaults/limits (especially
MAX_ACCESS_WAIT_MS and DEFAULT_ACCESS_WAIT_MS), confirm enum constraints work
for action/access/duration, and ensure the tool triggers correct policy/local
sandbox decisions and no regressions in agent onboarding or health probes.
🤖 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 `@agents/hermes/plugin/__init__.py`:
- Around line 435-441: The handler currently treats a missing wait_timeout_ms as
0 due to using params.get("wait_timeout_ms") with _clamp_wait_timeout, causing
schema defaults to be ignored; change _handle_check_network_access to read the
raw param (e.g., params.get("wait_timeout_ms")) and if it is None explicitly set
timeout_ms = DEFAULT_ACCESS_WAIT_MS before passing through _clamp_wait_timeout,
then call _get_access_request(request_id, timeout) as before; make the analogous
fix for the other handler instance that uses the same pattern (the block around
lines where the same get/_clamp_wait_timeout usage appears) so schema-defaulted
90s is applied in code via DEFAULT_ACCESS_WAIT_MS.

In `@docs/network-policy/provider-access-requests.md`:
- Around line 1-15: The frontmatter is missing the required topics field; add a
top-level topics key to the YAML frontmatter (alongside title, description,
keywords, tags, content.type, difficulty, audience, and status) and populate it
with an array of relevant topic strings (e.g., ["provider-access",
"network-policy"] or similar) so the page passes validation and follows the
documented frontmatter schema.

In `@docs/reference/nemoclaw-openshell-integration.md`:
- Around line 192-195: Replace the CLI code fence and example to use the console
language tag and include a leading $ prompt: change the opening fence currently
using bash (the CLI example around the curl command) to ```console and prefix
the curl invocation with "$ " so the snippet shows "$ curl -x \"$HTTPS_PROXY\"
\\" followed by the same headers and URL; ensure the closing fence remains
triple backticks.
- Line 1: This doc titled "NemoClaw Provider and Resource Access Flow" is
missing required frontmatter and the SPDX license header; add YAML frontmatter
containing title, description, keywords, topics, tags, content_type, difficulty,
audience, and status at the top of the file and then add the SPDX license header
(e.g., SPDX-License-Identifier: Apache-2.0) immediately after the frontmatter;
ensure the frontmatter values reflect the document (use the existing title
string for title) and follow the project's frontmatter field names and ordering
so tooling and linting recognize the page.
- Around line 3-11: The document contains multiple sentences on the same line
(e.g., text mentioning "NemoClaw", "OpenClaw", "provider access", and "network
access") which violates the docs rule of one sentence per line; fix by reflowing
the paragraph so each sentence is on its own line—split lines like "NemoClaw
gives OpenClaw agents..." and the subsequent bullet explanations into
single-sentence lines and apply the same one-sentence-per-line pattern to the
rest of the file.

In `@nemoclaw/src/access-client.ts`:
- Around line 946-960: The current catch around the requestJson call for
"/v1/providers" swallows all errors and returns env-only providers; change it to
only perform that fallback when the failure clearly indicates the endpoint is
absent (e.g., HTTP 404 or a specific "endpoint missing" error from
requestJson/parseAttachedProvidersResponse), and rethrow or propagate any other
errors (500s, timeouts, parse failures) so real outages surface. Concretely, in
the try/catch that wraps requestJson (and uses parseAttachedProvidersResponse
and mergeAttachedProviders), inspect the caught error for an HTTP status === 404
or the library's "not found" sentinel and return the env-only result in that
case; otherwise throw the error upward.
- Around line 685-710: The socket "end" handler in requestJsonViaHttpProxy() is
passing the raw body (variable body) straight to parseResponse(), which breaks
when the proxy sent Transfer-Encoding: chunked; detect chunked encoding by
checking the parsed header string (e.g., look for a Transfer-Encoding header
containing "chunked") and, if present, decode the chunked framing into the
actual payload before calling parseResponse(). Update the logic in the
socket.on("end") block (where header, body, statusCode, and parseResponse() are
used) to perform chunked decoding (consume each "<chunk-size>\\r\\n<data>\\r\\n"
block until a zero-size chunk) and then call parseResponse(decodedBody) instead
of parseResponse(body). Ensure you still handle non-chunked bodies unchanged and
preserve existing error handling for malformed headers/status codes.

In `@nemoclaw/src/index.ts`:
- Around line 852-856: The schemas' wait_timeout_ms property currently sets
default: 0 while runtime logic falls back to DEFAULT_ACCESS_WAIT_MS when the
field is absent, causing non-deterministic behavior; update each schema
occurrence of wait_timeout_ms (the ones constrained by MAX_ACCESS_WAIT_MS) to
use default: DEFAULT_ACCESS_WAIT_MS (keeping minimum/maximum intact) so the
schema default matches the runtime fallback, and verify the code paths that read
wait_timeout_ms (the execute/request handling logic) still respect the numeric
bounds.

In `@src/lib/onboard.ts`:
- Around line 1795-1805: The new wrapper function
ensureProviderProfilesAvailable (which calls
providerProfileOnboard.ensureNemoClawProviderProfiles with runOpenshell and
note, checks result.status and logs messages, and calls
policies.clearProviderProfileCache) must be moved out of this file to avoid
increasing onboard.ts length; extract this logic into a new module (e.g.,
provider-profiles.ts) and export a function with the same name, or remove the
extra logging here and call the underlying
providerProfileOnboard.ensureNemoClawProviderProfiles directly from onboard.ts;
ensure you keep the same behavior by preserving calls to
providerProfileOnboard.ensureNemoClawProviderProfiles, note, and
policies.clearProviderProfileCache and update imports/exports accordingly so
callers of ensureProviderProfilesAvailable continue to work.

In `@src/lib/onboard/provider-profiles.ts`:
- Around line 116-123: The current branch treats any non-zero result from the
OpenShell `list-profiles` call (checked via `list.status`) as "unsupported",
hiding real runtime failures; change the logic in the function that calls
`list-profiles` (the block using `list.status`) to only return
status:"unsupported" when the output/exit pattern clearly indicates the CLI is
absent or shows the expected "unsupported" signature, and otherwise throw an
error that includes `list.stdout` and `list.stderr` (and the exit `list.status`)
so callers can surface timeouts/process errors; update the conditional around
`list.status` to inspect the command signature (e.g., known missing-CLI text)
before returning unsupported and use a thrown Error with context for all other
non-zero cases.

In `@src/lib/policy/index.ts`:
- Around line 100-109: The listing logic (mergeProviderProfilePresets) preserves
built-in presets on name collisions but loadPreset currently prefers
provider-generated content (loadOpenShellProviderPreset), causing inconsistent
behavior; make collision resolution consistent by changing loadPreset to prefer
the built-in preset first (i.e., check the built-in preset source before calling
loadOpenShellProviderPreset) or alternatively change mergeProviderProfilePresets
to prefer provider presets—pick one canonical source and apply it consistently;
also update the analogous load logic around the 195-208 region so both listing
(mergeProviderProfilePresets/listOpenShellProviderPresets) and loading
(loadPreset/loadOpenShellProviderPreset) use the same precedence.

---

Outside diff comments:
In `@docs/reference/nemoclaw-openshell-integration.md`:
- Line 221: Add a "Next Steps" section to the bottom of the
nemoclaw-openshell-integration.md page: create a new header "## Next Steps" and
list 3–5 related documentation links (for example: Getting Started, Architecture
Overview, CLI Reference, Troubleshooting) with short one-line descriptions;
ensure links use the repo's relative doc paths and match existing link style
used elsewhere in docs so the section follows the docs/** page structure
guidelines.

---

Nitpick comments:
In `@agents/hermes/plugin/__init__.py`:
- Around line 671-707: Run the Hermes end-to-end test suite to validate the new
tool registration for openshell_network_access: execute the Hermes E2E jobs
(multi-agent onboarding, health probes, inference routing) and verify
policy.local and sandbox wiring behave correctly when ctx.register_tool
registers the openshell_network_access function with handler
_handle_network_access; check that schema fields (action, user_intent, resource,
access, reason, duration, request_id, task_id, wait_timeout_ms) accept expected
values and enforce defaults/limits (especially MAX_ACCESS_WAIT_MS and
DEFAULT_ACCESS_WAIT_MS), confirm enum constraints work for
action/access/duration, and ensure the tool triggers correct policy/local
sandbox decisions and no regressions in agent onboarding or health probes.

In `@Dockerfile`:
- Around line 32-33: This change adds/updates the Docker COPY step for
scripts/install-provider-tools.sh in the Dockerfile and touches other runtime
layers (lines near the COPY and blocks at 62-67, 254-255), so before merging run
the recommended E2E matrix to validate sandbox/runtime behavior: execute the
GitHub Actions workflow with the cloud-e2e, sandbox-survival-e2e, hermes-e2e,
and rebuild-openclaw-e2e jobs using the command `gh workflow run
nightly-e2e.yaml --ref <branch> -f
jobs=cloud-e2e,sandbox-survival-e2e,hermes-e2e,rebuild-openclaw-e2e` and confirm
all jobs pass.

In `@docs/network-policy/provider-access-requests.md`:
- Around line 33-40: The H2/H3 sections (notably "Access Types" and "Provider
Access Flow" and others referenced) start directly with tables/lists and the
bottom "Related Pages" should be "Next Steps"; add a one-sentence introductory
lead under each H2/H3 that briefly describes the purpose of the section before
the table or list, and rename the final "Related Pages" section to "Next Steps"
while ensuring it contains the same links — update section headers "Access
Types", "Provider Access Flow", and the final "Related Pages" heading
accordingly across the referenced blocks so every section begins with an
introductory sentence and the bottom section follows the "Next Steps"
convention.

In `@src/lib/onboard.ts`:
- Line 9751: The change adds a call to ensureProviderProfilesAvailable() in
src/lib/onboard.ts which affects core onboarding sequencing; before merging, run
the full onboarding E2E matrix and ensure all tests pass: 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, revert or adjust the
ensureProviderProfilesAvailable() insertion and re-run until the suite is green.

In `@src/lib/policy/index.ts`:
- Around line 84-100: listPresets() currently calls
listOpenShellProviderPresets() (via mergeProviderProfilePresets) which shells
out to openshell; change listPresets() to avoid invoking external providers
during local file validation by returning only on-disk presets or by adding an
optional parameter to listPresets(e.g., includeProviderPresets = false) so
callers like loadPresetFromFile() can request only disk-based names; update
mergeProviderProfilePresets or its usage so provider presets are merged only
when explicitly requested (e.g., from CLI code paths that already need provider
info) and remove the implicit call to listOpenShellProviderPresets() in the
default listPresets() path.
🪄 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: d20984f2-bfe0-412f-940d-c3adfeeb8adc

📥 Commits

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

📒 Files selected for processing (43)
  • Dockerfile
  • Dockerfile.base
  • agents/hermes/plugin/__init__.py
  • agents/hermes/plugin/plugin.yaml
  • docs/index.md
  • docs/network-policy/approve-network-requests.md
  • docs/network-policy/provider-access-requests.md
  • 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
  • nemoclaw/src/runtime-context.test.ts
  • nemoclaw/src/runtime-context.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/hermes-policy-local-runner.py
  • test/e2e/nemoclaw-policy-local-runner.mjs
  • test/e2e/test-hermes-policy-local-plugin.sh
  • test/e2e/test-nemoclaw-policy-local-plugin.sh
  • test/generate-openclaw-config.test.ts
  • test/hermes-plugin-handlers.test.ts
  • test/policies.test.ts
  • test/provider-profile-onboard.test.ts
  • test/sandbox-build-context.test.ts
  • test/validate-blueprint.test.ts

Comment on lines +435 to +441
def _handle_check_network_access(tool_input=None, context=None, **_kwargs):
params = tool_input if isinstance(tool_input, dict) else {}
request_id = params.get("request_id")
if not isinstance(request_id, str) or not request_id:
return json.dumps({"request_id": "", "status": "failed", "message": "Missing request_id."})
timeout = _clamp_wait_timeout(params.get("wait_timeout_ms"), 0)
return json.dumps(_tool_result(_get_access_request(request_id, timeout)))

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

Keep the schema default neutral and apply the 90s wait in code.

_handle_check_network_access() treats an omitted wait_timeout_ms as 0, but the shared schema sets the default to DEFAULT_ACCESS_WAIT_MS. If Hermes materializes defaults before invoking the handler, plain action=check calls will block for 90 seconds unexpectedly.

Also applies to: 695-700

🤖 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 `@agents/hermes/plugin/__init__.py` around lines 435 - 441, The handler
currently treats a missing wait_timeout_ms as 0 due to using
params.get("wait_timeout_ms") with _clamp_wait_timeout, causing schema defaults
to be ignored; change _handle_check_network_access to read the raw param (e.g.,
params.get("wait_timeout_ms")) and if it is None explicitly set timeout_ms =
DEFAULT_ACCESS_WAIT_MS before passing through _clamp_wait_timeout, then call
_get_access_request(request_id, timeout) as before; make the analogous fix for
the other handler instance that uses the same pattern (the block around lines
where the same get/_clamp_wait_timeout usage appears) so schema-defaulted 90s is
applied in code via DEFAULT_ACCESS_WAIT_MS.

Comment on lines +1 to +15
---
title:
page: "Provider and Network Access Requests"
nav: "Provider Access Requests"
description:
main: "How NemoClaw agents request provider-backed credentials and network-only access through OpenShell policy proposals."
agent: "Explains the provider-first access workflow, including openshell_provider_access, openshell_network_access, operator approval, credential placeholders, and when to use provider access instead of network-only policy."
keywords: ["nemoclaw provider access", "openshell provider access", "openshell_network_access", "openshell_provider_access", "provider credential placeholder"]
tags: ["openclaw", "openshell", "network_policy", "provider_access", "security", "nemoclaw"]
content:
type: how_to
difficulty: intermediate
audience: ["developer", "engineer", "security_engineer"]
status: published
---

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 missing topics frontmatter field.

This new docs page is missing topics, which is required for new pages.

Suggested minimal patch
 tags: ["openclaw", "openshell", "network_policy", "provider_access", "security", "nemoclaw"]
+topics: ["generative_ai", "ai_agents"]
 content:
   type: how_to

As per coding guidelines, "Frontmatter includes title, description, keywords, topics, tags, content type, difficulty, audience, and status fields."

📝 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
---
title:
page: "Provider and Network Access Requests"
nav: "Provider Access Requests"
description:
main: "How NemoClaw agents request provider-backed credentials and network-only access through OpenShell policy proposals."
agent: "Explains the provider-first access workflow, including openshell_provider_access, openshell_network_access, operator approval, credential placeholders, and when to use provider access instead of network-only policy."
keywords: ["nemoclaw provider access", "openshell provider access", "openshell_network_access", "openshell_provider_access", "provider credential placeholder"]
tags: ["openclaw", "openshell", "network_policy", "provider_access", "security", "nemoclaw"]
content:
type: how_to
difficulty: intermediate
audience: ["developer", "engineer", "security_engineer"]
status: published
---
---
title:
page: "Provider and Network Access Requests"
nav: "Provider Access Requests"
description:
main: "How NemoClaw agents request provider-backed credentials and network-only access through OpenShell policy proposals."
agent: "Explains the provider-first access workflow, including openshell_provider_access, openshell_network_access, operator approval, credential placeholders, and when to use provider access instead of network-only policy."
keywords: ["nemoclaw provider access", "openshell provider access", "openshell_network_access", "openshell_provider_access", "provider credential placeholder"]
tags: ["openclaw", "openshell", "network_policy", "provider_access", "security", "nemoclaw"]
topics: ["generative_ai", "ai_agents"]
content:
type: how_to
difficulty: intermediate
audience: ["developer", "engineer", "security_engineer"]
status: published
---
🤖 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/network-policy/provider-access-requests.md` around lines 1 - 15, The
frontmatter is missing the required topics field; add a top-level topics key to
the YAML frontmatter (alongside title, description, keywords, tags,
content.type, difficulty, audience, and status) and populate it with an array of
relevant topic strings (e.g., ["provider-access", "network-policy"] or similar)
so the page passes validation and follows the documented frontmatter schema.

@@ -0,0 +1,220 @@
# NemoClaw Provider and Resource Access Flow

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 required frontmatter and SPDX license header.

Every documentation page under docs/ must include frontmatter (title, description, keywords, topics, tags, content type, difficulty, audience, status) followed by an SPDX license header. As per coding guidelines, missing SPDX header in documentation should be flagged as an issue.

📋 Proposed frontmatter and header template
+---
+title:
+  page: NemoClaw Provider and Resource Access Flow
+  short: Provider Access Flow
+description: Describes the provider-first access model for granting OpenClaw agents account-backed versus network-only access through OpenShell.
+keywords: [provider access, network access, credentials, OpenShell policy, NemoClaw]
+topics: [access-control, security]
+tags: [provider, network, credentials]
+content_type: reference
+difficulty: intermediate
+audience: [operator, developer]
+status: stable
+---
+<!--
+SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+SPDX-License-Identifier: Apache-2.0
+-->
+
 # NemoClaw Provider and Resource Access Flow

As per coding guidelines, the SPDX header and frontmatter structure are required for all documentation pages.

📝 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
# NemoClaw Provider and Resource Access Flow
---
title:
page: NemoClaw Provider and Resource Access Flow
short: Provider Access Flow
description: Describes the provider-first access model for granting OpenClaw agents account-backed versus network-only access through OpenShell.
keywords: [provider access, network access, credentials, OpenShell policy, NemoClaw]
topics: [access-control, security]
tags: [provider, network, credentials]
content_type: reference
difficulty: intermediate
audience: [operator, developer]
status: stable
---
<!--
SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0
-->
# NemoClaw Provider and Resource Access Flow
🤖 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, This doc titled
"NemoClaw Provider and Resource Access Flow" is missing required frontmatter and
the SPDX license header; add YAML frontmatter containing title, description,
keywords, topics, tags, content_type, difficulty, audience, and status at the
top of the file and then add the SPDX license header (e.g.,
SPDX-License-Identifier: Apache-2.0) immediately after the frontmatter; ensure
the frontmatter values reflect the document (use the existing title string for
title) and follow the project's frontmatter field names and ordering so tooling
and linting recognize the page.

Comment on lines +3 to +11
NemoClaw gives OpenClaw agents a provider-first way to get account-backed access
to external services. The important distinction is:

- **Provider access** attaches a host-managed credential and the matching
provider policy to the sandbox.
- **Network access** only opens a resource path; it does not attach credentials.

Agents should prefer provider access whenever a task needs an account, token,
OAuth identity, API key, write operation, or service-specific CLI.

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.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Format with one sentence per line.

The source file places multiple sentences on single lines. The coding guidelines for docs/** require one sentence per line to make diffs readable.

♻️ Proposed formatting
-NemoClaw gives OpenClaw agents a provider-first way to get account-backed access
-to external services. The important distinction is:
+NemoClaw gives OpenClaw agents a provider-first way to get account-backed access to external services.
+The important distinction is:
 
 - **Provider access** attaches a host-managed credential and the matching
   provider policy to the sandbox.
 - **Network access** only opens a resource path; it does not attach credentials.
 
-Agents should prefer provider access whenever a task needs an account, token,
-OAuth identity, API key, write operation, or service-specific CLI.
+Agents should prefer provider access whenever a task needs an account, token, OAuth identity, API key, write operation, or service-specific CLI.

Apply this pattern throughout the file.

As per coding guidelines, one sentence per line is a formatting rule for all Markdown files under docs/.

📝 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
NemoClaw gives OpenClaw agents a provider-first way to get account-backed access
to external services. The important distinction is:
- **Provider access** attaches a host-managed credential and the matching
provider policy to the sandbox.
- **Network access** only opens a resource path; it does not attach credentials.
Agents should prefer provider access whenever a task needs an account, token,
OAuth identity, API key, write operation, or service-specific CLI.
NemoClaw gives OpenClaw agents a provider-first way to get account-backed access to external services.
The important distinction is:
- **Provider access** attaches a host-managed credential and the matching
provider policy to the sandbox.
- **Network access** only opens a resource path; it does not attach credentials.
Agents should prefer provider access whenever a task needs an account, token, OAuth identity, API key, write operation, or service-specific CLI.
🤖 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 3 - 11, The
document contains multiple sentences on the same line (e.g., text mentioning
"NemoClaw", "OpenClaw", "provider access", and "network access") which violates
the docs rule of one sentence per line; fix by reflowing the paragraph so each
sentence is on its own line—split lines like "NemoClaw gives OpenClaw agents..."
and the subsequent bullet explanations into single-sentence lines and apply the
same one-sentence-per-line pattern to the rest of the file.

Comment on lines +192 to +195
curl -x "$HTTPS_PROXY" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
https://api.github.com/user
```

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.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Use console language tag with $ prompt for CLI examples.

The code block uses ```bash instead of ```console with a $ prompt prefix. The coding guidelines for docs/** require CLI code blocks to use the console language tag with $ prefix.

♻️ Proposed fix
-```bash
-curl -x "$HTTPS_PROXY" \
+```console
+$ curl -x "$HTTPS_PROXY" \
   -H "Authorization: Bearer $GITHUB_TOKEN" \
   https://api.github.com/user
</details>

As per coding guidelines, CLI code blocks must use `console` with `$` prefix, not `bash` or `shell`.

<details>
<summary>🤖 Prompt for AI Agents</summary>

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 192 - 195,
Replace the CLI code fence and example to use the console language tag and
include a leading $ prompt: change the opening fence currently using bash (the
CLI example around the curl command) to ```console and prefix the curl
invocation with "$ " so the snippet shows "$ curl -x "$HTTPS_PROXY" \"
followed by the same headers and URL; ensure the closing fence remains triple
backticks.


</details>

<!-- fingerprinting:phantom:poseidon:puma -->

<!-- This is an auto-generated comment by CodeRabbit -->

Comment on lines +946 to +960
try {
const attachedProviders = await requestJson(
"GET",
"/v1/providers",
undefined,
options,
parseAttachedProvidersResponse,
);
return {
providers: mergeAttachedProviders(attachedProviders, envProviders),
};
} catch {
// Older OpenShell sandboxes do not expose /v1/providers. Preserve the
// previous behavior there, using injected credential placeholders only.
}

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

Don't collapse every /v1/providers error into an env-only result.

This fallback is only safe for the legacy "endpoint missing" case. As written, a 500, timeout, or malformed response is also treated as "no attached providers", which can make provider checks return false negatives during real outages.

🤖 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 946 - 960, The current catch
around the requestJson call for "/v1/providers" swallows all errors and returns
env-only providers; change it to only perform that fallback when the failure
clearly indicates the endpoint is absent (e.g., HTTP 404 or a specific "endpoint
missing" error from requestJson/parseAttachedProvidersResponse), and rethrow or
propagate any other errors (500s, timeouts, parse failures) so real outages
surface. Concretely, in the try/catch that wraps requestJson (and uses
parseAttachedProvidersResponse and mergeAttachedProviders), inspect the caught
error for an HTTP status === 404 or the library's "not found" sentinel and
return the env-only result in that case; otherwise throw the error upward.

Comment thread nemoclaw/src/index.ts
Comment on lines +852 to +856
wait_timeout_ms: {
type: "number",
minimum: 0,
maximum: MAX_ACCESS_WAIT_MS,
default: 0,

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

Make wait_timeout_ms deterministic.

Both tool schemas advertise default: 0, but the request branches fall back to DEFAULT_ACCESS_WAIT_MS when the field is absent. Whether a request returns immediately or blocks for 90 seconds now depends on whether the host materializes schema defaults before calling execute().

Also applies to: 916-920, 974-979, 1019-1023

🤖 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/index.ts` around lines 852 - 856, The schemas' wait_timeout_ms
property currently sets default: 0 while runtime logic falls back to
DEFAULT_ACCESS_WAIT_MS when the field is absent, causing non-deterministic
behavior; update each schema occurrence of wait_timeout_ms (the ones constrained
by MAX_ACCESS_WAIT_MS) to use default: DEFAULT_ACCESS_WAIT_MS (keeping
minimum/maximum intact) so the schema default matches the runtime fallback, and
verify the code paths that read wait_timeout_ms (the execute/request handling
logic) still respect the numeric bounds.

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 is exceeded.

Line 1795 introduces new wrapper logic in src/lib/onboard.ts, and CI is failing because this file grew by 16 lines. Please keep this file line-neutral (e.g., move this wrapper/logging into src/lib/onboard/provider-profiles.ts, or remove equivalent lines here).

🤖 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 wrapper function
ensureProviderProfilesAvailable (which calls
providerProfileOnboard.ensureNemoClawProviderProfiles with runOpenshell and
note, checks result.status and logs messages, and calls
policies.clearProviderProfileCache) must be moved out of this file to avoid
increasing onboard.ts length; extract this logic into a new module (e.g.,
provider-profiles.ts) and export a function with the same name, or remove the
extra logging here and call the underlying
providerProfileOnboard.ensureNemoClawProviderProfiles directly from onboard.ts;
ensure you keep the same behavior by preserving calls to
providerProfileOnboard.ensureNemoClawProviderProfiles, note, and
policies.clearProviderProfileCache and update imports/exports accordingly so
callers of ensureProviderProfilesAvailable continue to work.

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

Differentiate unsupported CLI from runtime/list failures.

At Line 116, any failed list-profiles call is treated as unsupported. That masks real failures (timeouts/process errors) and silently skips provider import. Gate unsupported on command-signature detection, and throw for other failures with stderr/stdout context.

Proposed 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 listing 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 treats any non-zero result from the OpenShell `list-profiles` call
(checked via `list.status`) as "unsupported", hiding real runtime failures;
change the logic in the function that calls `list-profiles` (the block using
`list.status`) to only return status:"unsupported" when the output/exit pattern
clearly indicates the CLI is absent or shows the expected "unsupported"
signature, and otherwise throw an error that includes `list.stdout` and
`list.stderr` (and the exit `list.status`) so callers can surface
timeouts/process errors; update the conditional around `list.status` to inspect
the command signature (e.g., known missing-CLI text) before returning
unsupported and use a thrown Error with context for all other non-zero cases.

Comment thread src/lib/policy/index.ts
Comment on lines +100 to +109
return mergeProviderProfilePresets(builtinPresets, listOpenShellProviderPresets());
}

/**
* Read a built-in preset by short name from `PRESETS_DIR`. Guards against
* path traversal and returns `null` if the preset does not exist.
*/
function loadPreset(name: string): string | null {
const providerPreset = loadOpenShellProviderPreset(name);
if (providerPreset) return providerPreset;

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

Keep collision resolution consistent between listing and loading.

mergeProviderProfilePresets() keeps the built-in preset entry on a name collision, but loadPreset() resolves the same name to the provider-generated YAML first. That means colliding names show built-in metadata in list/select flows while apply/remove/gateway-matching operate on different content. Pick one canonical source for colliding names instead of splitting metadata and behavior.

Suggested direction
 function loadPreset(name: string): string | null {
-  const providerPreset = loadOpenShellProviderPreset(name);
-  if (providerPreset) return providerPreset;
-
   const file = path.resolve(PRESETS_DIR, `${name}.yaml`);
   if (!file.startsWith(PRESETS_DIR + path.sep) && file !== PRESETS_DIR) {
     console.error(`  Invalid preset name: ${name}`);
     return null;
   }
-  if (!fs.existsSync(file)) {
-    console.error(`  Preset not found: ${name}`);
-    return null;
-  }
-  return fs.readFileSync(file, "utf-8");
+  if (fs.existsSync(file)) return fs.readFileSync(file, "utf-8");
+
+  const providerPreset = loadOpenShellProviderPreset(name);
+  if (providerPreset) return providerPreset;
+
+  console.error(`  Preset not found: ${name}`);
+  return null;
 }

Also applies to: 195-208

🤖 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/policy/index.ts` around lines 100 - 109, The listing logic
(mergeProviderProfilePresets) preserves built-in presets on name collisions but
loadPreset currently prefers provider-generated content
(loadOpenShellProviderPreset), causing inconsistent behavior; make collision
resolution consistent by changing loadPreset to prefer the built-in preset first
(i.e., check the built-in preset source before calling
loadOpenShellProviderPreset) or alternatively change mergeProviderProfilePresets
to prefer provider presets—pick one canonical source and apply it consistently;
also update the analogous load logic around the 195-208 region so both listing
(mergeProviderProfilePresets/listOpenShellProviderPresets) and loading
(loadPreset/loadOpenShellProviderPreset) use the same precedence.

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

  1. nemoclaw/src/access-client.ts:957 catches all /v1/providers failures and falls back to env-placeholder discovery. That hides malformed responses, HTTP 500s, timeouts, and policy.local failures as "older OpenShell" compatibility. Please catch only a known unsupported endpoint/status shape and surface other failures.
  2. nemoclaw/src/access-client.ts:380 still accepts provider-profile ports with Number(endpoint.port) <= 0, allowing NaN to pass and be serialized into proposals at line 383. Please require finite integer ports in 1..65535.
  3. nemoclaw/src/access-client.ts:658 hardcodes proxy absolute-form requests to http://policy.local:80..., ignoring configured OPENSHELL_POLICY_LOCAL_URL host/port/path in proxy mode.
  4. nemoclaw/src/index.ts:536 only normalizes GitHub URLs to canonical preset ids. Non-GitHub URLs can turn into unknown hostname ids even though list_presets advertises ids like pypi, npm, telegram, etc.
  5. nemoclaw/src/access-client.ts:472 preserves fallback rules on provider-profile name collisions and only copies provider_profile; provider profiles should replace fallback rules if they are the authoritative source when present.

Please add tests that distinguish /v1/providers unsupported from real errors, plus coverage for invalid ports, custom proxy target URLs, and non-GitHub resource normalization/rejection.

@wscurran wscurran added area: integrations Third-party service integration behavior area: providers Inference provider integrations and provider behavior feature PR adds or expands user-visible functionality and removed enhancement: provider labels Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: integrations Third-party service integration behavior area: providers Inference provider integrations and provider behavior 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