Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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
157 changes: 84 additions & 73 deletions agents/hermes/Dockerfile

Large diffs are not rendered by default.

102 changes: 82 additions & 20 deletions agents/hermes/Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,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.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
Expand Down Expand Up @@ -424,7 +424,10 @@ 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 deliberately keeps agent-browser outside the root workspace
# graph and resolves the exact NemoClaw-pinned package through npx. Warm that
# package in the sandbox user's runtime cache below so browser startup does not
# require a registry fetch.
# 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 @@ -443,6 +446,7 @@ RUN pip3 install --no-cache-dir --break-system-packages "uv==${UV_VERSION}" \
# 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 /tmp/nemoclaw-agent-browser-runtime/
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
RUN mkdir -p /opt/hermes \
Expand Down Expand Up @@ -498,16 +502,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 @@ -549,11 +557,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 @@ -569,6 +577,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 @@ -607,12 +619,63 @@ 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. Prove
# that the patched source selects the exact npx fallback. Cache the exact
# registry package, bind its observed integrity to the reviewed lockfile, then
# prove the cached package works without network access.
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 chown -R sandbox:sandbox /tmp/nemoclaw-agent-browser-runtime \
&& install -o sandbox -g sandbox -m 0400 /dev/null \
/tmp/nemoclaw-agent-browser-runtime/npm-userconfig \
&& install -o sandbox -g sandbox -m 0400 /dev/null \
/tmp/nemoclaw-agent-browser-runtime/npm-globalconfig \
&& expected_browser_integrity='sha512-pdqSfjwbFSp+qnwlb2g23e9wXveIOfMi19xpPA9xZUbzEAUp6W4YBZj6Ybj8z4M7WkcbGDDYc+oDIHDt9R3EDQ==' \
&& HOME=/sandbox npm_config_cache=/sandbox/.npm \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P1] Keep the npx executable outside the sandbox write boundary

This creates and executes the /sandbox/.npm npx cache as the sandbox user, and no later layer makes that cache root-owned or immutable. Supported OpenShell startup also runs Hermes as sandbox, so untrusted sandbox code can replace the cached agent-browser launcher before Hermes invokes it. Hermes then passes BROWSERBASE_API_KEY, BROWSER_USE_API_KEY, FIRECRAWL_API_KEY, and related browser keys into that launcher. An exact npm 11 reproduction left the _npx launcher sandbox-owned with mode 0700 and executed a replacement offline. Install the reviewed executable in a root-owned immutable path and add a negative sandbox-mutation test.

npm_config_registry=https://registry.npmjs.org/ \
npm_config_userconfig=/tmp/nemoclaw-agent-browser-runtime/npm-userconfig \
npm_config_globalconfig=/tmp/nemoclaw-agent-browser-runtime/npm-globalconfig \
npm_config_ignore_scripts=true \
/usr/bin/setpriv --reuid=sandbox --regid=sandbox --init-groups -- \
/usr/local/bin/npm cache add agent-browser@0.26.0 \
&& registry_browser_integrity="$( \
HOME=/sandbox npm_config_cache=/sandbox/.npm \
npm_config_registry=https://registry.npmjs.org/ \
npm_config_userconfig=/tmp/nemoclaw-agent-browser-runtime/npm-userconfig \
npm_config_globalconfig=/tmp/nemoclaw-agent-browser-runtime/npm-globalconfig \
npm_config_ignore_scripts=true \
/usr/bin/setpriv --reuid=sandbox --regid=sandbox --init-groups -- \
/usr/local/bin/npm view agent-browser@0.26.0 dist.integrity \
)" \
&& test "${registry_browser_integrity}" = "${expected_browser_integrity}" \
&& HOME=/sandbox npm_config_cache=/sandbox/.npm \
npm_config_registry=https://registry.npmjs.org/ \
npm_config_userconfig=/tmp/nemoclaw-agent-browser-runtime/npm-userconfig \
npm_config_globalconfig=/tmp/nemoclaw-agent-browser-runtime/npm-globalconfig \
npm_config_ignore_scripts=true \
/usr/bin/setpriv --reuid=sandbox --regid=sandbox --init-groups -- \
/usr/local/bin/npm ci \
--prefix /tmp/nemoclaw-agent-browser-runtime \
--ignore-scripts --no-audit --no-fund \
&& rm -rf /tmp/nemoclaw-agent-browser-runtime/node_modules
RUN --network=none agent_browser_version="$( \
HOME=/sandbox npm_config_cache=/sandbox/.npm npm_config_offline=true \
npm_config_registry=https://registry.npmjs.org/ \
npm_config_userconfig=/tmp/nemoclaw-agent-browser-runtime/npm-userconfig \
npm_config_globalconfig=/tmp/nemoclaw-agent-browser-runtime/npm-globalconfig \
npm_config_ignore_scripts=true \
/usr/bin/setpriv --reuid=sandbox --regid=sandbox --init-groups -- \
/usr/local/bin/npx --ignore-scripts --prefer-offline -y \
agent-browser@0.26.0 --version \
)" \
&& test "${agent_browser_version}" = 'agent-browser 0.26.0' \
&& rm -rf /tmp/nemoclaw-agent-browser-runtime

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 @@ -622,10 +685,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
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.

