Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
69f133c
feat(hermes): update runtime to 0.20.6
prekshivyas Aug 29, 2026
1c184c0
fix(hermes): repair 0.20.6 base build
prekshivyas Aug 29, 2026
b641064
fix(hermes): expose venv to sandbox probe
prekshivyas Aug 29, 2026
fda1835
fix(hermes): pin base venv interpreter
prekshivyas Aug 29, 2026
3608065
fix(hermes): pin lazy browser runtime
prekshivyas Aug 29, 2026
2bbd3d3
fix(hermes): initialize MCP image probe
prekshivyas Aug 29, 2026
37c47df
fix(hermes): pin 0.20.6 base image
prekshivyas Aug 29, 2026
25f6077
fix(hermes): verify resolved cron ledger path
prekshivyas Aug 29, 2026
8b89546
fix(hermes): bind layered browser policy source
prekshivyas Aug 29, 2026
92f49da
fix(hermes): bind venv interpreter link
prekshivyas Aug 29, 2026
97bdee4
fix(hermes): close migration review findings
prekshivyas Aug 29, 2026
6a7aafa
fix(hermes): remove duplicate final image setup
prekshivyas Aug 29, 2026
732dba8
fix(hermes): align portable image context
prekshivyas Aug 29, 2026
7ebc0bf
fix(hermes): complete managed image validation
prekshivyas Aug 29, 2026
7d540d4
fix(hermes): close review evidence gaps
prekshivyas Aug 29, 2026
997666e
fix(hermes): simplify browser cache validation
prekshivyas Aug 29, 2026
619d344
refactor(hermes): remove redundant browser manifest
prekshivyas Aug 29, 2026
a8a5fba
fix(hermes): close runtime fallback gaps
prekshivyas Aug 29, 2026
4f24bf1
fix(hermes): normalize final image repairs
prekshivyas Aug 30, 2026
2095221
fix(hermes): remove build-only migration patchers
prekshivyas Aug 30, 2026
28ca8c8
fix(hermes): stay within managed image layer budget
prekshivyas Aug 30, 2026
13574de
fix(hermes): scope one-shot completion wait
prekshivyas Aug 30, 2026
7d16555
fix(hermes): pin repaired base image
prekshivyas Aug 30, 2026
c52e3da
merge: sync Hermes upgrade with main
prekshivyas Aug 30, 2026
14cdb62
fix(rebuild): verify replacement agent version
prekshivyas Aug 30, 2026
65d02b7
fix(hermes): trim portable build context
prekshivyas Aug 30, 2026
ec0f8ca
fix(hermes): avoid plugin discovery deadlock
prekshivyas Aug 30, 2026
59b3936
Merge branch 'main' into codex/hermes-0206-upgrade
prekshivyas Aug 30, 2026
4f4bae5
Merge branch 'main' into codex/hermes-0206-upgrade
prekshivyas Aug 30, 2026
e384bcf
fix(hermes): repair 0.20.6 E2E regressions
prekshivyas Aug 31, 2026
216ba8b
merge(main): update Hermes upgrade branch
prekshivyas Aug 31, 2026
42471ab
fix(hermes): repair 0.20.6 E2E regressions
prekshivyas Aug 31, 2026
128522e
merge(main): update Hermes upgrade branch
prekshivyas Aug 31, 2026
08aad07
test(e2e): close Hermes fixture gaps
prekshivyas Aug 31, 2026
3411f78
fix(hermes): repair deterministic E2E regressions
prekshivyas Aug 31, 2026
e626167
fix(hermes): preserve cron recovery gate time
prekshivyas Aug 31, 2026
0efcebe
fix(e2e): repair Hermes 0.20.6 fixture assertions
prekshivyas Aug 31, 2026
9d2e195
Merge branch 'main' into codex/hermes-0206-upgrade
prekshivyas Aug 31, 2026
e2aedff
merge(main): update Hermes upgrade branch
prekshivyas Sep 1, 2026
3dacc53
Merge branch 'main' into codex/hermes-0206-upgrade
prekshivyas Sep 1, 2026
bbab740
fix(hermes): align lifecycle and test contracts
cv Sep 1, 2026
25e1342
test(hermes): align lifecycle package fixture
cv Sep 1, 2026
1354b97
test(hermes): remove obsolete test helpers
cv Sep 1, 2026
ecf3708
fix(hermes): resolve advisor lifecycle findings
cv Sep 1, 2026
e479c6f
test(hermes): align lifecycle observation fixtures
cv Sep 1, 2026
91d5530
test(hermes): remove stale browser cache helper
cv Sep 1, 2026
99d0863
test(hermes): remove unused dependency review setup
cv Sep 1, 2026
4aa75e6
Merge branch 'main' into codex/hermes-0206-upgrade
prekshivyas Sep 1, 2026
bdc726f
merge(main): refresh Hermes 0.20.6 upgrade
prekshivyas Sep 1, 2026
6bd002a
merge(main): include latest platform support
prekshivyas Sep 1, 2026
55c2682
Merge branch 'main' into codex/hermes-0206-upgrade
prekshivyas Sep 1, 2026
488f1e6
Merge branch 'main' into codex/hermes-0206-upgrade
prekshivyas Sep 1, 2026
ff68d77
fix(hermes): normalize mutable shields state
prekshivyas Sep 1, 2026
88a7c41
fix(hermes): address advisor findings
prekshivyas Sep 2, 2026
02c236c
Merge branch 'main' into codex/hermes-0206-upgrade
prekshivyas Sep 2, 2026
4deef0d
merge: sync Hermes upgrade with main and address reviews
prekshivyas Sep 2, 2026
591483d
Merge remote-tracking branch 'origin/main' into codex/pr10595-advisor…
prekshivyas Sep 2, 2026
ebe8a9d
fix(hermes): match Langfuse locked source shape
prekshivyas Sep 2, 2026
1d1a606
Merge remote-tracking branch 'origin/main' into codex/pr10595-advisor…
prekshivyas Sep 2, 2026
2cebf41
ci: retry modern GHCR digest misses
prekshivyas Sep 2, 2026
6de7ba8
ci: retry unavailable GHCR digest pulls
prekshivyas Sep 2, 2026
417f8c7
fix(hermes): keep managed image below layer limit
prekshivyas Sep 2, 2026
07b184e
fix(hermes): prove image publication integrity
prekshivyas Sep 2, 2026
ce1c762
fix(hermes): close final advisor findings
prekshivyas Sep 2, 2026
64b455d
fix(hermes): verify build scripts before execution
prekshivyas Sep 2, 2026
0bce4a2
fix(hermes): close remaining review findings
prekshivyas Sep 2, 2026
2ce65e7
merge: sync main and resolve review findings
prekshivyas Sep 2, 2026
baa9668
test(hermes): replace source-shape upgrade checks
prekshivyas Sep 2, 2026
4599963
Merge branch 'main' into codex/hermes-0206-upgrade
prekshivyas Sep 2, 2026
daffdec
fix(hermes): repair image build contracts
prekshivyas Sep 2, 2026
d7855f9
fix(hermes): include layout helper in portable context
prekshivyas Sep 2, 2026
873f470
fix(hermes): pin current base image
prekshivyas Sep 2, 2026
1464849
Merge remote-tracking branch 'origin/main' into codex/pr10595-final-d…
prekshivyas Sep 3, 2026
61d0683
test(hermes): align managed qualification evidence
prekshivyas Sep 3, 2026
9ef7503
test(hermes): exercise sqlite pragmas
prekshivyas Sep 3, 2026
dbcf087
merge(hermes): resolve current main
prekshivyas Sep 3, 2026
bbfccf0
merge: sync Hermes 0.20.6 upgrade with main
prekshivyas Sep 3, 2026
0c3d293
merge: reconcile concurrent Hermes resolution
prekshivyas Sep 3, 2026
cfe6178
fix(pi): refresh Hermes qualification receipts
prekshivyas Sep 3, 2026
870bc03
fix(hermes): repair upgraded image build
prekshivyas Sep 3, 2026
6c61b4c
fix(hermes): restore final image build gates
prekshivyas Sep 3, 2026
e156cee
fix(hermes): preserve shared runtime modes
prekshivyas Sep 3, 2026
e49e9a0
fix(hermes): admit permission patch to portable builds
prekshivyas Sep 3, 2026
4fea838
fix(hermes): bind A2A neutralization patch
prekshivyas Sep 3, 2026
9d8f410
fix(hermes): remove Hindsight build probe
prekshivyas Sep 3, 2026
f6e8618
fix(hermes): keep managed image within layer limit
prekshivyas Sep 3, 2026
41eb922
merge(main): refresh Hermes repair validation base
prekshivyas Sep 3, 2026
75f7a33
fix(ci): retain managed contracts across reruns
prekshivyas Sep 3, 2026
310bd97
test(hermes): exercise managed fallback boundaries
prekshivyas Sep 3, 2026
9aee878
Merge branch 'main' into codex/hermes-0206-upgrade
prekshivyas Sep 3, 2026
45ba65f
fix(hermes): repair restart and rebuild e2e
prekshivyas Sep 3, 2026
225b150
Merge commit '36f545485' into codex/hermes-0206-upgrade-publish
prekshivyas Sep 3, 2026
199a45e
fix(hermes): bind validator image checksum
prekshivyas Sep 3, 2026
8489f2b
merge: resolve conflicts with main
github-actions[bot] Sep 3, 2026
3619372
Merge remote-tracking branch 'origin/main' into codex/pr10595-focused…
prekshivyas Sep 3, 2026
9e51d29
Merge branch 'main' into codex/hermes-0206-upgrade
prekshivyas Sep 3, 2026
e6a11db
merge: sync Hermes upgrade with main
prekshivyas Sep 3, 2026
50d339b
test(hermes): strengthen review coverage
prekshivyas Sep 4, 2026
a3ccb8c
Merge remote-tracking branch 'origin/main' into codex/pr10595-conflic…
prekshivyas Sep 4, 2026
2a3627a
merge: resolve conflicts with main
github-actions[bot] Sep 4, 2026
33590f2
Merge branch 'main' into codex/hermes-0206-upgrade
prekshivyas Sep 4, 2026
f618288
Merge branch 'main' into codex/hermes-0206-upgrade
prekshivyas Sep 4, 2026
f5eeb5b
Merge branch 'main' into codex/hermes-0206-upgrade
prekshivyas Sep 4, 2026
7ba3bab
fix(hermes): pin gateway package environment
jyaunches Sep 4, 2026
1828a0e
fix(ci): mark WeChat runtime peer optional
jyaunches Sep 4, 2026
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
2 changes: 1 addition & 1 deletion .github/actions/resolve-hermes-base-image/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ runs:
--read-only \
--user sandbox \
--entrypoint /opt/hermes/.venv/bin/python "$ref" -I -c \
'import importlib.metadata as metadata; import sys; import acp; import mcp; from acp_adapter.server import HermesACPAgent; from tools import mcp_tool; metadata.version("agent-client-protocol") == "0.9.0" or sys.exit(1); getattr(mcp_tool, "_MCP_AVAILABLE", False) or sys.exit(1); getattr(mcp_tool, "_MCP_HTTP_AVAILABLE", False) or sys.exit(1)' \
'import importlib.metadata as metadata; import sys; import acp; import mcp; from acp_adapter.server import HermesACPAgent; from tools import mcp_tool; metadata.version("agent-client-protocol") == "0.9.0" or sys.exit(1); mcp_tool._ensure_mcp_sdk() or sys.exit(1); getattr(mcp_tool, "_MCP_AVAILABLE", False) or sys.exit(1); getattr(mcp_tool, "_MCP_HTTP_AVAILABLE", False) or sys.exit(1)' \
>/dev/null 2>&1
}

