Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions agents/hermes/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ RUN chmod 755 /usr/local/bin/nemoclaw-start /usr/local/lib/nemoclaw/sandbox-init
RUN test -x /usr/local/lib/nemoclaw/validate-hermes-env-secret-boundary.py \
|| { echo "ERROR: validate-hermes-env-secret-boundary.py missing or not executable" >&2; exit 1; }

# Hermes v0.18.0 computes `sessions list` preview from the first user message,
# Hermes v0.18.2 computes `sessions list` preview from the first user message,
# while #5254's user-facing expectation is that the existing row reflects the
# latest resumed/continued one-shot turn. Patch only the pinned query shape and
# prove the SessionDB list contract at build time so a Hermes update cannot
Expand All @@ -266,7 +266,7 @@ assert rows and rows[0]["id"] == session_id, rows
assert rows[0]["preview"] == "NEMOCLAW_PREVIEW_LATEST", rows
PY

# Hermes v0.18.0's bundled Langfuse plugin rejects OpenShell resolver
# Hermes v0.18.2's bundled Langfuse plugin rejects OpenShell resolver
# placeholders before the SDK can construct its Basic-auth headers. Accept only
# the exact public/secret placeholder bound to the matching standard Langfuse
# key; raw keys keep Hermes' upstream pk-lf-/sk-lf- validation. The patcher
Expand Down Expand Up @@ -354,10 +354,10 @@ RUN hermes_version_output="$(/usr/local/bin/hermes --version)" \
echo "ERROR: could not parse Hermes semver from: $hermes_version_output" >&2; \
exit 1; \
fi \
&& if [ "$hermes_semver" != "0.18.0" ] \
&& if [ "$hermes_semver" != "0.18.2" ] \
&& { grep -q '_translate_resumed_oneshot' /usr/local/lib/nemoclaw/hermes-wrapper.py \
|| grep -q 'EXPECTED_OCCURRENCES' /usr/local/lib/nemoclaw/patch-hermes-session-list-preview.py; }; then \
echo "ERROR: installed Hermes ${hermes_semver} but Hermes v0.18.0 compatibility workarounds are still installed; re-review #5254 workaround removal before upgrading Hermes" >&2; \
echo "ERROR: installed Hermes ${hermes_semver} but Hermes v0.18.2 compatibility workarounds are still installed; re-review #5254 workaround removal before upgrading Hermes" >&2; \
exit 1; \
fi
# This runs before `/usr/local/bin/hermes` is moved to `hermes.real`, so the
Expand Down
10 changes: 5 additions & 5 deletions agents/hermes/Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ ENV DEBIAN_FRONTEND=noninteractive
# pyproject.toml, HERMES_TARBALL_SHA256 the GitHub tarball checksum, and
# HERMES_NPM_INTEGRITY the `npm view hermes-agent@<semver> dist.integrity`
# sha512 used as a registry cross-check at build time.
# Calver tag v2026.7.1 = Hermes Agent v0.18.0.
ARG HERMES_VERSION=v2026.7.1
ARG HERMES_SEMVER=0.18.0
ARG HERMES_TARBALL_SHA256=ec8a380629cc2f3f2102dd92cad50c4ded706fe59c0e359a05681166a0ae2991
ARG HERMES_NPM_INTEGRITY=sha512-fS+8MsE69shNPmFnKDrE1a+KUDEEXgbyN/QM7J+2nh294gTjh2wCknk2tfMsD03GhSxl7YwyDmp6e5HLYzw8yA==
# Calver tag v2026.7.7.2 = Hermes Agent v0.18.2.
ARG HERMES_VERSION=v2026.7.7.2
ARG HERMES_SEMVER=0.18.2
ARG HERMES_TARBALL_SHA256=f5d1022eed3763a768cf7b0f0844831f0170a35f54eb8d18223f2e93f503025e
ARG HERMES_NPM_INTEGRITY=sha512-Zhkhl209frEHGh65S3+eGZu/M+mFPFQwQeBOnG/MtRfAFEfRMkji+deyjH7TFrQNcp+RHioiBamCFYDNR6dPlw==
ARG HERMES_UV_EXTRAS="anthropic messaging web pty mcp"
ARG UV_VERSION=0.11.8