8 changes: 8 additions & 0 deletions agents/hermes/agent-browser-runtime/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "nemoclaw-hermes-agent-browser-runtime",
"version": "1.0.0",
"private": true,
"dependencies": {
"agent-browser": "0.26.0"
}
}
2 changes: 1 addition & 1 deletion agents/hermes/config/managed-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const MANAGED_IMAGE_HERMES_SUPPORTED_PLATFORMS = [
"google_chat",
] as const;

// Hermes v0.19.0 also packages platform plugins and built-in adapters that are
// Hermes v0.20.6 also packages platform plugins and built-in adapters that are
Comment thread
prekshivyas marked this conversation as resolved.
// not yet supported by NemoClaw's messaging manifests. A neutral managed image
// must explicitly disable the complete installed surface, while keeping this
// list separate from the supported/activatable contract above.
Expand Down
6 changes: 3 additions & 3 deletions agents/hermes/hermes-cli-adapter-v1.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"adapter_version": 1,
"upstream_cli_version": "0.19.0",
"upstream_cli_version": "0.20.6",
Comment thread
prekshivyas marked this conversation as resolved.
"managed_commands": ["chat"],
"session_name_coalescer": {
"module": "hermes_cli.main",
Expand Down Expand Up @@ -128,7 +128,7 @@
"hermes [(-p|--profile) PROFILE] ((-r|--resume) SESSION|(-c|--continue) [SESSION]) (-z|--oneshot) PROMPT [CHAT_OPTION ...]",
"hermes [--profile=PROFILE] (--resume=SESSION|--continue[=SESSION]) --oneshot=PROMPT [CHAT_OPTION ...]"
],
"reason": "Hermes 0.19 stores a top-level resumed or continued one-shot turn in a new session instead of the selected or most recent session.",
"reason": "Hermes 0.20.6 stores a top-level resumed or continued one-shot turn in a new session instead of the selected or most recent session.",
"source_fix_constraint": "The affected session persistence implementation belongs to Hermes. The durable source fix must land in Hermes before NemoClaw removes this translation.",
"removal_condition": "Hermes appends top-level resumed and continued one-shot turns to the selected or most recent session."
},
Expand All @@ -143,7 +143,7 @@
"hermes [TOP_LEVEL_OPTION ...] chat [CHAT_OPTION ...] MODEL_OPTION [CHAT_OPTION ...] PROVIDER_OPTION [CHAT_OPTION ...]"
],
"ambiguity_rule": "A top-level session name must be one argv value. The adapter rejects separate provider and model flags after an unquoted multi-word session name because a later positional can be an upstream command.",
"reason": "Hermes 0.19 does not resolve the OpenShell credential placeholder when provider and model are separate.",
"reason": "Hermes 0.20.6 does not resolve the OpenShell credential placeholder when provider and model are separate.",
"source_fix_constraint": "The affected credential-resolution implementation belongs to Hermes. The durable source fix must land in Hermes before NemoClaw removes this translation.",
"removal_condition": "Hermes resolves OpenShell credential placeholders for separate provider and model flags."
}
Expand Down
25 changes: 9 additions & 16 deletions agents/hermes/image-build-probes.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,20 +146,7 @@ def reject_network(*_args, **_kwargs):
finally:
socket.socket.connect = original_connect
socket.create_connection = original_create_connection
bundled_plugins = {
manifest.parent.name
for manifest in Path("/opt/hermes/plugins/platforms").glob("*/plugin.yaml")
}
built_in_optional = {
platform.value
for platform in Platform
if platform.value not in {"api_server", "local"}
}
expected = bundled_plugins | built_in_optional
assert "google_chat" in expected, expected
assert "whatsapp_cloud" in expected, expected

for name in expected:
for name in ("google_chat", "whatsapp_cloud"):
platform = Platform(name)
platform_config = config.platforms.get(platform)
assert platform_config is not None, name
Expand All @@ -170,12 +157,18 @@ def reject_network(*_args, **_kwargs):


def verify_cron_runtime_source() -> None:
from cron.executions import EXECUTIONS_FILE
from cron.executions import EXECUTIONS_FILE, _connect
from hermes_cli.backup import _QUICK_STATE_FILES
from hermes_constants import get_hermes_home

