feat(onboard): bake secondary agents via NEMOCLAW_EXTRA_AGENTS_JSON - #4653
Conversation
Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughAdds support for baking secondary OpenClaw agents via NEMOCLAW_EXTRA_AGENTS_JSON: Docker ARG/ENV, onboard Dockerfile patching, config generation/validation that prepends canonical main agent, tests, and documentation. ChangesSecondary Agents Feature
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 docstrings
🧪 Generate unit tests (beta)
Comment |
Signed-off-by: Tinson Lai <tinsonl@nvidia.com> # Conflicts: # docs/reference/commands.mdx
|
🌿 Preview your docs: https://nvidia-preview-pr-4653.docs.buildwithfern.com/nemoclaw |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
docs/reference/commands.mdx (1)
1391-1391: ⚡ Quick winSplit this table description into one sentence per source line.
This row currently packs multiple sentences into one source line, which hurts reviewability and violates docs formatting rules.
As per coding guidelines, "
One sentence per line in source (makes diffs readable). Flag paragraphs where multiple sentences appear on the same line."🤖 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/commands.mdx` at line 1391, The table cell for NEMOCLAW_EXTRA_AGENTS_JSON is currently one long source line—split it so each sentence is its own source line (one-sentence-per-line rule). Break the content into separate sentences such as: 1) "NEMOCLAW_EXTRA_AGENTS_JSON is a JSON array of OpenClaw secondary-agent entries." 2) "It adds secondary agents to agents.list in the built OpenClaw config." 3) "Each entry requires id (1-32 chars, matching ^[a-z][a-z0-9_-]*$, not main), workspace, and agentDir." 4) "Both workspace and agentDir must be absolute paths under /sandbox/.openclaw/." 5) "Operators must supply per-agent tools policies and subagents.maxSpawnDepth; nothing is implicitly granted." 6) "The canonical main entry is always written first with default: true so secondary agents cannot displace it." 7) "Malformed JSON or invalid entries fail the image build with a structured error." Ensure each of the above sentences occupies its own source line in the docs.
🤖 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 `@scripts/generate-openclaw-config.mts`:
- Around line 521-529: The current containment check uses
pathValue.startsWith(AGENT_DATA_ROOT) which can be bypassed with dot segments;
first normalize the incoming path before any containment checks (e.g., use
path.posix.normalize or path.resolve to collapse "."/".." segments), then
validate the normalizedPath is absolute and verify it is under AGENT_DATA_ROOT
(ensure consistent trailing slash handling) before throwing the existing error;
update the checks around pathValue, AGENT_DATA_ROOT, and the error throws (refer
to variables pathValue, normalizedPath, AGENT_DATA_ROOT and the surrounding
validation logic in scripts/generate-openclaw-config.mts) so the normalized path
is what's tested for containment.
---
Nitpick comments:
In `@docs/reference/commands.mdx`:
- Line 1391: The table cell for NEMOCLAW_EXTRA_AGENTS_JSON is currently one long
source line—split it so each sentence is its own source line
(one-sentence-per-line rule). Break the content into separate sentences such as:
1) "NEMOCLAW_EXTRA_AGENTS_JSON is a JSON array of OpenClaw secondary-agent
entries." 2) "It adds secondary agents to agents.list in the built OpenClaw
config." 3) "Each entry requires id (1-32 chars, matching ^[a-z][a-z0-9_-]*$,
not main), workspace, and agentDir." 4) "Both workspace and agentDir must be
absolute paths under /sandbox/.openclaw/." 5) "Operators must supply per-agent
tools policies and subagents.maxSpawnDepth; nothing is implicitly granted." 6)
"The canonical main entry is always written first with default: true so
secondary agents cannot displace it." 7) "Malformed JSON or invalid entries fail
the image build with a structured error." Ensure each of the above sentences
occupies its own source line in the docs.
🪄 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: 6a9589c5-f0e0-41c4-89b1-244cc0051025
📒 Files selected for processing (6)
Dockerfiledocs/reference/commands.mdxscripts/generate-openclaw-config.mtssrc/lib/onboard/dockerfile-patch.test.tssrc/lib/onboard/dockerfile-patch.tstest/generate-openclaw-config.test.ts
PR Review AdvisorFindings: 0 needs attention, 3 worth checking, 0 nice ideas Review findings🛠️ Needs attention
🔎 Worth checking
🌱 Nice ideas
Since last review detailsCurrent findings:
This is an automated advisory review. A human maintainer must make the final merge decision. |
E2E Advisor RecommendationRequired E2E: Dispatch hint: Auto-dispatched E2E: Full advisor summaryE2E Recommendation AdvisorBase: Required E2E
Optional E2E
New E2E recommendations
Dispatch hint
|
E2E Scenario Advisor RecommendationRequired scenario E2E: Dispatch required scenario E2E:
Full scenario advisor summaryE2E Scenario AdvisorBase: Required scenario E2E
Optional scenario E2E
Relevant changed files
|
Selective E2E Results — ✅ All requested jobs passedRun: 26804857751
|
…uild Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/lib/onboard/dockerfile-patch.ts (1)
263-266: ⚡ Quick winConsider wrapping the base64-encoded value with
sanitizeDockerArgfor consistency and defense-in-depth.All other base64-encoded JSON args in this file use
encodeSanitizedDockerJsonArgor at leastsanitizeDockerArgto strip\r\ncharacters before Docker ARG interpolation (lines 115, 210, 216, 222, 228, 234, 240, 250). While base64 output is inherently safe (alphanumeric +/+=only), applyingsanitizeDockerArgmaintains the established pattern and provides defense-in-depth.🔄 Suggested consistency fix
- const encoded = Buffer.from(extraAgentsRaw, "utf8").toString("base64"); + const encoded = sanitizeDockerArg( + Buffer.from(extraAgentsRaw, "utf8").toString("base64") + ); dockerfile = dockerfile.replace(🤖 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/dockerfile-patch.ts` around lines 263 - 266, Wrap the base64 value assigned to ARG NEMOCLAW_EXTRA_AGENTS_JSON_B64 with the existing sanitization helper instead of inserting `encoded` raw; locate the `encoded` variable and the `dockerfile.replace` call that sets `ARG NEMOCLAW_EXTRA_AGENTS_JSON_B64=...` and pass the value through `sanitizeDockerArg` (or use `encodeSanitizedDockerJsonArg` for parity with other JSON args) before interpolation so it follows the same defense-in-depth pattern as the other ARG insertions.
🤖 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.
Nitpick comments:
In `@src/lib/onboard/dockerfile-patch.ts`:
- Around line 263-266: Wrap the base64 value assigned to ARG
NEMOCLAW_EXTRA_AGENTS_JSON_B64 with the existing sanitization helper instead of
inserting `encoded` raw; locate the `encoded` variable and the
`dockerfile.replace` call that sets `ARG NEMOCLAW_EXTRA_AGENTS_JSON_B64=...` and
pass the value through `sanitizeDockerArg` (or use
`encodeSanitizedDockerJsonArg` for parity with other JSON args) before
interpolation so it follows the same defense-in-depth pattern as the other ARG
insertions.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: edbf2cfd-f377-481b-9653-7baad3b1812d
📒 Files selected for processing (6)
Dockerfiledocs/reference/commands.mdxscripts/generate-openclaw-config.mtssrc/lib/onboard/dockerfile-patch-extra-agents.test.tssrc/lib/onboard/dockerfile-patch.tstest/generate-openclaw-config.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- docs/reference/commands.mdx
- scripts/generate-openclaw-config.mts
- Dockerfile
Selective E2E Results — ✅ All requested jobs passedRun: 26806681061
|
Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
… agents Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
Selective E2E Results — ✅ All requested jobs passedRun: 26809278439
|
prekshivyas
left a comment
There was a problem hiding this comment.
APPROVE. Reviewed the env-driven agent bake end-to-end.
Validation is solid (generate-openclaw-config.mts:224-299): id allowlisted by regex, main reserved and always pinned first with default:true (fixes #4562), paths are resolve()-normalized and matched against the exact canonical /sandbox/.openclaw/workspace-<id> and agents/<id> slots (dot-segment traversal rejected), and a strict per-level key allowlist drops any credential-like field — the baked entry is rebuilt from scratch, never copied from raw input. Confirmed the canonical paths match what provision_agent_workspaces in nemoclaw-start.sh actually parses, and the bare main entry is handled by the if (workspace)/if (agentDir) guards in migration-state.ts (no phantom host root). Using /sandbox/.openclaw/ over the issue's proposed .openclaw-data/ is the correct deviation.
Tests cover the negative space well (malformed, traversal, dup, reserved id, smuggled secrets, default-displacement regression); docs in commands.mdx cover schema, path constraints, and prohibited keys. CI green on 3ee9b5d; both CodeRabbit/CodeQL threads resolved. Resolves #4560, #4562.
Non-blocking nit: a whitespace-only NEMOCLAW_EXTRA_AGENTS_JSON is dropped host-side and never reaches the validator (dockerfile-patch.ts:501) — defensible as "empty == no extras," worth a one-line note if intentional.
Signed-off-by: Prekshi Vyas prekshiv@nvidia.com
Selective E2E Results — ✅ All requested jobs passedRun: 26833892971
|
## Summary - Add the missing `v0.0.57` release-notes section with links to the detailed docs pages for command, inference, onboarding, messaging, status, installer, and policy changes. - Remove public references to docs-skip terms from source docs and regenerate the NemoClaw user skills from the current Fern MDX docs. - Carry forward generated references for the per-agent documentation split, including Hermes-specific reference files. ## Source summary - #4615 and #4653 -> `docs/about/release-notes.mdx`, `docs/reference/commands.mdx`: Release notes now cover host-side `sessions` and `agents` commands plus `NEMOCLAW_EXTRA_AGENTS_JSON` secondary-agent baking. - #4163, #4204, #4611, #4619, and #4676 -> `docs/about/release-notes.mdx`, `docs/inference/use-local-inference.mdx`: Release notes now cover managed vLLM progress/readiness, DGX Spark model default changes, local Ollama streaming usage, and inference route divergence warnings. - #4267, #4601, #4609, #4642, #4645, and #4661 -> `docs/about/release-notes.mdx`, `docs/reference/commands.mdx`: Release notes now cover UFW auto-remediation, local-inference reachability gates, gateway reuse/binding, cancel rollback, and policy selection persistence. - #4577, #4582, #4607, and #4660 -> `docs/about/release-notes.mdx`, `docs/manage-sandboxes/messaging-channels.mdx`: Release notes now cover Slack validation, atomic `channels add`, WhatsApp QR diagnostics, and Slack placeholder normalization. - #4388, #4600, #4646, and #4647 -> `docs/about/release-notes.mdx`, `docs/reference/commands.mdx`: Release notes now cover status failure layers, paused-container hints, Docker-driver doctor behavior, and non-destructive stale-registry recovery. - #4569, #4579, and #4678 -> `docs/about/release-notes.mdx`, `docs/manage-sandboxes/lifecycle.mdx`, `docs/network-policy/integration-policy-examples.mdx`: Release notes now cover installer tag pinning, PyPI `uv` policy access, and observable Jira validation. - #4632 -> `.agents/skills/`: Regenerated user skills from the current per-agent docs source, including newly generated Hermes reference files. ## Verification - `python3 scripts/docs-to-skills.py docs/ .agents/skills/ --prefix nemoclaw-user --doc-platform fern-mdx` - `rg "permissive mode|shields down|shields up|shields status|config rotate-token|rotate-token" docs --glob "*.mdx"` - `rg "permissive mode|shields down|shields up|shields status|config rotate-token|rotate-token" .agents/skills --glob "*.md"` - `npm run docs` - `npm run build:cli` - Commit hooks: markdownlint, docs-to-skills verification, gitleaks, skills YAML, commitlint <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Restructured documentation to clearly distinguish OpenClaw and Hermes agent variants throughout user guides. * Enhanced security, credential storage, and deployment guidance with clearer setup flows. * Added Hermes plugin installation and ecosystem documentation. * Improved workspace, messaging, and policy management references with variant-specific command examples. * Refined troubleshooting and CLI reference sections for clarity. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary Add `nemoclaw onboard --agents <agents.yaml>` to bake a declarative manifest into the sandbox image at build time, and `nemoclaw <name> agents apply -f <agents.yaml>` to reconcile the manifest against an already-running sandbox without a rebuild. The manifest schema mirrors OpenClaw's own [`agents.list[]`](https://docs.openclaw.ai/gateway/config-agents) field names verbatim, and per-agent `model` overrides fan out into `models.providers[<onboard-provider>].models[]` automatically. ## Related Issue Resolves #2853 Complements #2854 (host-side OpenClaw agent lifecycle commands): the manifest is the fixed checked-in path that bakes a layout into the image, while the `nemoclaw <sandbox> agents add|delete|list` lifecycle commands cover ad-hoc edits inside an existing sandbox. The two paths share the same `agents.list[]` semantics and OpenClaw-native field vocabulary, and the new `agents apply` verb sits on top of `agents add|delete` to drive declarative roster reconciliation from the same manifest the build path consumes. This PR extends the `NEMOCLAW_EXTRA_AGENTS_JSON` bake path introduced in #4653. While extending it, the previous validator was found to accept `subagents.maxSpawnDepth` per-agent and to allow value `0`. OpenClaw's per-agent zod schema is `.strict()` and never accepted that field, and the global default schema requires `min(1).max(5)`. Any non-empty `NEMOCLAW_EXTRA_AGENTS_JSON_B64` payload would have tripped OpenClaw's runtime parser. This PR strict-rejects per-agent `maxSpawnDepth` with a migration hint pointing at the top-level `defaults.subagents.maxSpawnDepth`. ## Changes - `scripts/generate-openclaw-config.mts`: accept either the legacy bare-array payload or a new `{agents, defaults?, main?}` object. Extend the per-agent allowlist with `model`; replace `ALLOWED_SUBAGENTS_KEYS` with OpenClaw-native fields `{delegationMode, allowAgents, model, thinking, requireAgentId}`. Strict-reject per-agent `maxSpawnDepth` with a clear migration hint. Add `validateAgentsDefaults` and `validateMainOverrides` so `defaults.subagents.maxSpawnDepth` and `main.{tools, subagents}` bake into the canonical places. Widen `providers[<onboard-provider>].models[]` with one entry per unique referenced `provider/model` ref; reject cross-provider refs. - `src/lib/onboard/agents-manifest.ts` (new): YAML loader that parses the manifest, auto-fills canonical `workspace`/`agentDir` paths from `id`, and sets `NEMOCLAW_EXTRA_AGENTS_JSON` for the downstream Dockerfile patcher. Includes a defence-in-depth credential-name denylist (`apiKey`, `token`, `secret`, etc.) before the payload reaches the staged Dockerfile/build context. - `src/lib/onboard/command-support.ts`, `src/lib/onboard/legacy-command.ts`: thread `--agents <file>` through the oclif flag definition, legacy-args parser, `OnboardCommandOptions.agentsManifest`, and `runOnboardCommand` (invokes `applyAgentsManifestEnv` before dispatching to the underlying `runOnboard`). - `src/lib/actions/sandbox/agents/apply.ts` (new), `src/commands/sandbox/agents/apply.ts` (new): in-sandbox `agents apply` verb. Lists current agents via `openclaw agents list --json`, diffs against the manifest, runs `openclaw agents add|delete` per item. Per-agent `model`, `subagents.*`, top-level `defaults`, and `main` overrides require a rebuild and are reported as warnings before exit. Confirmation gates: `--yes` to apply, `--non-interactive` to fail fast when `--yes` is absent. - `test/generate-openclaw-config.test.ts`, `test/generate-openclaw-config-agents-manifest.test.ts` (new): drop the per-agent `maxSpawnDepth` fixture; add coverage in the dedicated file for the new payload object shape, per-agent `model` accept + provider-prefix reject, `allowAgents` round-trip, `delegationMode` enum rejection, `defaults.subagents.maxSpawnDepth` range, `main.{subagents, tools}` merge, multi-model provider expansion, and the per-agent `maxSpawnDepth` strict-reject with migration hint. Split keeps the original file under the legacy size budget. - `test/agents-manifest-policy-conformance.test.ts` (new): policy mirror of OpenClaw's `resolveSubagentTargetPolicy` against the baked `agents.list[].subagents.allowAgents` shape — covers configured target allow, unlisted target deny, wildcard `"*"` + unknown target deny, and self-only default. - `src/lib/onboard/agents-manifest.test.ts` (new), `src/lib/onboard/agents-manifest-credential-denylist`: 12 cases for the YAML loader plus the credential denylist matrix. - `src/lib/onboard/legacy-command.test.ts`, `src/lib/onboard/legacy-command-agents.test.ts` (new): `--agents` lifecycle moved into the focused file (parse, missing-path / missing-value rejection, env-var application before `runOnboard`). - `src/lib/actions/sandbox/agents/apply.test.ts` (new): diff computation + rebuild-only warning + `--yes` / `--non-interactive` gating + no-op when roster matches. - `src/lib/onboard/dockerfile-patch-extra-agents.test.ts`: rotate the fixture from `subagents: { maxSpawnDepth: 0 }` to `subagents: { allowAgents: ["analyst"] }`. - `docs/inference/declarative-agents-manifest.mdx` (new), `docs/reference/commands.mdx`, `docs/reference/commands-nemohermes.mdx`, `docs/index.yml`: user-facing reference for the manifest schema, the `main` / `defaults` / `agents` blocks, the OpenClaw-native sub-agent fields, the multi-model bake semantics, a manager-worker walkthrough, the new `agents apply` verb, and the `--agents` onboard flag listing. ## Type of Change - [ ] Code change (feature, bug fix, or refactor) - [x] Code change with doc updates - [ ] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Verification - [x] Git hooks passed during commit and push, or `npx prek run --from-ref main --to-ref HEAD` passes - [x] Targeted tests pass for changed behavior - [x] Full `npm test` passes (broad runtime changes only) - [x] Tests added or updated for new or changed behavior - [x] No secrets, API keys, or credentials committed - [x] Docs updated for user-facing behavior changes - [ ] `npm run docs` builds without warnings (doc changes only) - [ ] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) --- Signed-off-by: Tinson Lai <tinsonl@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Added `--agents <agents.yaml>` to onboarding (including compatibility aliases). * Added `sandbox <name> agents apply` to reconcile a sandbox’s agents from a manifest, with rebuild-required warnings. * **Documentation** * Added an Inference navigation entry and a task-specific manifest setup guide. * Updated command reference pages for onboarding and apply (including nemohermes). * **Tests** * Expanded coverage for agents manifest parsing/validation, config generation, and sandbox apply diff/rebuild-only behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Tinson Lai <tinsonl@nvidia.com> Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Summary
Adds
NEMOCLAW_EXTRA_AGENTS_JSONso operators can bake one or more OpenClaw secondary agents into the sandboxopenclaw.jsonat onboard time, alongside the canonical primarymainagent. The bake always emitsagents.listwith{ id: "main", default: true }first and operator-supplied entries appended after it, so secondary agents cannot displace the primary agent as the default.Related Issue
Closes #4560.
Closes #4562 — complementary to #4560.
Changes
scripts/generate-openclaw-config.mts: validateNEMOCLAW_EXTRA_AGENTS_JSON_B64— id regex, reserved-idmainrejection, duplicate-id rejection,resolve()-based containment under/sandbox/.openclaw/(rejects..traversal), required per-agenttoolsallow/deny policy, requiredsubagents.maxSpawnDepth, nodefault: trueoverrides. Always emitagents.listwith the canonicalmainentry first.src/lib/onboard/dockerfile-patch.ts: readprocess.env.NEMOCLAW_EXTRA_AGENTS_JSONand base64-encode the raw payload intoARG NEMOCLAW_EXTRA_AGENTS_JSON_B64. The host-side stage does not parse or shape-check the JSON, so the build-time validator inscripts/generate-openclaw-config.mtsremains the single source of truth for structured validation errors.Dockerfile: declareARG NEMOCLAW_EXTRA_AGENTS_JSON_B64=W10=and promote it toENV, matching theNEMOCLAW_*_B64convention.test/generate-openclaw-config.test.ts: cases for default-only emission, append-after-main,mainreservation,default: truerejection, duplicate-id rejection, id-regex enforcement, absolute-path and dot-segment traversal rejection, required-tools enforcement, required-subagents enforcement, and primary-agent-default regression with extras present.src/lib/onboard/dockerfile-patch-extra-agents.test.ts: focused test file covering valid-payload encoding, empty-default preservation, and raw-payload passthrough so malformed input reaches the build-time validator instead of being silently dropped on the host.docs/reference/commands.mdx: row pointer plus a new "Extra OpenClaw agents" subsection documenting the field schema, path constraints, validation rules, and an example payload.Type of Change
Verification
npx prek run --all-filespassesnpm testpassesnpm run docsbuilds without warnings (doc changes only)Signed-off-by: Tinson Lai tinsonl@nvidia.com
Summary by CodeRabbit
New Features
Documentation
Tests