Expand Down
6 changes: 3 additions & 3 deletions agents/hermes/hermes-wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,9 @@ def _run_gateway_guard(guard_path: str) -> int:
"--resume": "--resume",
}
# Keep this allowlist aligned with the top-level flags accepted by the pinned
# Hermes Agent CLI in agents/hermes/Dockerfile.base (HERMES_VERSION=v2026.7.1,
# HERMES_SEMVER=0.18.0) and agents/hermes/manifest.yaml (expected_version
# "0.18.0"). Unknown flags deliberately fail closed by passing the original argv
# Hermes Agent CLI in agents/hermes/Dockerfile.base (HERMES_VERSION=v2026.7.7.2,
# HERMES_SEMVER=0.18.2) and agents/hermes/manifest.yaml (expected_version
# "0.18.2"). Unknown flags deliberately fail closed by passing the original argv
Comment on lines +292 to +294

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Update all Dockerfile integrity hashes for changed protected sources.

Both edited files are covered by fail-closed SHA-256 gates, so their unchanged hashes will make the image build fail.

  • agents/hermes/hermes-wrapper.py#L292-L294: regenerate and update NEMOCLAW_HERMES_WRAPPER_SHA256 in agents/hermes/Dockerfile.
  • agents/hermes/patch-langfuse-credentials.mts#L10-L10: regenerate and update NEMOCLAW_HERMES_LANGFUSE_PATCHER_SHA256 in agents/hermes/Dockerfile.
📍 Affects 2 files
  • agents/hermes/hermes-wrapper.py#L292-L294 (this comment)
  • agents/hermes/patch-langfuse-credentials.mts#L10-L10
