feat(plugin): add provider-first access tools#3749
Conversation
📝 WalkthroughWalkthroughThis 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. ChangesOpenShell Access Request System
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
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
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add 🔧 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 |
|
✨ 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. |
There was a problem hiding this comment.
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 winAdd 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-e2eAs 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 winAlign section structure with docs page rules.
Several H2 sections start immediately with lists/tables, and the bottom section should be named
Next Stepsinstead ofRelated 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 winAvoid shelling out to OpenShell when validating a local preset file.
loadPresetFromFile()now callslistPresets()for collision detection, andlistPresets()can invokeopenshell 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.localand 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
📒 Files selected for processing (43)
DockerfileDockerfile.baseagents/hermes/plugin/__init__.pyagents/hermes/plugin/plugin.yamldocs/index.mddocs/network-policy/approve-network-requests.mddocs/network-policy/provider-access-requests.mddocs/reference/nemoclaw-openshell-integration.mdnemoclaw-blueprint/policies/presets/github.yamlnemoclaw-blueprint/provider-profiles/brave.yamlnemoclaw-blueprint/provider-profiles/brew.yamlnemoclaw-blueprint/provider-profiles/discord.yamlnemoclaw-blueprint/provider-profiles/huggingface.yamlnemoclaw-blueprint/provider-profiles/jira.yamlnemoclaw-blueprint/provider-profiles/local-inference.yamlnemoclaw-blueprint/provider-profiles/npm.yamlnemoclaw-blueprint/provider-profiles/pypi.yamlnemoclaw-blueprint/provider-profiles/slack.yamlnemoclaw-blueprint/provider-profiles/telegram.yamlnemoclaw/src/access-client.test.tsnemoclaw/src/access-client.tsnemoclaw/src/index.tsnemoclaw/src/onboard/config.test.tsnemoclaw/src/onboard/config.tsnemoclaw/src/register.test.tsnemoclaw/src/runtime-context.test.tsnemoclaw/src/runtime-context.tsscripts/generate-openclaw-config.pyscripts/install-provider-tools.shsrc/lib/onboard.tssrc/lib/onboard/provider-profiles.tssrc/lib/policy/index.tssrc/lib/sandbox/build-context.tstest/e2e/hermes-policy-local-runner.pytest/e2e/nemoclaw-policy-local-runner.mjstest/e2e/test-hermes-policy-local-plugin.shtest/e2e/test-nemoclaw-policy-local-plugin.shtest/generate-openclaw-config.test.tstest/hermes-plugin-handlers.test.tstest/policies.test.tstest/provider-profile-onboard.test.tstest/sandbox-build-context.test.tstest/validate-blueprint.test.ts
| 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))) |
There was a problem hiding this comment.
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.
| --- | ||
| 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 | ||
| --- |
There was a problem hiding this comment.
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_toAs 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.
| --- | |
| 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 | |||
There was a problem hiding this comment.
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 FlowAs 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.
| # 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.
| 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. |
There was a problem hiding this comment.
🛠️ 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.
| 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.
| curl -x "$HTTPS_PROXY" \ | ||
| -H "Authorization: Bearer $GITHUB_TOKEN" \ | ||
| https://api.github.com/user | ||
| ``` |
There was a problem hiding this comment.
🛠️ 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 -->
| 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. | ||
| } |
There was a problem hiding this comment.
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.
| wait_timeout_ms: { | ||
| type: "number", | ||
| minimum: 0, | ||
| maximum: MAX_ACCESS_WAIT_MS, | ||
| default: 0, |
There was a problem hiding this comment.
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.
| 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(); | ||
| } |
There was a problem hiding this comment.
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.
| if (list.status !== 0) { | ||
| return { | ||
| status: "unsupported", | ||
| imported: [], | ||
| skipped: [], | ||
| message: "OpenShell provider profiles are not available; using local preset fallbacks.", | ||
| }; | ||
| } |
There was a problem hiding this comment.
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.
| 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; |
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
Reviewed with the PR review advisor rubric (static patch review only; not evaluating CI/mergeability). Requesting changes:
nemoclaw/src/access-client.ts:957catches all/v1/providersfailures 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.nemoclaw/src/access-client.ts:380still accepts provider-profile ports withNumber(endpoint.port) <= 0, allowingNaNto pass and be serialized into proposals at line 383. Please require finite integer ports in1..65535.nemoclaw/src/access-client.ts:658hardcodes proxy absolute-form requests tohttp://policy.local:80..., ignoring configuredOPENSHELL_POLICY_LOCAL_URLhost/port/path in proxy mode.nemoclaw/src/index.ts:536only normalizes GitHub URLs to canonical preset ids. Non-GitHub URLs can turn into unknown hostname ids even thoughlist_presetsadvertises ids likepypi,npm,telegram, etc.nemoclaw/src/access-client.ts:472preserves fallback rules on provider-profile name collisions and only copiesprovider_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.
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_accessandopenshell_network_access.Changes
nemoclaw/src/access-client.tswith provider attachment listing, provider request proposals, provider status checks, and credential usage guidance.openshell_provider_accessand consolidatedopenshell_network_accessin the OpenClaw plugin.docs/network-policy/provider-access-requests.mdand update network policy/reference docs.Type of Change
Verification
npx prek run --all-filespassesnpm testpassesmake docsbuilds without warnings (doc changes only)Signed-off-by: Patrick Riel priel@nvidia.com
Summary by CodeRabbit
Release Notes
New Features
Documentation
Bug Fixes