feat(hermes): request network access through policy local#3750
feat(hermes): request network access through policy local#3750cheese-head wants to merge 9 commits into
Conversation
📝 WalkthroughWalkthroughThis PR introduces OpenShell network and provider access request capabilities across NemoClaw. A new TypeScript access-client library communicates with OpenShell's policy.local endpoint via HTTP proxy, manages provider profiles as dynamic presets, and handles access proposal workflows. Two new OpenClaw tools (openshell_provider_access and openshell_network_access) are registered in both NemoClaw SDK and Hermes plugin. Ten provider profile configurations define access rules. E2E tests validate the full request-approval-applied lifecycle. ChangesAccess & Policy System
OpenClaw Tool Registration & Implementation
Runtime Guidance & Agent Instructions
Onboarding Integration
End-to-End Testing & Validation
Documentation
🎯 4 (Complex) | ⏱️ ~60 minutes
🚥 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
Comment |
|
✨ Thanks for submitting this detailed PR to enhance the policy for requesting network access through a local policy, which improves the reliability of the Docker setup and E2E testing. This change aims to defer Docker daemon reachability failures to onboarding preflight in non-interactive installer runs, allowing the installer to finish setup and report Docker remediation through the same preflight path. |
There was a problem hiding this comment.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
test/e2e/docs/parity-map.yaml (1)
1-1:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd required SPDX license header to this YAML file.
This file is missing the required SPDX copyright + Apache-2.0 header.
As per coding guidelines: "
**/*.{js,ts,tsx,jsx,sh,yaml,yml,json,md,mdx}: Every source file must include an SPDX license header for copyright and Apache-2.0 license".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/e2e/docs/parity-map.yaml` at line 1, Add the required SPDX copyright + Apache-2.0 header to the top of the YAML file so the file begins with the standard SPDX license header comment (e.g. a YAML comment block containing the copyright and "SPDX-License-Identifier: Apache-2.0") above the existing top-level "scripts:" key in parity-map.yaml.
🧹 Nitpick comments (8)
src/lib/onboard.ts (1)
9751-9751: Run the onboarding E2E set for this orchestration change.Since this call changes post-gateway onboarding flow, run the recommended jobs before merge:
cloud-e2e,sandbox-operations-e2e,rebuild-openclaw-e2e,channels-stop-start-e2e,messaging-compatible-endpoint-e2e,hermes-discord-e2e,hermes-slack-e2e, andopenshell-gateway-upgrade-e2e.As per coding guidelines
src/lib/onboard.ts: “E2E test recommendation: cloud-e2e, sandbox-operations-e2e, rebuild-openclaw-e2e, channels-stop-start-e2e, messaging-compatible-endpoint-e2e, hermes-discord-e2e, hermes-slack-e2e, openshell-gateway-upgrade-e2e.”🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/onboard.ts` at line 9751, This change invokes ensureProviderProfilesAvailable() and alters post-gateway onboarding flow in src/lib/onboard.ts; before merging, run the recommended E2E job set: 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 to validate the orchestration change affecting ensureProviderProfilesAvailable().Dockerfile (1)
32-33: Run container E2E coverage for these image-layer changes.These changes alter sandbox image contents and startup-time behavior; run the Docker-targeted E2E jobs before merge to validate runtime parity.
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, 633-634
🤖 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, The Dockerfile change adding COPY of scripts/install-provider-tools.sh (and the other modified sections around lines 62-67, 254-255, 633-634) modifies sandbox image contents and startup behavior, so before merging run the full Docker-targeted E2E suite: cloud-e2e, sandbox-survival-e2e, hermes-e2e, and rebuild-openclaw-e2e to validate runtime parity and startup behavior; if any test fails, iterate on the Dockerfile and scripts/install-provider-tools.sh changes until the E2E jobs pass.Dockerfile.base (1)
75-77: Validate base-image rebuild path with the recommended E2E subset.Because this changes the cached base image layer content, run the base-impact E2Es to catch regressions that unit tests cannot see.
As per coding guidelines: "
Dockerfile.base: This file affects the sandbox container image... E2E test recommendation: cloud-e2e, sandbox-survival-e2e, hermes-e2e, rebuild-openclaw-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 `@Dockerfile.base` around lines 75 - 77, This change modifies Dockerfile.base by copying and running /usr/local/lib/nemoclaw/install-provider-tools.sh which affects the cached base image layer; validate the base-image rebuild path by running the recommended E2E subset (cloud-e2e, sandbox-survival-e2e, hermes-e2e, rebuild-openclaw-e2e) either locally or by triggering the corresponding CI job, record the results in the PR (pass/fail and any logs), and if failures appear, revert or fix the Dockerfile.base / install-provider-tools.sh change and re-run the same E2E subset until green.docs/reference/nemoclaw-openshell-integration.md (2)
6-8: ⚡ Quick winRemove routine bold emphasis in prose (LLM pattern detected).
Bold on basic descriptive bullets should be avoided unless it is a UI label, parameter name, or true warning.
As per coding guidelines: "Unnecessary bold on routine instructions ... should be flagged. LLM pattern detected."
🤖 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 6 - 8, The two bullets use unnecessary bold markup for routine prose; remove the Markdown bold from the phrases "**Provider access**" and "**Network access**" so they read as normal text (Provider access and Network access) unless those phrases are actual UI labels—update the doc content in nemoclaw-openshell-integration.md by replacing the bolded instances in those list items while keeping the rest of the sentences unchanged.
3-12: ⚡ Quick winApply one-sentence-per-line formatting across prose.
Many sentences are wrapped across multiple lines; please reflow to one sentence per source line.
As per coding guidelines: "One sentence per line in source (makes diffs readable)."
Also applies to: 64-71, 167-169, 178-180
🤖 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 - 12, The prose in the NemoClaw/OpenClaw integration section contains multiple sentences wrapped across lines (e.g., the opening paragraph starting "NemoClaw gives OpenClaw agents a provider-first way..." and the bullets "Provider access attaches a host-managed credential..." and "Network access only opens a resource path..."), violating the one-sentence-per-line guideline; fix by reflowing the paragraph and each bullet so each individual sentence is placed on its own source line (preserve existing wording and punctuation), and apply the same reflow to the other prose blocks noted in the review.docs/network-policy/provider-access-requests.md (1)
24-31: ⚡ Quick winNormalize to one sentence per source line.
Several sentences are split across multiple lines, which breaks the docs diff/readability rule.
As per coding guidelines: "One sentence per line in source (makes diffs readable)."
Also applies to: 63-64, 75-77, 114-116, 125-129
🤖 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 24 - 31, Split multi-sentence paragraphs so each sentence occupies its own source line: reflow the block that begins "NemoClaw agents can ask OpenShell for additional access..." and place the sentence starting "The sandbox can submit a request, but it cannot approve its own request." on its own line, do the same for the sentence starting "OpenShell records the proposal, waits for operator approval, and only then attaches provider credentials or updates network policy.", and ensure the "Use provider access when the task needs an account..." vs "Use network-only access when the task only needs unauthenticated reachability." sentences are each on separate lines; apply the same one-sentence-per-line normalization to the other identified groups that start with "Use provider..." / "Use network-only..." and the sentence groups around the phrases found in the other noted ranges.test/hermes-plugin-handlers.test.ts (1)
109-145: ⚡ Quick winAssert the proposal uses the provider-profile rule, not just the preset name.
This fixture gives
githuba provider-profile-specific rule, but the test only checksprovider_profilemetadata and the request path. A merge bug can ignore the provider profile’sruleentirely and still pass. Please assert oncalls[0].payload.operations[0].addRule.rule.Also applies to: 180-185
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/hermes-plugin-handlers.test.ts` around lines 109 - 145, The test currently only checks provider_profile metadata and request path but doesn't assert that the provider-profile-specific rule was applied; update the test after invoking ctx.tools["openshell_network_access"]["handler"] so it asserts calls[0].payload.operations[0].addRule.rule equals the expected provider-profile rule (the rule defined in module._read_provider_profiles for "github"); also add the same assertion for the similar assertions around lines 180-185 to ensure the merge didn't omit the provider profile's rule.nemoclaw/src/register.test.ts (1)
254-297: ⚡ Quick winAdd a mismatched
provider_typeregression here.This fast path is only exercised with a matching type, so the current implementation can still return
appliedfor the wrong attached provider without failing CI. Please add a case whereprovider_namematches butprovider_typediffers and verify we do not short-circuit.🤖 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/register.test.ts` around lines 254 - 297, Add a test that covers the mismatched provider_type fast-path: call the openshell_provider_access tool via getRegisteredTool(...).execute with provider_name "github" but provider_type "gitlab" while mockedGetProviderAccess.resolveValue returns an attached provider with provider_name "github" and provider_type "github", then assert that mockedGetProviderAccess was still called, that mockedCreateProviderAccessRequest was invoked (i.e. we did not short-circuit), and that the returned result does not report status "applied" (assert status is "requested" or similar expected request status) to ensure the code requires both name and type to match before short-circuiting.
🤖 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 230-237: The merge in _all_presets() only copies provider_profile
into an existing fallback entry, so when a provider preset shares a name (e.g.,
"github") its rule and other fields never override the fallback; change the
merge so provider presets take precedence: when an existing entry is found for
preset["name"] update/replace its fields with the provider preset (e.g.,
existing.update(preset) or replace by_name[preset["name"]] = dict(preset) while
preserving any required metadata), ensuring _rule_for_access_request() will see
the provider preset's rule and not the stale fallback.
In `@docs/network-policy/provider-access-requests.md`:
- Around line 33-38: Add a one-sentence introduction immediately below the "##
Access Types" header that summarizes the table purpose (e.g., "The table below
describes the different access types and what each approval grants.") so readers
get context before the table; update the section containing the "## Access
Types" header and the rows for `openshell_provider_access` and
`openshell_network_access` to ensure the intro appears directly above the table.
- Around line 152-156: The bottom section currently titled "Related Pages"
should be renamed to "Next Steps" to conform to the guideline requiring a Next
Steps section; update the heading text from "Related Pages" to "Next Steps"
while preserving the existing links (Approve or Deny Network Requests, Customize
the Network Policy, NemoClaw Provider and Resource Access Flow) and formatting
so nothing else in the "Related Pages" block (link URLs and order) changes.
In `@docs/reference/nemoclaw-openshell-integration.md`:
- Around line 191-195: Update the CLI snippet block: change the code fence
language tag from "bash" to "console" and add "$ " prompt prefixes to each
command line in the snippet (the curl invocation, the -x "$HTTPS_PROXY" line,
the -H "Authorization: Bearer $GITHUB_TOKEN" line, and the URL line) so the
example follows the docs guideline for console blocks with $ prompts.
- Around line 200-220: Add a "Next Steps" section to the bottom of the document
following the "Why the CLIs Are in the Image" section; create a heading "Next
Steps" and include a short bulleted list linking to related pages such as the
provider approval policy, OpenShell policy reference, sandbox image docs, and
provider integration guide (use their existing internal doc titles/anchors), so
readers have direct navigation to provider policy, credential setup, sandbox
access model, and integration instructions.
- Around line 1-2: Add a YAML frontmatter block at the top of the document
(immediately before the "# NemoClaw Provider and Resource Access Flow" heading)
containing title, description, keywords, topics, tags, content type, difficulty,
audience, and status fields, and then add the SPDX license header block (e.g.,
"SPDX-License-Identifier: Apache-2.0" or the project standard) immediately after
the frontmatter; ensure the frontmatter keys match the repo guideline names and
the SPDX header appears directly following the frontmatter so the file complies
with the "SPDX license header is present after frontmatter" rule.
In `@nemoclaw/src/access-client.ts`:
- Around line 379-384: The cleanProviderEndpoint function currently converts
endpoint.port with Number(...) but lets NaN, non-integers, and out-of-range
values through; update its validation to only accept a numeric integer port in
the TCP range 1–65535 and return null otherwise. Specifically, in
cleanProviderEndpoint check that typeof endpoint.host === "string", parse the
port to a number (or integer) and ensure it is not NaN, is an integer, and 1 <=
port <= 65535 before constructing the NetworkEndpoint object (otherwise return
null); keep using the same function name cleanProviderEndpoint and the output
variable NetworkEndpoint so callers remain unchanged.
In `@nemoclaw/src/index.ts`:
- Around line 877-895: When checking provider readiness, honor an explicit
requested provider_type before reusing an attached provider: read the requested
provider_type with readStringProperty(params, "provider_type") and, after
fetching the attached provider via getProviderAccess(providerName,
clientOptions), verify that the fetched provider's type field (e.g.,
provider.provider_type or provider.type as used in your provider object) matches
the requested provider_type; if it does not match, return a failed
providerAccessToolResult (or a similar result object) explaining the mismatch
instead of reporting the provider as ready. Apply the same check in the
analogous fast-path later in the file (the block around lines referenced
898-910) so both code paths use readStringProperty, getProviderAccess, and
providerAccessToolResult and consistently validate provider_type before
reporting success.
In `@src/lib/onboard.ts`:
- Around line 1795-1805: The new wrapper logic in
ensureProviderProfilesAvailable (calling
providerProfileOnboard.ensureNemoClawProviderProfiles, logging via note, and
calling policies.clearProviderProfileCache) increased the file size and
triggered the entrypoint budget gate; extract that orchestration into a new
module named provider-profiles.ts under the same lib/onboard area, implement a
function (e.g., ensureProviderProfilesOrchestrator) that performs the call to
providerProfileOnboard.ensureNemoClawProviderProfiles, handles the result
branches (unsupported, already-present with skipped), logs with note, and calls
policies.clearProviderProfileCache, then replace the body of
ensureProviderProfilesAvailable with a single thin call that imports and invokes
the new orchestrator to keep onboard.ts net-neutral in size.
In `@test/e2e/docs/parity-inventory.generated.json`:
- Around line 5412-5418: The parity inventory currently lists
test/e2e/test-hermes-policy-local-plugin.sh and
test/e2e/test-nemoclaw-policy-local-plugin.sh as legacy zero-assertion entries;
update those test scripts to emit explicit PASS:/FAIL: (or pass "..." / fail
"...") markers so scripts/e2e/extract-legacy-assertions.ts can extract
assertions, then regenerate test/e2e/docs/parity-inventory.generated.json so
each entry has a non-empty assertions array and no zero_assertion_review.todo;
additionally add the required SPDX license header at the top of
parity-inventory.generated.json (replacing the current file-starting JSON) so
the file includes the SPDX identifier before the JSON payload.
In `@test/e2e/test-hermes-policy-local-plugin.sh`:
- Around line 25-28: Don't mutate global settings permanently: instead of
calling "${OPENSHELL_BIN}" settings set --global --key
agent_policy_proposals_enabled --value true --yes, either set it only for the
test scope by using --local (replace --global with --local) or, if a global
change is required, first capture the current value (e.g.
PREV=$("${OPENSHELL_BIN}" settings get --global --key
agent_policy_proposals_enabled) ), set the new value, and register a cleanup
trap to restore it via "${OPENSHELL_BIN}" settings set --global --key
agent_policy_proposals_enabled --value "$PREV" --yes so the script leaves no
persistent side effects.
In `@test/e2e/test-nemoclaw-policy-local-plugin.sh`:
- Around line 25-28: The test sets the global OpenShell setting
agent_policy_proposals_enabled using "${OPENSHELL_BIN}" settings set but never
restores the previous value; update the test to read and store the existing
value before changing it (e.g., via "${OPENSHELL_BIN}" settings get for
agent_policy_proposals_enabled), then set the value to true for the test, and
finally restore the original value in a teardown/cleanup block (or trap) so the
global setting is returned to its prior state after the test completes.
In `@test/sandbox-build-context.test.ts`:
- Around line 90-93: The test only checked the "others" write bit (0o002),
allowing modes like 0o775 to pass; update the assertion to verify the full
write-bit mask (0o222) so only the owner write bit remains set for
stagedManifestDir. Replace the current check that uses stagedManifestDirMode &
0o002 with a check that (stagedManifestDirMode & 0o222) equals 0o200 (owner
write only), referencing the stagedManifestDirMode variable and the 0o222 mask
to locate and fix the assertion.
---
Outside diff comments:
In `@test/e2e/docs/parity-map.yaml`:
- Line 1: Add the required SPDX copyright + Apache-2.0 header to the top of the
YAML file so the file begins with the standard SPDX license header comment (e.g.
a YAML comment block containing the copyright and "SPDX-License-Identifier:
Apache-2.0") above the existing top-level "scripts:" key in parity-map.yaml.
---
Nitpick comments:
In `@Dockerfile`:
- Around line 32-33: The Dockerfile change adding COPY of
scripts/install-provider-tools.sh (and the other modified sections around lines
62-67, 254-255, 633-634) modifies sandbox image contents and startup behavior,
so before merging run the full Docker-targeted E2E suite: cloud-e2e,
sandbox-survival-e2e, hermes-e2e, and rebuild-openclaw-e2e to validate runtime
parity and startup behavior; if any test fails, iterate on the Dockerfile and
scripts/install-provider-tools.sh changes until the E2E jobs pass.
In `@Dockerfile.base`:
- Around line 75-77: This change modifies Dockerfile.base by copying and running
/usr/local/lib/nemoclaw/install-provider-tools.sh which affects the cached base
image layer; validate the base-image rebuild path by running the recommended E2E
subset (cloud-e2e, sandbox-survival-e2e, hermes-e2e, rebuild-openclaw-e2e)
either locally or by triggering the corresponding CI job, record the results in
the PR (pass/fail and any logs), and if failures appear, revert or fix the
Dockerfile.base / install-provider-tools.sh change and re-run the same E2E
subset until green.
In `@docs/network-policy/provider-access-requests.md`:
- Around line 24-31: Split multi-sentence paragraphs so each sentence occupies
its own source line: reflow the block that begins "NemoClaw agents can ask
OpenShell for additional access..." and place the sentence starting "The sandbox
can submit a request, but it cannot approve its own request." on its own line,
do the same for the sentence starting "OpenShell records the proposal, waits for
operator approval, and only then attaches provider credentials or updates
network policy.", and ensure the "Use provider access when the task needs an
account..." vs "Use network-only access when the task only needs unauthenticated
reachability." sentences are each on separate lines; apply the same
one-sentence-per-line normalization to the other identified groups that start
with "Use provider..." / "Use network-only..." and the sentence groups around
the phrases found in the other noted ranges.
In `@docs/reference/nemoclaw-openshell-integration.md`:
- Around line 6-8: The two bullets use unnecessary bold markup for routine
prose; remove the Markdown bold from the phrases "**Provider access**" and
"**Network access**" so they read as normal text (Provider access and Network
access) unless those phrases are actual UI labels—update the doc content in
nemoclaw-openshell-integration.md by replacing the bolded instances in those
list items while keeping the rest of the sentences unchanged.
- Around line 3-12: The prose in the NemoClaw/OpenClaw integration section
contains multiple sentences wrapped across lines (e.g., the opening paragraph
starting "NemoClaw gives OpenClaw agents a provider-first way..." and the
bullets "Provider access attaches a host-managed credential..." and "Network
access only opens a resource path..."), violating the one-sentence-per-line
guideline; fix by reflowing the paragraph and each bullet so each individual
sentence is placed on its own source line (preserve existing wording and
punctuation), and apply the same reflow to the other prose blocks noted in the
review.
In `@nemoclaw/src/register.test.ts`:
- Around line 254-297: Add a test that covers the mismatched provider_type
fast-path: call the openshell_provider_access tool via
getRegisteredTool(...).execute with provider_name "github" but provider_type
"gitlab" while mockedGetProviderAccess.resolveValue returns an attached provider
with provider_name "github" and provider_type "github", then assert that
mockedGetProviderAccess was still called, that mockedCreateProviderAccessRequest
was invoked (i.e. we did not short-circuit), and that the returned result does
not report status "applied" (assert status is "requested" or similar expected
request status) to ensure the code requires both name and type to match before
short-circuiting.
In `@src/lib/onboard.ts`:
- Line 9751: This change invokes ensureProviderProfilesAvailable() and alters
post-gateway onboarding flow in src/lib/onboard.ts; before merging, run the
recommended E2E job set: 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 to validate the orchestration change affecting
ensureProviderProfilesAvailable().
In `@test/hermes-plugin-handlers.test.ts`:
- Around line 109-145: The test currently only checks provider_profile metadata
and request path but doesn't assert that the provider-profile-specific rule was
applied; update the test after invoking
ctx.tools["openshell_network_access"]["handler"] so it asserts
calls[0].payload.operations[0].addRule.rule equals the expected provider-profile
rule (the rule defined in module._read_provider_profiles for "github"); also add
the same assertion for the similar assertions around lines 180-185 to ensure the
merge didn't omit the provider profile's rule.
🪄 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: a55f6246-6e79-4059-992b-3ed2f9a0552a
📒 Files selected for processing (49)
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.shscripts/install.shsrc/lib/onboard.tssrc/lib/onboard/provider-profiles.tssrc/lib/policy/index.tssrc/lib/sandbox/build-context.tstest/cli.test.tstest/e2e/docs/parity-inventory.generated.jsontest/e2e/docs/parity-map.yamltest/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/install-preflight.test.tstest/policies.test.tstest/provider-profile-onboard.test.tstest/sandbox-build-context.test.tstest/sandbox-provisioning.test.tstest/validate-blueprint.test.ts
| def _all_presets(): | ||
| by_name = {preset["name"]: dict(preset) for preset in FALLBACK_PRESETS} | ||
| for preset in _provider_presets(): | ||
| existing = by_name.get(preset["name"]) | ||
| if existing: | ||
| existing["provider_profile"] = preset.get("provider_profile") | ||
| else: | ||
| by_name[preset["name"]] = preset |
There was a problem hiding this comment.
Provider profiles never override the fallback rule on name collisions.
When a loaded provider profile reuses a built-in preset name like github, _all_presets() only copies provider_profile onto the fallback entry. _rule_for_access_request() then still builds the proposal from the stale fallback rule, so provider-profile-backed presets never actually drive the requested policy.
Suggested fix
def _all_presets():
by_name = {preset["name"]: dict(preset) for preset in FALLBACK_PRESETS}
for preset in _provider_presets():
- existing = by_name.get(preset["name"])
- if existing:
- existing["provider_profile"] = preset.get("provider_profile")
- else:
- by_name[preset["name"]] = preset
+ by_name[preset["name"]] = dict(preset)
return [by_name[name] for name in sorted(by_name)]🤖 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 230 - 237, The merge in
_all_presets() only copies provider_profile into an existing fallback entry, so
when a provider preset shares a name (e.g., "github") its rule and other fields
never override the fallback; change the merge so provider presets take
precedence: when an existing entry is found for preset["name"] update/replace
its fields with the provider preset (e.g., existing.update(preset) or replace
by_name[preset["name"]] = dict(preset) while preserving any required metadata),
ensuring _rule_for_access_request() will see the provider preset's rule and not
the stale fallback.
| ## Access Types | ||
|
|
||
| | Type | Tool | What approval grants | | ||
| | ---- | ---- | -------------------- | | ||
| | Provider access | `openshell_provider_access` | A host-managed provider attachment, provider policy, and credential placeholders when the provider has credentials. | | ||
| | Network-only access | `openshell_network_access` | Network reachability for an approved preset. It does not attach credentials or account identity. | |
There was a problem hiding this comment.
Add an introductory sentence under Access Types.
## Access Types starts directly with a table, but new docs sections should open with a brief introductory sentence.
As per coding guidelines: "Sections use H2 and H3, each starting with an introductory sentence."
🤖 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 - 38, Add a
one-sentence introduction immediately below the "## Access Types" header that
summarizes the table purpose (e.g., "The table below describes the different
access types and what each approval grants.") so readers get context before the
table; update the section containing the "## Access Types" header and the rows
for `openshell_provider_access` and `openshell_network_access` to ensure the
intro appears directly above the table.
| ## Related Pages | ||
|
|
||
| - [Approve or Deny Network Requests](approve-network-requests.md) explains the operator approval workflow for access proposals. | ||
| - [Customize the Network Policy](customize-network-policy.md) explains persistent policy edits and presets. | ||
| - [NemoClaw Provider and Resource Access Flow](../reference/nemoclaw-openshell-integration.md) provides the architecture reference for this feature. |
There was a problem hiding this comment.
Use a Next Steps section name at the bottom.
This new page ends with Related Pages; the required bottom section label is Next Steps.
As per coding guidelines: "A 'Next Steps' section at the bottom links to related pages."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/network-policy/provider-access-requests.md` around lines 152 - 156, The
bottom section currently titled "Related Pages" should be renamed to "Next
Steps" to conform to the guideline requiring a Next Steps section; update the
heading text from "Related Pages" to "Next Steps" while preserving the existing
links (Approve or Deny Network Requests, Customize the Network Policy, NemoClaw
Provider and Resource Access Flow) and formatting so nothing else in the
"Related Pages" block (link URLs and order) changes.
| # NemoClaw Provider and Resource Access Flow | ||
|
|
There was a problem hiding this comment.
Add required frontmatter and SPDX header.
This new docs page is missing frontmatter metadata and the SPDX license header block.
As per coding guidelines: "When reviewing new pages, verify: SPDX license header is present after frontmatter" and "Frontmatter includes title, description, keywords, topics, tags, content type, difficulty, audience, and status fields."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/reference/nemoclaw-openshell-integration.md` around lines 1 - 2, Add a
YAML frontmatter block at the top of the document (immediately before the "#
NemoClaw Provider and Resource Access Flow" heading) containing title,
description, keywords, topics, tags, content type, difficulty, audience, and
status fields, and then add the SPDX license header block (e.g.,
"SPDX-License-Identifier: Apache-2.0" or the project standard) immediately after
the frontmatter; ensure the frontmatter keys match the repo guideline names and
the SPDX header appears directly following the frontmatter so the file complies
with the "SPDX license header is present after frontmatter" rule.
| ```bash | ||
| curl -x "$HTTPS_PROXY" \ | ||
| -H "Authorization: Bearer $GITHUB_TOKEN" \ | ||
| https://api.github.com/user | ||
| ``` |
There was a problem hiding this comment.
Switch CLI example to console block with $ prompt.
This CLI snippet uses bash; docs CLI examples must use console and include $ prompt prefixes.
As per coding guidelines: "CLI code blocks must use the console language tag with $ prompt prefix. Flag bash or shell for CLI examples."
🤖 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 191 - 195,
Update the CLI snippet block: change the code fence language tag from "bash" to
"console" and add "$ " prompt prefixes to each command line in the snippet (the
curl invocation, the -x "$HTTPS_PROXY" line, the -H "Authorization: Bearer
$GITHUB_TOKEN" line, and the URL line) so the example follows the docs guideline
for console blocks with $ prompts.
| 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.
Entrypoint budget gate is failing with this helper in onboard.ts.
Line 1795 introduces additional orchestration in this file, and CI is already failing onboard-entrypoint-budget (+16 lines). Please move this wrapper logic into src/lib/onboard/provider-profiles.ts and keep only a thin call here so src/lib/onboard.ts returns to net-neutral growth.
🤖 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 logic in
ensureProviderProfilesAvailable (calling
providerProfileOnboard.ensureNemoClawProviderProfiles, logging via note, and
calling policies.clearProviderProfileCache) increased the file size and
triggered the entrypoint budget gate; extract that orchestration into a new
module named provider-profiles.ts under the same lib/onboard area, implement a
function (e.g., ensureProviderProfilesOrchestrator) that performs the call to
providerProfileOnboard.ensureNemoClawProviderProfiles, handles the result
branches (unsupported, already-present with skipped), logs with note, and calls
policies.clearProviderProfileCache, then replace the body of
ensureProviderProfilesAvailable with a single thin call that imports and invokes
the new orchestrator to keep onboard.ts net-neutral in size.
| { | ||
| "script": "test/e2e/test-hermes-policy-local-plugin.sh", | ||
| "assertions": [], | ||
| "zero_assertion_review": { | ||
| "reason": "TODO: review legacy entrypoint for assertions not expressed as PASS/FAIL output" | ||
| } | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Verify whether the new E2E scripts emit recognizable PASS/FAIL markers
# and whether extraction logic can classify them.
echo "== Inspect policy.local scripts for assertion markers =="
rg -n -C2 'PASS|FAIL|\$PASS|\$FAIL|assert|check' test/e2e/test-hermes-policy-local-plugin.sh test/e2e/test-nemoclaw-policy-local-plugin.sh || true
echo
echo "== Inspect extractor heuristics used to populate parity inventory =="
rg -n -C3 'extract-legacy-assertions|PASS|FAIL|zero_assertion|assertions' scripts/e2e/extract-legacy-assertions.ts || true
echo
echo "== Confirm parity inventory entries for the two scripts =="
rg -n -C2 'test-hermes-policy-local-plugin.sh|test-nemoclaw-policy-local-plugin.sh|zero_assertion_review' test/e2e/docs/parity-inventory.generated.jsonRepository: NVIDIA/NemoClaw
Length of output: 7420
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect extractor logic around how it detects PASS/FAIL markers
echo "== Extractor: find where PASS/FAIL are matched =="
rg -n "PASS|FAIL|pass|fail|extractDirectOutput|extractAssertionsFromText|mapped" scripts/e2e/extract-legacy-assertions.ts
echo
echo "== Extractor: show extractAssertionsFromText + helpers =="
sed -n '120,260p' scripts/e2e/extract-legacy-assertions.ts
echo
echo "== Scripts: search for PASS:/FAIL: markers (case-sensitive) =="
rg -n 'PASS:\s|FAIL:\s' test/e2e/test-hermes-policy-local-plugin.sh test/e2e/test-nemoclaw-policy-local-plugin.sh || true
echo
echo "== Scripts: search for PASS/FAIL words (case-insensitive) =="
rg -n -i '\bpass\b|\bfail\b' test/e2e/test-hermes-policy-local-plugin.sh test/e2e/test-nemoclaw-policy-local-plugin.sh || trueRepository: NVIDIA/NemoClaw
Length of output: 6068
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== File header check (first 30 lines) =="
sed -n '1,30p' test/e2e/docs/parity-inventory.generated.json | cat -nRepository: NVIDIA/NemoClaw
Length of output: 1376
Avoid shipping new policy.local tests as zero-assertion legacy entries.
test/e2e/docs/parity-inventory.generated.jsonnow liststest/e2e/test-hermes-policy-local-plugin.shandtest/e2e/test-nemoclaw-policy-local-plugin.shasassertions: []with the legacyzero_assertion_review.reasonTODO (lines 5412-5418 and 8955-8961).scripts/e2e/extract-legacy-assertions.tsonly extracts legacy assertions fromPASS:/FAIL:(andpass "..."/fail "..."calls); these scripts contain none of the expected markers, so they can’t be validated by the parity tooling as PASS/FAIL-covered entrypoints.test/e2e/docs/parity-inventory.generated.jsonis missing the required SPDX license header (file starts immediately with{ "generated_by": ... }).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@test/e2e/docs/parity-inventory.generated.json` around lines 5412 - 5418, The
parity inventory currently lists test/e2e/test-hermes-policy-local-plugin.sh and
test/e2e/test-nemoclaw-policy-local-plugin.sh as legacy zero-assertion entries;
update those test scripts to emit explicit PASS:/FAIL: (or pass "..." / fail
"...") markers so scripts/e2e/extract-legacy-assertions.ts can extract
assertions, then regenerate test/e2e/docs/parity-inventory.generated.json so
each entry has a non-empty assertions array and no zero_assertion_review.todo;
additionally add the required SPDX license header at the top of
parity-inventory.generated.json (replacing the current file-starting JSON) so
the file includes the SPDX identifier before the JSON payload.
| "${OPENSHELL_BIN}" settings set --global \ | ||
| --key agent_policy_proposals_enabled \ | ||
| --value true \ | ||
| --yes |
There was a problem hiding this comment.
Avoid persistent global config side effects in this E2E script.
Line 25 enables agent_policy_proposals_enabled globally, but cleanup never restores the prior value. That can contaminate subsequent tests in shared environments.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@test/e2e/test-hermes-policy-local-plugin.sh` around lines 25 - 28, Don't
mutate global settings permanently: instead of calling "${OPENSHELL_BIN}"
settings set --global --key agent_policy_proposals_enabled --value true --yes,
either set it only for the test scope by using --local (replace --global with
--local) or, if a global change is required, first capture the current value
(e.g. PREV=$("${OPENSHELL_BIN}" settings get --global --key
agent_policy_proposals_enabled) ), set the new value, and register a cleanup
trap to restore it via "${OPENSHELL_BIN}" settings set --global --key
agent_policy_proposals_enabled --value "$PREV" --yes so the script leaves no
persistent side effects.
| "${OPENSHELL_BIN}" settings set --global \ | ||
| --key agent_policy_proposals_enabled \ | ||
| --value true \ | ||
| --yes |
There was a problem hiding this comment.
Restore global settings after the test to avoid cross-test state leakage.
Line 25 mutates a global OpenShell setting and never restores it. This can make later E2E jobs pass/fail depending on execution order.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@test/e2e/test-nemoclaw-policy-local-plugin.sh` around lines 25 - 28, The test
sets the global OpenShell setting agent_policy_proposals_enabled using
"${OPENSHELL_BIN}" settings set but never restores the previous value; update
the test to read and store the existing value before changing it (e.g., via
"${OPENSHELL_BIN}" settings get for agent_policy_proposals_enabled), then set
the value to true for the test, and finally restore the original value in a
teardown/cleanup block (or trap) so the global setting is returned to its prior
state after the test completes.
| const stagedManifestDirMode = fs.statSync(stagedManifestDir).mode & 0o777; | ||
| expect(stagedManifestDirMode & 0o555).toBe(0o555); | ||
| expect(stagedManifestDirMode & 0o002).toBe(0); | ||
| expect((fs.statSync(stagedManifest).mode & 0o777).toString(8)).toBe("644"); |
There was a problem hiding this comment.
Permission mask check misses owner/group write bits.
Line 92 only asserts “others write” is off. Modes like 0o775 still pass, which weakens this regression guard. Use a full write-bit mask check instead.
Suggested fix
- expect(stagedManifestDirMode & 0o002).toBe(0);
+ expect(stagedManifestDirMode & 0o222).toBe(0);📝 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.
| const stagedManifestDirMode = fs.statSync(stagedManifestDir).mode & 0o777; | |
| expect(stagedManifestDirMode & 0o555).toBe(0o555); | |
| expect(stagedManifestDirMode & 0o002).toBe(0); | |
| expect((fs.statSync(stagedManifest).mode & 0o777).toString(8)).toBe("644"); | |
| const stagedManifestDirMode = fs.statSync(stagedManifestDir).mode & 0o777; | |
| expect(stagedManifestDirMode & 0o555).toBe(0o555); | |
| expect(stagedManifestDirMode & 0o222).toBe(0); | |
| expect((fs.statSync(stagedManifest).mode & 0o777).toString(8)).toBe("644"); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@test/sandbox-build-context.test.ts` around lines 90 - 93, The test only
checked the "others" write bit (0o002), allowing modes like 0o775 to pass;
update the assertion to verify the full write-bit mask (0o222) so only the owner
write bit remains set for stagedManifestDir. Replace the current check that uses
stagedManifestDirMode & 0o002 with a check that (stagedManifestDirMode & 0o222)
equals 0o200 (owner write only), referencing the stagedManifestDirMode variable
and the 0o222 mask to locate and fix the assertion.
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). The incremental installer Docker reachability change looks reasonable, but I'm requesting changes because this PR is opened against main and carries the unresolved lower-stack blockers in its diff:
- Provider-profile import treats every non-zero
openshell provider list-profiles -o jsonas unsupported instead of throwing for real OpenShell/runtime failures. - Provider endpoint port validation accepts
NaNin the policy and plugin client paths. - policy.local proxy transport hardcodes
http://policy.local:80...instead of honoring configuredOPENSHELL_POLICY_LOCAL_URLhost/port/path. - Resource URL normalization only maps GitHub hosts, so non-GitHub URLs become unknown hostnames rather than advertised preset ids.
/v1/providerserrors are caught broadly and hidden as older-OpenShell fallback behavior.- Provider-profile collisions preserve fallback rules instead of using provider-profile rules as the source of truth.
Please either retarget this PR onto its parent stack after those blockers are fixed, or include the fixes here. Also, the PR title currently says Hermes policy.local but the incremental change is installer Docker reachability deferral; please rename it to match the diff.
Summary
Defers Docker daemon reachability failures to onboarding preflight in non-interactive installer runs. This lets the installer finish setup and report Docker remediation through the same preflight path instead of failing early in
ensure_docker.Changes
scripts/install.shso root and non-root non-interactive flows warn and continue when Docker is installed but not reachable.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
openshell_network_accessandopenshell_provider_accessfor access workflow managementgh,glab, etc.)Documentation
Improvements