Expand Down
14 changes: 14 additions & 0 deletions .github/workflows/managed-images.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,13 @@ jobs:
run: |
set -euo pipefail
image_json="$(docker image inspect "$IMAGE_REFERENCE")"
if [ "$AGENT" = "hermes" ]; then
layer_count="$(jq -er '.[0].RootFS.Layers | length' <<< "$image_json")"
if [ "$layer_count" -gt 125 ]; then
echo "ERROR: Hermes managed image has ${layer_count} filesystem layers; the publication runner compatibility ceiling is 125." >&2
exit 1
fi
fi
Comment thread
coderabbitai[bot] marked this conversation as resolved.
image_id="$(
jq -er '
if length == 1 and (.[0].Id | type) == "string"
Expand Down Expand Up @@ -856,6 +863,13 @@ jobs:
reference="${IMAGE}@${DIGEST}"
raw="$RUNNER_TEMP/${AGENT}-published-manifest.raw"
scripts/checks/pull-public-exact-digest.sh "$reference" linux/amd64
if [ "$AGENT" = "hermes" ]; then
published_layer_count="$(docker image inspect --format '{{len .RootFS.Layers}}' "$reference")"
if [ "$published_layer_count" -gt 125 ]; then
echo "ERROR: Hermes managed image has ${published_layer_count} filesystem layers; the publication runner compatibility ceiling is 125." >&2
exit 1
fi
fi
docker buildx imagetools inspect "$reference" --raw > "$raw"
[[ "sha256:$(sha256sum "$raw" | awk '{print $1}')" == "$DIGEST" ]] || {
echo "ERROR: published PR manifest bytes do not match the build digest" >&2
Expand Down
415 changes: 221 additions & 194 deletions agents/hermes/Dockerfile

Large diffs are not rendered by default.

121 changes: 97 additions & 24 deletions agents/hermes/Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,26 @@ FROM node:24-trixie-slim@sha256:05c08ce4291e9a58f59456a7985176defb12cdd42271f35f
COPY --from=perl-builder /out /tmp/nemoclaw-native-security

ENV DEBIAN_FRONTEND=noninteractive
ENV HERMES_SKIP_CHMOD=1

# Hermes version pinned for reproducibility. All four values below are
# managed by scripts/update-hermes-agent.sh — HERMES_VERSION is the GitHub
# calver tag, HERMES_SEMVER the matching package version from the release's
# 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.20 = Hermes Agent v0.19.0.
ARG HERMES_VERSION=v2026.7.20
ARG HERMES_SEMVER=0.19.0
ARG HERMES_TARBALL_SHA256=285f3fc134ff466a90065e1517801a68993733b807158ee8f32aa01613786990
ARG HERMES_NPM_INTEGRITY=sha512-+oVKG3lXbk2kEP+J6BXZjtmSBSaFfczIdOWQ9CUSTdTqq2uyHbk4p+kPyZ6MeGs56JU5qXzMNbqGKRVOQRGC1A==
# Calver tag v2026.8.27 = Hermes Agent v0.20.6.
ARG HERMES_VERSION=v2026.8.27
ARG HERMES_SEMVER=0.20.6
ARG HERMES_TARBALL_SHA256=e622723b5bf3cd6c1db974d92d32242f1cb63f61c1112b6f708b34d619ef0fc7
ARG HERMES_NPM_INTEGRITY=sha512-s5q1IEBifCBb77QMwkse4MRaAaoZSxIa4IkicIO3jL7MIdq15YvnSyiNvsTOWNBi6t3shFpIg+H7+9MJsOiSkg==
ARG HERMES_UV_EXTRAS="anthropic messaging web pty mcp acp"
ARG NODE_VERSION=24.18.1
ARG UV_VERSION=0.11.33
ARG NEMOCLAW_HERMES_RUNTIME_BOUNDARIES_PATCH_SHA256=9e41bed797965990bf7edf214c782c18c04d086a15042415ae7085a507873b1c
ARG NEMOCLAW_HERMES_RUNTIME_BOUNDARIES_PATCH_SHA256=820563cc3450de582663c8137670001bb9974ac8a5584901880982f6509747b8
ARG NEMOCLAW_HERMES_SECURE_DIR_SOURCE_SHA256=ffae3271120cf53eb8a7f574f76758eac3ea172f9ddc22c1056277e37d8d392c
ARG NEMOCLAW_HERMES_SECURE_DIR_PATCH_SHA256=1c1a898226bc67b123ec72847d4dcda7abbab1e95958c9e019790d9425e15d69
ARG NEMOCLAW_HERMES_SECURE_DIR_OUTPUT_SHA256=130cf4e76d6f4f16b85517cf8d3d81dfe294aff63d6c561b979971c401f78761

# build-essential: hermes-agent >= 0.16.0 ships npm dependencies that need a
# node-gyp native build during `npm ci`; the runtime Dockerfile purges build
Expand Down Expand Up @@ -425,7 +429,8 @@ RUN printf '%s\n' \
# final image when selected.
# New Hermes integrations should be installed by the agent workflow when they
# are enabled rather than shipped in the base image by default.
# Root Node dependencies provide Hermes browser tooling such as agent-browser.
# Hermes 0.20.6 keeps agent-browser outside its root dependency graph. The
# root-owned runtime below installs the pinned package from a reviewed lockfile.
# The WhatsApp adapter ships a separate Node project under
# scripts/whatsapp-bridge whose dependencies Hermes otherwise installs lazily
# on the first `hermes whatsapp` run. That lazy `npm install` targets
Expand All @@ -438,13 +443,21 @@ RUN printf '%s\n' \
# build.
RUN pip3 install --no-cache-dir --break-system-packages "uv==${UV_VERSION}" \
&& uv_version_output="$(uv --version)" \
&& uv_version="${uv_version_output#uv }" \
&& test "${uv_version%% *}" = "${UV_VERSION}"
&& uv_command="${uv_version_output%% *}" \
&& uv_version_tail="${uv_version_output#* }" \
&& uv_version="${uv_version_tail%% *}" \
&& [ "$uv_version_tail" != "$uv_version_output" ] \
&& [ "$uv_command" = "uv" ] \
&& [ "$uv_version" = "$UV_VERSION" ]
# Upstream tests are not part of the production runtime and can contain
# intentionally hostile security-test fixtures. Remove them in the extraction
# RUN so their bytes never enter a published image layer.
COPY agents/hermes/security-dependencies.patch /tmp/hermes-security-dependencies.patch
COPY agents/hermes/agent-browser-runtime/package.json \
agents/hermes/agent-browser-runtime/package-lock.json \
/opt/nemoclaw-agent-browser-runtime/
COPY agents/hermes/runtime-boundaries.patch /tmp/hermes-runtime-boundaries.patch
COPY agents/hermes/secure-dir-skip-chmod.patch /tmp/hermes-secure-dir-skip-chmod.patch
COPY agents/hermes/whatsapp-proxy.patch /tmp/hermes-whatsapp-proxy.patch
COPY scripts/checks/download-hermes-source-archive.sh /tmp/nemoclaw-download-hermes-source-archive.sh
# hadolint ignore=DL4006
Expand All @@ -454,11 +467,22 @@ RUN mkdir -p /opt/hermes \
&& sha256sum -c /tmp/hermes.tar.gz.sha256 \
&& tar -xzf /tmp/hermes.tar.gz -C /opt/hermes --strip-components=1 \
&& rm -rf /opt/hermes/tests \
&& printf '%s %s\n' "$NEMOCLAW_HERMES_RUNTIME_BOUNDARIES_PATCH_SHA256" /tmp/hermes-runtime-boundaries.patch | sha256sum -c - \
&& printf '%s %s\n' \
"$NEMOCLAW_HERMES_RUNTIME_BOUNDARIES_PATCH_SHA256" /tmp/hermes-runtime-boundaries.patch \
"$NEMOCLAW_HERMES_SECURE_DIR_PATCH_SHA256" /tmp/hermes-secure-dir-skip-chmod.patch \
"$NEMOCLAW_HERMES_SECURE_DIR_SOURCE_SHA256" /opt/hermes/hermes_cli/config.py \
| sha256sum -c - \
|| { echo "ERROR: Hermes secure-directory patch or source identity mismatch" >&2; exit 1; } \
&& git -C /opt/hermes apply --check /tmp/hermes-security-dependencies.patch \
&& git -C /opt/hermes apply /tmp/hermes-security-dependencies.patch \
&& git -C /opt/hermes apply --check /tmp/hermes-runtime-boundaries.patch \
&& git -C /opt/hermes apply /tmp/hermes-runtime-boundaries.patch \
&& git -C /opt/hermes apply --check /tmp/hermes-secure-dir-skip-chmod.patch \
&& git -C /opt/hermes apply /tmp/hermes-secure-dir-skip-chmod.patch \
&& printf '%s %s\n' \
"$NEMOCLAW_HERMES_SECURE_DIR_OUTPUT_SHA256" /opt/hermes/hermes_cli/config.py \
| sha256sum -c - \
|| { echo "ERROR: Hermes secure-directory patched output identity mismatch" >&2; exit 1; } \
&& grep -Fq 'def nemoclaw_managed_gateway_plugins_only()' /opt/hermes/hermes_constants.py \
&& grep -Fq 'nemoclaw_protected_process_control' /opt/hermes/hermes_cli/env_loader.py \
&& grep -Fq 'nemoclaw_sanitized_installer_env' /opt/hermes/tools/lazy_deps.py \
Expand All @@ -470,6 +494,7 @@ RUN mkdir -p /opt/hermes \
&& git -C /opt/hermes apply /tmp/hermes-whatsapp-proxy.patch \
&& rm /tmp/hermes.tar.gz /tmp/hermes.tar.gz.sha256 \
/tmp/hermes-security-dependencies.patch /tmp/hermes-runtime-boundaries.patch \
/tmp/hermes-secure-dir-skip-chmod.patch \
/tmp/hermes-whatsapp-proxy.patch \
/tmp/nemoclaw-download-hermes-source-archive.sh

Expand Down Expand Up @@ -512,16 +537,20 @@ WORKDIR /opt/hermes
# workspaces disabled; this keeps browser tooling without either UI build tree.
# Root npm, Electron, and node-gyp caches are build-only. Remove them in the
# same RUN that creates them so Docker cannot retain their bytes in this layer.
# Hermes ships a `.python-version` for Python 3.11. Use the reviewed system
# Python 3.13 directly and forbid a root-private uv-managed interpreter so the
# finished virtual environment remains executable by the sandbox user.
# hadolint ignore=SC2086
RUN set -eu; \
set --; \
for extra in ${HERMES_UV_EXTRAS}; do \
set -- "$@" --extra "$extra"; \
done; \
uv sync --frozen --no-dev "$@" --no-cache \
uv sync --python /usr/bin/python3.13 --no-managed-python \
--frozen --no-dev "$@" --no-cache \
&& uv pip check --python /opt/hermes/.venv/bin/python \
&& /opt/hermes/.venv/bin/python -I -c \
"from importlib.metadata import version; expected = {'agent-client-protocol': '0.9.0', 'aiohttp': '3.14.3', 'cryptography': '50.0.0', 'mcp': '1.28.1', 'pillow': '12.3.0', 'starlette': '1.3.1', 'tornado': '6.5.7'}; actual = {name: version(name) for name in expected}; assert actual == expected, actual" \
"from importlib.metadata import version; expected = {'agent-client-protocol': '0.9.0', 'aiohttp': '3.14.3', 'cryptography': '50.0.0', 'mcp': '2.0.0', 'pillow': '12.3.0', 'starlette': '1.3.1', 'tornado': '6.5.7'}; actual = {name: version(name) for name in expected}; assert actual == expected, actual" \
&& npm ci --prefer-offline --no-audit --no-fund \
&& for ui_dir in ui-tui web; do \
if [ -f "${ui_dir}/package-lock.json" ]; then \
Expand Down Expand Up @@ -563,11 +592,11 @@ RUN set -eu; \
# route that uses File/Form, so without python-multipart the plugin's API routes
# fail to mount ("Form data requires python-multipart to be installed").
#
# Hermes 0.19.0 resolves 0.0.27, which is affected by the network-reachable
# GHSA-5rvq-cxj2-64vf CPU denial of service and GHSA-6jv3-5f52-599m parser
# differential. Keep this hash-verified downstream override at the first stable
# release that fixes those issues plus GHSA-v9pg-7xvm-68hf. Re-review the
# version and both hashes on every Hermes version bump.
# Hermes 0.20.6 resolves 0.0.32, the first stable release that addresses the
# reviewed network-reachable GHSA-5rvq-cxj2-64vf CPU denial of service,
# GHSA-6jv3-5f52-599m parser differential, and GHSA-v9pg-7xvm-68hf. Keep the
# hash-verified install so the final image cannot inherit a stale published
# base. Re-review the version and both hashes on every Hermes version bump.
# uv creates a phony .git cache marker even with --no-cache. Remove the cache
# after the final uv command because the root cache is not used at runtime.
# hadolint ignore=DL3059
Expand All @@ -583,6 +612,10 @@ RUN printf '%s\n' \
"import multipart; assert multipart.__version__ == '0.0.32', multipart.__version__" \
&& rm -rf /root/.cache/uv

# The offline lazy-package probe runs as the sandbox user. Make the finalized
# virtual environment readable before that unprivileged build-time check.
RUN chmod -R a+rX /opt/hermes/.venv

# Keep official-package compatibility separate from the final image's offline
# lazy-installer contract. BuildKit verifies both reviewed PyPI artifacts before
# any build command can consume them. Install only those hash-locked wheels into
Expand Down Expand Up @@ -621,12 +654,53 @@ RUN --network=none install -d -o sandbox -g sandbox -m 0750 \
ENV PATH="/usr/local/bin:/opt/hermes/.venv/bin:${PATH}" \
HERMES_TUI_DIR="/opt/hermes/ui-tui" \
HERMES_WEB_DIST="/opt/hermes/hermes_cli/web_dist"

# Hermes 0.20.6 removed agent-browser from its root dependency graph. Install
# the reviewed lockfile into an immutable root-owned runtime so browser
# credentials never cross a sandbox-writable executable boundary.
RUN HOME=/sandbox \
/usr/bin/setpriv --reuid=sandbox --regid=sandbox --init-groups -- \
/opt/hermes/.venv/bin/python -c \
Comment thread
prekshivyas marked this conversation as resolved.
'from tools import browser_tool; assert browser_tool.AGENT_BROWSER_NPX_SPEC == "agent-browser@0.26.0"; assert browser_tool._find_agent_browser() == browser_tool.NPX_AGENT_BROWSER_SENTINEL'
RUN install -o root -g root -m 0400 /dev/null \
/tmp/nemoclaw-agent-browser-userconfig \
&& install -o root -g root -m 0400 /dev/null \
/tmp/nemoclaw-agent-browser-globalconfig \
&& HOME=/root npm_config_cache=/root/.npm \
npm_config_registry=https://registry.npmjs.org/ \
npm_config_userconfig=/tmp/nemoclaw-agent-browser-userconfig \
npm_config_globalconfig=/tmp/nemoclaw-agent-browser-globalconfig \
/usr/local/bin/npm ci \
--prefix /opt/nemoclaw-agent-browser-runtime \
--ignore-scripts --no-audit --no-fund \
&& agent_browser_install_version="$( \
/opt/nemoclaw-agent-browser-runtime/node_modules/.bin/agent-browser --version \
)" \
&& test "$agent_browser_install_version" = 'agent-browser 0.26.0' \
&& chown -R root:root /opt/nemoclaw-agent-browser-runtime \
&& chmod -R go-w /opt/nemoclaw-agent-browser-runtime \
&& ln -s /opt/nemoclaw-agent-browser-runtime/node_modules/.bin/agent-browser \
/usr/local/bin/agent-browser \
&& rm -rf /root/.npm \
/tmp/nemoclaw-agent-browser-userconfig \
/tmp/nemoclaw-agent-browser-globalconfig
RUN --network=none agent_browser_version="$( \
/usr/bin/setpriv --reuid=sandbox --regid=sandbox --init-groups -- \
/usr/local/bin/agent-browser --version \
)" \
&& test "${agent_browser_version}" = 'agent-browser 0.26.0' \
&& test "$(readlink /usr/local/bin/agent-browser)" = \
'/opt/nemoclaw-agent-browser-runtime/node_modules/.bin/agent-browser' \
&& ! /usr/bin/setpriv --reuid=sandbox --regid=sandbox --init-groups -- \
/bin/sh -c 'printf unsafe >> /opt/nemoclaw-agent-browser-runtime/node_modules/agent-browser/bin/agent-browser.js' \
&& ! /usr/bin/setpriv --reuid=sandbox --regid=sandbox --init-groups -- \
/bin/sh -c 'rm /usr/local/bin/agent-browser' \
&& HOME=/sandbox /usr/bin/setpriv --reuid=sandbox --regid=sandbox --init-groups -- \
/opt/hermes/.venv/bin/python -c \
'from tools import browser_tool; assert browser_tool._find_agent_browser() == "/usr/local/bin/agent-browser"'

RUN /usr/local/bin/hermes --version \
&& /usr/local/bin/hermes acp --check \
&& test -x /opt/hermes/node_modules/.bin/agent-browser \
&& /opt/hermes/node_modules/.bin/agent-browser --version \
&& /opt/hermes/.venv/bin/python -c \
'from tools import browser_tool; expected = "/opt/hermes/node_modules/.bin/agent-browser"; assert browser_tool._find_agent_browser() == expected' \
&& test -s "${HERMES_TUI_DIR}/dist/entry.js" \
&& test -s "${HERMES_WEB_DIST}/index.html" \
&& test ! -e /opt/hermes/.node_modules.runtime \
Expand All @@ -636,10 +710,9 @@ RUN /usr/local/bin/hermes --version \
&& rmdir /opt/hermes/node_modules \
&& mv /opt/hermes/.node_modules.runtime /opt/hermes/node_modules \
&& /opt/hermes/.venv/bin/python -c \
'import mcp; from tools import mcp_tool; assert getattr(mcp_tool, "_MCP_AVAILABLE", False), "Hermes MCP client runtime is unavailable"; assert getattr(mcp_tool, "_MCP_HTTP_AVAILABLE", False), "Hermes MCP Streamable HTTP runtime is unavailable"'
'import mcp; from tools import mcp_tool; assert mcp_tool._ensure_mcp_sdk(), "Hermes MCP client runtime is unavailable"; assert getattr(mcp_tool, "_MCP_AVAILABLE", False), "Hermes MCP client runtime is unavailable"; assert getattr(mcp_tool, "_MCP_HTTP_AVAILABLE", False), "Hermes MCP Streamable HTTP runtime is unavailable"'

RUN chmod -R a+rX /opt/hermes/.venv \
&& test -r /opt/hermes/.venv/pyvenv.cfg \
RUN test -r /opt/hermes/.venv/pyvenv.cfg \
&& su -s /bin/sh sandbox -c '/opt/hermes/.venv/bin/python3 -c "import urllib.request"'

# Reject build-only paths before the base image can be published.
Expand Down
43 changes: 43 additions & 0 deletions agents/hermes/a2a-neutral.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# NemoClaw has no accepted product scope for Hermes A2A. Keep its inbound
# adapter discoverable but disabled, and remove its outbound tool registration.
diff --git a/plugins/platforms/a2a/plugin.yaml b/plugins/platforms/a2a/plugin.yaml
--- a/plugins/platforms/a2a/plugin.yaml
+++ b/plugins/platforms/a2a/plugin.yaml
@@ -27,15 +27,5 @@ description: >
Pure stdlib transport (http.server + urllib) — no a2a-sdk dependency required.
author: Nous Research
-# The outbound client tools. Declaring them here is what asks discovery to
-# import `tools.py` in CLI/TUI processes, where the plugin is otherwise
-# deferred and the tools would never register at all (#78050). The inbound
-# adapter stays deferred either way — only this submodule is imported.
-provides_tools:
- - a2a_discover
- - a2a_call
- - a2a_list
- - a2a_history
- - a2a_orchestrate
# requires_env / optional_env are surfaced in the `hermes config` UI via the
# platform-plugin env var injector in hermes_cli/config.py.
requires_env: []
diff --git a/plugins/platforms/a2a/__init__.py b/plugins/platforms/a2a/__init__.py
--- a/plugins/platforms/a2a/__init__.py
+++ b/plugins/platforms/a2a/__init__.py
@@ -97,14 +97,6 @@ def interactive_setup() -> None:
def register(ctx) -> None:
"""Plugin entry point — called by the Hermes plugin system."""
- # 1) Client tools (outbound). Registering these even when the inbound
- # platform is disabled lets the agent call peers without exposing itself.
- try:
- from .tools import register_tools
- register_tools(ctx)
- except Exception:
- logger.warning("A2A: failed to register client tools", exc_info=True)
-
- # 2) Inbound platform adapter.
+ # The managed policy keeps this inbound adapter disabled.
try:
from .adapter import A2AAdapter
ctx.register_platform(
25 changes: 25 additions & 0 deletions agents/hermes/agent-browser-runtime/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions agents/hermes/agent-browser-runtime/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "nemoclaw-hermes-agent-browser-runtime",
"version": "1.0.0",
"private": true,
"license": "Apache-2.0",
"dependencies": {
"agent-browser": "0.26.0"
}
}
Loading
Loading