expected = get_hermes_home().resolve() / "runtime" / "cron-executions.db"
assert EXECUTIONS_FILE == expected
assert EXECUTIONS_FILE is None
connection = _connect()
try:
databases = connection.execute("PRAGMA database_list").fetchall()
finally:
connection.close()
assert databases == [(0, "main", str(expected))], databases
assert "runtime/cron-executions.db" in _QUICK_STATE_FILES
assert "cron/executions.db" not in _QUICK_STATE_FILES

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.19.0"
# Hermes reports semver from `hermes --version` (for example, `0.19.0`) even
# though its GitHub release cadence is calendar-based (`v2026.7.20`). Declaring the
expected_version: "0.20.6"
# Hermes reports semver from `hermes --version` (for example, `0.20.6`) even
# though its GitHub release cadence is calendar-based (`v2026.8.27`). 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
6 changes: 3 additions & 3 deletions agents/hermes/patch-cron-execution-runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# SPDX-License-Identifier: Apache-2.0
"""Relocate the pinned Hermes cron execution ledger into writable runtime state.

Hermes v2026.7.20 / 0.19.0 creates ``cron/executions.db`` from the gateway.
Hermes v2026.8.27 / 0.20.6 creates ``cron/executions.db`` from the gateway.
When Shields up is active, NemoClaw correctly seals ``cron`` as the high-risk
location for cron job definitions, so a managed gateway restart cannot reopen
that database.
Expand All @@ -24,10 +24,10 @@
from pathlib import Path

OLD_EXECUTIONS_PATH = (
'EXECUTIONS_FILE = get_hermes_home().resolve() / "cron" / "executions.db"'
' path = EXECUTIONS_FILE or (get_hermes_home().resolve() / "cron" / "executions.db")'
)
NEW_EXECUTIONS_PATH = (
'EXECUTIONS_FILE = get_hermes_home().resolve() / "runtime" / "cron-executions.db"'
' path = EXECUTIONS_FILE or (get_hermes_home().resolve() / "runtime" / "cron-executions.db")'
)
EXECUTIONS_CONTEXT = "from hermes_constants import get_hermes_home"

Expand Down
11 changes: 2 additions & 9 deletions agents/hermes/patch-cron-restore-drain.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# SPDX-License-Identifier: Apache-2.0
"""Compose NemoClaw's rebuild drain with pinned Hermes operator drain control.

Hermes v2026.7.20 / 0.19.0 scopes its operator marker to one container epoch.
Hermes v2026.8.27 / 0.20.6 scopes its operator marker to one container epoch.
That is correct for operator lifecycle actions, but a NemoClaw rebuild marker
must survive replacement gateway and container restarts until restored scripts
and cron jobs are revalidated. Keep those two owners on separate paths and OR
Expand All @@ -22,18 +22,11 @@
import argparse
from pathlib import Path

OLD_MARKER_ANCHOR = '''_DRAIN_REQUEST_FILENAME = ".drain_request.json"


@functools.lru_cache(maxsize=1)
'''
OLD_MARKER_ANCHOR = '_DRAIN_REQUEST_FILENAME = ".drain_request.json"'
NEW_MARKER_ANCHOR = '''_DRAIN_REQUEST_FILENAME = ".drain_request.json"
_NEMOCLAW_CRON_RESTORE_DRAIN_PATH = Path(
"/sandbox/.nemoclaw/hermes-cron-restore-drain.json"
)


@functools.lru_cache(maxsize=1)
'''

OLD_OPERATOR_HEADER = '''def drain_requested(*, home: Optional[Path] = None) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion agents/hermes/patch-discord-recovery-permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# SPDX-License-Identifier: Apache-2.0
"""Patch the pinned Hermes Discord recovery ledger to remain group-writable.

Hermes v2026.7.20 / 0.19.0 creates the Discord recovery database as the
Hermes v2026.8.27 / 0.20.6 creates the Discord recovery database as the
``gateway`` user and then forces mode 0600. NemoClaw snapshot and restore
commands run as the ``sandbox`` user, so that mode prevents online backup and
prevents the gateway from reopening a sandbox-restored database.
Expand Down
2 changes: 1 addition & 1 deletion agents/hermes/patch-gateway-runtime-metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# SPDX-License-Identifier: Apache-2.0
"""Relocate pinned Hermes gateway metadata below its writable runtime directory.

Hermes v0.19.0 stores ``gateway.pid``, ``gateway.lock``, and
Hermes v0.20.6 stores ``gateway.pid``, ``gateway.lock``, and
``gateway_state.json`` directly below ``HERMES_HOME``. NemoClaw shields-up
correctly makes that config root root-owned and non-writable, so NemoClaw's
managed stop/start recovery cannot remove the old PID file or atomically
Expand Down
Loading
Loading