🤖 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/hermes-wrapper.py` around lines 292 - 294, The protected
sources changed, but their Dockerfile integrity hashes are stale. Regenerate the
SHA-256 values and update NEMOCLAW_HERMES_WRAPPER_SHA256 in
agents/hermes/Dockerfile for agents/hermes/hermes-wrapper.py lines 292-294, and
NEMOCLAW_HERMES_LANGFUSE_PATCHER_SHA256 there for
agents/hermes/patch-langfuse-credentials.mts line 10; leave the source files
otherwise unchanged.

# through to upstream Hermes.
_BOOLEAN_FLAGS = {
"--worktree",
Expand Down
6 changes: 3 additions & 3 deletions agents/hermes/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ homepage: "https://github.com/NousResearch/hermes-agent"
install_method: curl # curl install.sh | bash
binary_path: /usr/local/bin/hermes
version_command: "hermes --version"
expected_version: "0.18.0"
# Hermes reports semver from `hermes --version` (e.g. `0.18.0`) even though its
# GitHub release cadence is calendar-based (`v2026.7.1`). Declaring the
expected_version: "0.18.2"
# Hermes reports semver from `hermes --version` (e.g. `0.18.2`) even though its
# GitHub release cadence is calendar-based (`v2026.7.7.2`). Declaring the
# scheme keeps the staleness check off the shape heuristic when a manifest
# could otherwise straddle both schemes (#6049).
version_scheme: semver
Expand Down
2 changes: 1 addition & 1 deletion agents/hermes/patch-langfuse-credentials.mts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import process from "node:process";
import { pathToFileURL } from "node:url";

/**
* Patch the Langfuse validator bundled with pinned Hermes v2026.7.1 / 0.18.0.
* Patch the Langfuse validator bundled with pinned Hermes v2026.7.7.2 / 0.18.2.
*
* Hermes rejects OpenShell resolver placeholders before the Langfuse SDK can
* turn them into outbound authentication headers. NemoClaw keeps the real
Expand Down
4 changes: 2 additions & 2 deletions agents/hermes/patch-session-list-preview.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Patch pinned Hermes v0.18.0 session-list previews to show the latest user turn.
"""Patch pinned Hermes v0.18.2 session-list previews to show the latest user turn.

Source-of-truth note for this localized Hermes runtime patch:
- Invalid state: Hermes v0.18.0 computes `sessions list` preview text from
- Invalid state: Hermes v0.18.2 computes `sessions list` preview text from
the first user message, but #5254's resumed/continued one-shot UX expects
the original row to reflect the latest appended turn.
- Value being patched: pinned/prebuilt `/opt/hermes/hermes_state.py`
Expand Down
1 change: 1 addition & 0 deletions src/lib/domain/sandbox/connect-env.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe("sandbox connect environment helpers", () => {
expect(NEMOCLAW_HERMES_LIGHT_SKIN_REVIEWED_HERMES_VERSIONS).toEqual([
"v2026.6.19",
"v2026.7.1",
"v2026.7.7.2",
]);
});

Expand Down
1 change: 1 addition & 0 deletions src/lib/domain/sandbox/connect-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const NEMOCLAW_HERMES_LIGHT_SKIN_NAME = "nemoclaw-light";
export const NEMOCLAW_HERMES_LIGHT_SKIN_REVIEWED_HERMES_VERSIONS = [
"v2026.6.19",
"v2026.7.1",
"v2026.7.7.2",
] as const;

// Compatibility boundary: remove this NemoClaw-managed light skin once the
Expand Down
2 changes: 1 addition & 1 deletion test/hermes-doctor-config-hash.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe("Hermes doctor and config hash boundary", () => {

expect(result.status).toBe(1);
expect(result.stderr).toContain(
"Hermes v0.18.0 compatibility workarounds are still installed",
"Hermes v0.18.2 compatibility workarounds are still installed",
);
} finally {
fs.rmSync(tmp, { recursive: true, force: true });
Expand Down
8 changes: 4 additions & 4 deletions test/update-hermes-agent-script.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const HERMES_BASE_DOCKERFILE = path.join(
"Dockerfile.base",
);
const HERMES_MANIFEST = path.join(import.meta.dirname, "..", "agents", "hermes", "manifest.yaml");
const TARGET_TAG = "v2026.7.1";
const TARGET_TAG = "v2026.7.7.2";

const CURRENT_INSTALLED_BASE = [
"# Calver tag v2026.6.5 = Hermes Agent v0.16.0.",
Expand Down Expand Up @@ -88,7 +88,7 @@ printf 'fake archive' > "$output"
);
writeExecutable(
path.join(fakeBin, "tar"),
"#!/usr/bin/env bash\nprintf 'version = \"0.18.0\"\\n'\n",
"#!/usr/bin/env bash\nprintf 'version = \"0.18.2\"\\n'\n",
);
writeExecutable(path.join(fakeBin, "npm"), "#!/usr/bin/env bash\nprintf 'sha512-test\\n'\n");
writeExecutable(
Expand All @@ -107,7 +107,7 @@ esac
set -euo pipefail
printf '%s|%s\\n' "\${NEMOCLAW_HERMES_SANDBOX_BASE_IMAGE_REF:-}" "$*" >> "$FAKE_NEMOHERMES_LOG"
if [[ "$*" == "hermes exec -- hermes --version" ]]; then
printf '0.18.0\\n'
printf '0.18.2\\n'
fi
`,
);
Expand All @@ -130,7 +130,7 @@ fi
expect(run.status, `${run.stdout}\n${run.stderr}`).toBe(0);
expect(fs.readFileSync(dockerLog, "utf8")).toContain(`tag ${baseRef} ${pinnedRef}`);
expect(fs.readFileSync(nemohermesLog, "utf8")).toContain(`${pinnedRef}|hermes rebuild`);
expect(run.stdout).toContain("OK: sandbox reports Hermes Agent v0.18.0");
expect(run.stdout).toContain("OK: sandbox reports Hermes Agent v0.18.2");
} finally {
fs.rmSync(tmp, { recursive: true, force: true });
}
Expand Down
Loading