fix(inference): re-seed Hermes dashboard config on in-place inference set - #6898
Conversation
… set Switching a running Hermes sandbox with `nemoclaw inference set` updated the OpenShell route, the NemoClaw registry, and the main Hermes gateway config, then reported "Inference route synced" — but the separately isolated Hermes Dashboard profile under `/sandbox/.hermes/dashboard-home/config.yaml` stayed on the previous model. Dashboard Chat and its `/api/model/info` endpoint therefore kept using the old model even though every other status surface reported the new one. The dashboard config only re-mirrors the gateway's model routing at sandbox startup (via the seeder in agents/hermes/start.sh); the in-place switch never re-ran it, and a gateway restart did not restart the dashboard either. Re-run the same dashboard config seeder inside the sandbox after the Hermes gateway config is written, mirroring `model` / `custom_providers` / `_nemoclaw_upstream` into the dashboard-home config so Dashboard Chat converges on the selected model. The seeder runs as the sandbox user (non-privileged `sandbox exec`, matching start.sh's step-down before touching sandbox-owned dashboard state) and does its own no-follow atomic writes. It is best-effort: on failure the switch still succeeds and the user is told to restart the sandbox. OpenClaw has no isolated dashboard config, so it is unaffected. Fixes #6893 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Yanyun Liao <yanyunl@nvidia.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughHermes inference-route updates now reseed isolated dashboard configuration after successful in-place sandbox writes. Dashboard convergence is propagated to final status handling, with coverage for failure and absent-dashboard cases, argv-safe seeding, OpenClaw exclusion, and live dashboard state. ChangesHermes dashboard routing
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant InferenceSet
participant SandboxConfig
participant OpenShell
participant GatewayRestart
InferenceSet->>SandboxConfig: write in-sandbox gateway configuration
SandboxConfig-->>InferenceSet: configuration and hash update succeeds
InferenceSet->>SandboxConfig: seed Hermes dashboard configuration
SandboxConfig->>OpenShell: inspect dashboard-home and run seeder
OpenShell-->>SandboxConfig: return convergence status
SandboxConfig-->>InferenceSet: return converged, absent, or failed
InferenceSet->>GatewayRestart: provide dashboardConverged
GatewayRestart-->>InferenceSet: emit or suppress sync message
Possibly related issues
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Code Coverage OverviewLanguages: TypeScript TypeScript / code-coverage/pluginThe overall coverage remains at 96%, unchanged from the TypeScript / code-coverage/cliThe overall coverage in the Show a code coverage summary of the most impacted files.
Updated |
PR Review Advisor — InformationalAdvisor assessment: Informational / high confidence Model lanes
Nemotron output stays in workflow artifacts and does not change the assessment above. E2E guidanceAdvisory only. E2E / PR Gate selects and runs jobs independently. Recommended E2E: 1 optional E2E recommendation
This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/lib/sandbox/config.ts (1)
615-632: 🔒 Security & Privacy | 🔵 Trivial | ⚖️ Poor tradeoffShell-script construction instead of structured argv for a security-boundary file.
seedHermesDashboardConfigbuilds afor/&&/execcontrol-flow string and runs it viash -crather than invoking the seeder through argv arrays. All interpolated values areshellQuote-escaped and derived from trusted internal state (constants + resolvedAgentConfigTarget), so exploitability here is low, but this still departs from the stated preference for this file.Since the goal is only "pick the first executable python3 candidate," this could alternatively be resolved without a shell wrapper (e.g., have the caller/adapter probe candidates or pass the candidate list as discrete argv items to a small trusted resolver), keeping
sh -cstring construction out of this security boundary.As per path instructions, "
src/lib/sandbox/{config,privileged-exec}.ts: Treat this as a security boundary. ... Prefer argv arrays and structured APIs over shell command construction."🤖 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/sandbox/config.ts` around lines 615 - 632, Update seedHermesDashboardConfig to avoid constructing a shell control-flow string and invoking sh -c. Resolve the first executable Python candidate through structured argv/API handling, then invoke HERMES_DASHBOARD_SEEDER_PATH with discrete arguments via captureOpenshellCommand, preserving the existing failure result when no candidate is available.Source: Path instructions
🤖 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 `@src/lib/sandbox/config.ts`:
- Around line 615-632: Add direct unit tests for seedHermesDashboardConfig using
adversarial configPath and configDir values, including spaces and shell
metacharacters, and mock captureOpenshellCommand/getOpenshellBinary to inspect
the generated command safely. Cover a failing seed execution with stderr/stdout
handling and assert the helper returns false while preserving safe shell
quoting.
---
Nitpick comments:
In `@src/lib/sandbox/config.ts`:
- Around line 615-632: Update seedHermesDashboardConfig to avoid constructing a
shell control-flow string and invoking sh -c. Resolve the first executable
Python candidate through structured argv/API handling, then invoke
HERMES_DASHBOARD_SEEDER_PATH with discrete arguments via
captureOpenshellCommand, preserving the existing failure result when no
candidate is available.
🪄 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: ace5f16d-1a1b-4abc-9963-54b85538ac3d
📒 Files selected for processing (5)
src/lib/actions/inference-set-hermes-run.test.tssrc/lib/actions/inference-set-openclaw-run.test.tssrc/lib/actions/inference-set.test-support.tssrc/lib/actions/inference-set.tssrc/lib/sandbox/config.ts
…6893) Follow-up on the Hermes dashboard re-seed: make the re-seed report a tri-state (converged / absent / failed) and withhold the "Inference route synced" line when the dashboard did not converge, matching the reporter's "converge, or fail without printing Inference route synced". - config.ts: `seedHermesDashboardConfig` now returns `HermesDashboardReseedResult`. It probes for the Dashboard profile first; an absent profile (Dashboard disabled) is a clean no-op ("absent"), a seeder error is "failed". - inference-set.ts: thread `dashboardConverged` into the gateway-restart result (true on converged/absent, false on failed; still best-effort — the switch succeeds and warns on failure). - inference-set-gateway-restart.ts: withhold the success line for a Hermes switch whose dashboard did not converge. - Tests: withhold synced on failure; still report synced when absent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Yanyun Liao <yanyunl@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@src/lib/sandbox/config.ts`:
- Line 632: Update the dashboard path validation command near the dashboard-home
check so only a genuinely nonexistent path emits HERMES_DASHBOARD_ABSENT_MARKER
and returns "absent"; treat files, broken symlinks, and inspection errors as
failures instead. Ensure the caller cannot report a synced route for
present-but-invalid or uninspectable dashboard paths.
🪄 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: 82724a68-fbb1-4edc-9733-80f6f4736b9f
📒 Files selected for processing (5)
src/lib/actions/inference-set-gateway-restart.tssrc/lib/actions/inference-set-hermes-run.test.tssrc/lib/actions/inference-set.test-support.tssrc/lib/actions/inference-set.tssrc/lib/sandbox/config.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/lib/actions/inference-set.test-support.ts
E2E Target Results — ✅ All requested tests passedRun: 29389872917
|
E2E Target Results — ✅ All requested tests passedRun: 29390643378
|
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@src/lib/sandbox/config.ts`:
- Around line 645-696: Add a finite timeout option to the shared capture wrapper
inside seedHermesDashboardConfig, passing it to every captureOpenshellCommand
invocation used for Python detection, dashboard inspection, and seeding.
Preserve the existing failed-result handling so timed-out calls return the
best-effort "failed" result without blocking indefinitely.
🪄 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: c36de39d-ea23-467e-b55b-41a3b6ed4f0f
📒 Files selected for processing (3)
src/lib/actions/inference-set-hermes-run.test.tssrc/lib/sandbox/config.tssrc/lib/sandbox/hermes-dashboard-reseed.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/lib/actions/inference-set-hermes-run.test.ts
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
|
PRA-1 addressed in |
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
|
🌿 Preview your docs: https://nvidia-preview-pr-6898.docs.buildwithfern.com/nemoclaw |
cv
left a comment
There was a problem hiding this comment.
Maintainer review complete for commit e1e00a9. The seeder now requires write evidence before declaring dashboard convergence, zero-exit no-ops fail the post-commit command path, argv handling remains injection-safe, and user recovery behavior is documented. Focused tests, typecheck, hooks, and Fern docs validation pass.
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
<!-- markdownlint-disable MD041 --> ## Summary Adds the canonical `docs/changelog/2026-07-15.mdx` entry with the exact `## v0.0.84` heading for the release candidate range from `v0.0.83` through `710d2b36b9eebcb6bca3c2b2f796a1bdb69c3a31`. Fills two owner-page gaps for model-aware local inference health and pre-write OpenClaw candidate validation. ## Changes - Add the complete shared Fern changelog entry for `v0.0.84`, with literal CLI names and root-absolute OpenClaw and Hermes routes. - Document that sandbox status and doctor compare the configured Ollama or vLLM model with provider inventory without issuing a completion. - Document that host-side OpenClaw `config set` validates the complete candidate before replacing live config or reaching gateway restart. - Reconcile the `v0.0.84` release label with the commit range. PR #6773 is already contained in `v0.0.83` and remains documented there; CI, test-harness, docs-infrastructure, and `.js` to `.mts` migration-only changes require no additional user guidance. ### Source summary - [#6882](#6882) -> `docs/manage-sandboxes/backup-restore.mdx`, `docs/changelog/2026-07-15.mdx`: Explain that OpenClaw runtime identity and pairing state are excluded from snapshots and ignored during restore. - [#6873](#6873) -> `docs/inference/set-up-ollama.mdx`, `docs/changelog/2026-07-15.mdx`: Record the Ollama requested-model environment fallback and interactive default. - [#6835](#6835) -> `docs/changelog/2026-07-15.mdx`: Include the sandbox name in the documented rebuild resume-recovery behavior. - [#6886](#6886) -> `docs/inference/custom-endpoint-security.mdx`, `docs/inference/set-up-openai-compatible-endpoint.mdx`, `docs/changelog/2026-07-15.mdx`: Explain the exact-host trusted-private endpoint opt-in and retained SSRF boundaries. - [#6887](#6887) -> `docs/reference/commands.mdx`, `docs/changelog/2026-07-15.mdx`: Document Telegram channel health verdicts, summary behavior, and exit status. - [#6863](#6863) -> `docs/manage-sandboxes/lifecycle.mdx`, `docs/changelog/2026-07-15.mdx`: Add the missing model-inventory behavior for local status and doctor checks. - [#6902](#6902) -> `docs/manage-sandboxes/runtime-controls.mdx`, `docs/changelog/2026-07-15.mdx`: Add the missing pre-write OpenClaw candidate-validation contract. - [#6916](#6916) -> `docs/changelog/2026-07-15.mdx`: Preserve the failed-session fresh-install recovery correction in the release entry. - [#6934](#6934) -> `docs/reference/commands.mdx`, `docs/reference/troubleshooting.mdx`, `docs/security/credential-storage.mdx`, `docs/changelog/2026-07-15.mdx`: Summarize completed-prompt checkpointing and validated credential reuse during OpenClaw resume. - [#6898](#6898) -> `docs/inference/switch-models.mdx`, `docs/inference/switch-providers.mdx`, `docs/reference/troubleshooting.mdx`, `docs/changelog/2026-07-15.mdx`: Explain Hermes dashboard convergence after in-place inference changes. - [#6711](#6711) -> `docs/manage-sandboxes/run-sandboxes.mdx`, `docs/manage-sandboxes/uninstall-nemoclaw.mdx`, `docs/reference/architecture.mdx`, `docs/reference/commands.mdx`, `docs/changelog/2026-07-15.mdx`: Summarize port-scoped host state and uninstall preservation. - [#6767](#6767) -> `docs/inference/configure-model-limits.mdx`, `docs/inference/set-up-ollama.mdx`, `docs/reference/troubleshooting.mdx`, `docs/changelog/2026-07-15.mdx`: Record the Hermes `64000`-token Ollama floor and unchanged OpenClaw floor. - [#6862](#6862) -> `docs/get-started/quickstart.mdx`, `docs/inference/verify-inference-route.mdx`, `docs/changelog/2026-07-15.mdx`: Explain retryable not-ready finalization for unhealthy inference routes. - [#6766](#6766) -> `docs/security/tcb-boundary.mdx`, `docs/changelog/2026-07-15.mdx`: Document definitive stale transition-lock recovery and fail-closed ambiguous cases. - [#6948](#6948) -> `docs/manage-sandboxes/manage-mcp-servers.mdx`, `docs/changelog/2026-07-15.mdx`: Include Hermes MCP apply-state race recovery in the release entry without changing the established user workflow. - [#6964](#6964) -> `docs/reference/troubleshooting.mdx`, `docs/changelog/2026-07-15.mdx`: Record complete agent-specific fresh-install and resume recovery commands. - [#6883](#6883) -> `docs/get-started/quickstart.mdx`, `docs/inference/set-up-vllm.mdx`, `docs/reference/platform-support.mdx`, `docs/changelog/2026-07-15.mdx`: Summarize the DGX Station Nemotron Ultra express path and pinned managed-vLLM recipe. - [#6985](#6985) -> `docs/inference/set-up-vllm.mdx`, `docs/reference/commands.mdx`, `docs/changelog/2026-07-15.mdx`: Capture the final automated and interactive storage-warning behavior. ## Type of Change - [ ] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [x] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Quality Gates - [ ] Tests added or updated for changed behavior - [x] Existing tests cover changed behavior — `test/changelog-docs.test.ts` validates the dated-entry structure, exact version heading, and preserved history. - [ ] Tests not applicable — justification: - [x] Docs updated for user-facing behavior changes - [ ] Docs not applicable — justification: - [ ] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging) - [ ] Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: ## Verification - [x] PR description includes a `Signed-off-by:` line and every commit appears as `Verified` in GitHub - [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or `npm run check:diff` passed when hooks were skipped or unavailable - [x] Targeted behavior tests pass for the current change set, or tests are marked not applicable above — `npx vitest run test/changelog-docs.test.ts` (6 passed) - [ ] Applicable broad gate passed — `npm test` for broad runtime/test-harness changes; `npm run check` for repo-wide validation/coverage changes — not run for this doc-only change. - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, or credentials committed - [ ] `npm run docs` builds without warnings (doc changes only) — completed with 0 errors; Fern reported the unchanged unauthenticated redirect-check and light-theme contrast warnings. - [x] 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) — the native changelog entry uses the required parser-safe MDX SPDX comment and intentionally has no frontmatter. --- Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added the v0.0.84 changelog entry covering setup, endpoint onboarding, model handling, sandbox readiness, recovery, channel status, and configuration safeguards. * Clarified that sandbox health checks validate configured models against local Ollama and vLLM provider inventories without generating completions or consuming tokens. * Documented that invalid runtime configuration changes are rejected while preserving the existing working configuration. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Summary
nemoclaw inference seton a running Hermes sandbox updated the OpenShell route, the registry, and the main Hermes gateway config, then reportedInference route synced— but the separately isolated Hermes Dashboard profile (/sandbox/.hermes/dashboard-home/config.yaml) stayed on the previous model, so Dashboard Chat and/api/model/infokept using the old one. This PR re-runs the dashboard config seeder after the switch so the dashboard converges.Closes #6893.
Reproduction
On our DGX Spark aarch64 test host (GB10 GPU) — matching the reporter's DGX Station aarch64 — a real Hermes +
ollama-localsandbox with two models (llama3.1:8b,qwen2.5:3b).Environment
main(v0.0.82); agent Hermes; providerollama-localObserved on
main(before fix) —inference set --provider ollama-local --model qwen2.5:3b --sandbox hermesprintsInference route synced, then:Observed on
fix/...(after fix) — the same switch (and a switch back) converge every surface:Analysis
Hermes keeps an isolated dashboard profile under
HERMES_DASHBOARD_HOME(/sandbox/.hermes/dashboard-home) for privilege separation. Its config only receives the gateway's model routing (model/custom_providers/_nemoclaw_upstream) viaseed-hermes-dashboard-config.py, whichagents/hermes/start.shruns at sandbox startup.runInferenceSet(src/lib/actions/inference-set.ts) patches the gateway config (patchHermesInferenceConfig) and writes it withwriteSandboxConfig, but never re-runs the seeder, so the dashboard config is left on the prior model. Ahermes gateway restartdoes not converge it either, because it restarts the gateway, not the long-running dashboard, so the startup seeder never re-runs. Related to #4765 (which added the dashboard-home mirror for cold startup) but a distinct in-place-switch gap. The dashboard reads its config live (/api/model/inforeflects the file immediately after a re-seed), so re-seeding the file is sufficient — no dashboard restart is needed.Fix
seedHermesDashboardConfig(sandboxName, target)insrc/lib/sandbox/config.tsthat runs the same seeder inside the sandbox via a non-privilegedsandbox exec(matching start.sh's step-down before touching sandbox-owned dashboard state; the seeder itself does no-follow atomic writes and refuses symlinks), mirroring the gateway config into the dashboard-home config.runInferenceSet's Hermes branch after the gateway config write succeeds. On failure, the route and main config remain committed, but the command exits nonzero and tells the user to restart the sandbox. OpenClaw has no isolated dashboard config and is unaffected.Changes
src/lib/sandbox/config.ts: add + exportseedHermesDashboardConfig.src/lib/actions/inference-set.ts: dep + call in the Hermes branch after the config write.src/lib/actions/inference-set.test-support.ts: mock the new dep.src/lib/actions/inference-set-hermes-run.test.ts: re-seed happens after the write; skipped when the write fails.src/lib/actions/inference-set-openclaw-run.test.ts: boundary — OpenClaw never re-seeds.Type of Change
Verification
npm testpasses (touched files); broader inference-set + sandbox/config suites pass (147 tests)AI Disclosure
Update — converge-or-don't-claim-synced + tri-state
The re-seed now reports a tri-state (
converged/absent/failed) and, when the dashboard did not converge,inference setwithholds theInference route syncedline (the route and main config remain committed, but the command exits nonzero and warns), matching the reporter's "converge on the selected model, or fail without printing Inference route synced". An absent Dashboard profile (Dashboard disabled) is a clean no-op that still reports synced.src/lib/actions/inference-set-gateway-restart.ts: withhold the success line for a Hermes switch whose dashboard did not converge (dashboardConverged === false).Note: this fully re-mirrors the dashboard routing (verified:
model.default,custom_providers,_nemoclaw_upstream, andproviders.<provider>.default_modelall converge — matching cold startup), rather than setting a subset of keys.Signed-off-by: Yanyun Liao yanyunl@nvidia.com
Summary by CodeRabbit
--providerinputs to match supported provider identifiers.