Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
fd5a8a2
fix(hermes): unify WhatsApp session state
sandl99 Aug 4, 2026
d7d2c17
test(hermes): keep session patch cases linear
sandl99 Aug 4, 2026
c3e80d3
refactor(messaging): use runtime manifest for Hermes WhatsApp
sandl99 Aug 4, 2026
2cc4096
fix(build): resolve Hermes runtime preload types
sandl99 Aug 4, 2026
57f4e94
fix(messaging): address runtime preload review
sandl99 Aug 4, 2026
8b2aa86
test(messaging): keep secret scan setup linear
sandl99 Aug 4, 2026
af461ff
test(messaging): always clean secret scan fixtures
sandl99 Aug 4, 2026
02e10e0
test(hermes): repair runtime preload fixtures
sandl99 Aug 4, 2026
ea3a556
Merge branch 'main' into fix/hermes-whatsapp-session-path
apurvvkumaria Aug 4, 2026
a5c3e2b
Merge branch 'main' into fix/hermes-whatsapp-session-path
senthilr-nv Aug 5, 2026
350724e
Merge branch 'main' into fix/hermes-whatsapp-session-path
senthilr-nv Aug 5, 2026
2216e9c
fix(messaging): scope Hermes session preload
apurvvkumaria Aug 5, 2026
5ecaa0c
Merge branch 'main' into fix/hermes-whatsapp-session-path
senthilr-nv Aug 5, 2026
b9f1345
fix(messaging): apply Hermes runtime aliases
apurvvkumaria Aug 5, 2026
8269976
fix(messaging): reject missing Hermes session values
senthilr-nv Aug 5, 2026
1f3bb77
merge(main): refresh PR #8229
senthilr-nv Aug 5, 2026
3e1a70a
merge(main): refresh PR #8229
senthilr-nv Aug 5, 2026
280951d
Merge branch 'main' into fix/hermes-whatsapp-session-path
sandl99 Aug 5, 2026
e994ddc
fix(messaging): reject unsafe runtime preload paths
apurvvkumaria Aug 5, 2026
9ee45be
Merge remote-tracking branch 'origin/main' into codex/pr8229-traversa…
apurvvkumaria Aug 5, 2026
8ea4a4d
test(messaging): cover Hermes session preload wiring
apurvvkumaria Aug 5, 2026
344fbc0
merge(messaging): refresh PR 8229 with main
apurvvkumaria Aug 5, 2026
4fdb936
test(messaging): keep runtime setup harness linear
apurvvkumaria Aug 5, 2026
0634898
merge: refresh PR #8229 with current main
apurvvkumaria Aug 5, 2026
24c4688
fix(messaging): preserve Hermes WhatsApp config integrity
sandl99 Aug 5, 2026
0a3f3fe
test(messaging): keep command route fixture linear
sandl99 Aug 5, 2026
bf7a892
fix(whatsapp): keep Hermes CLI pairing unchanged
sandl99 Aug 5, 2026
2e84a3e
docs(whatsapp): describe dashboard session storage
sandl99 Aug 5, 2026
d309d0e
Merge remote-tracking branch 'origin/main' into fix/hermes-whatsapp-s…
sandl99 Aug 5, 2026
1ff42e8
fix(whatsapp): scope session patch to dashboard
sandl99 Aug 5, 2026
a742997
chore(whatsapp): revert broad session implementation
sandl99 Aug 5, 2026
a7a7f3e
fix(hermes): align dashboard WhatsApp session path
sandl99 Aug 5, 2026
545c490
fix(hermes): pin WhatsApp session base image
sandl99 Aug 5, 2026
1daba19
Merge remote-tracking branch 'origin/main' into fix/hermes-whatsapp-s…
sandl99 Aug 5, 2026
9dcef91
docs(security): record Hermes WhatsApp base image
sandl99 Aug 5, 2026
49fce0a
merge(main): refresh PR #8229
apurvvkumaria Aug 5, 2026
f9c4e82
merge: resolve conflicts with main
github-actions[bot] Aug 6, 2026
4963d1c
Merge branch 'main' into fix/hermes-whatsapp-session-path
hunglp6d Aug 6, 2026
589c492
Merge branch 'main' into fix/hermes-whatsapp-session-path
hunglp6d Aug 6, 2026
5401ca9
Merge branch 'main' into fix/hermes-whatsapp-session-path
sandl99 Aug 6, 2026
ea18717
Merge remote-tracking branch 'origin/main' into fix/hermes-whatsapp-s…
apurvvkumaria Aug 6, 2026
3c1f7fd
docs(whatsapp): remove obsolete Hermes session workaround
apurvvkumaria Aug 6, 2026
6d8bb3d
docs(whatsapp): document legacy Hermes re-pairing
apurvvkumaria Aug 6, 2026
8197641
Merge branch 'main' into fix/hermes-whatsapp-session-path
hunglp6d Aug 6, 2026
868a2cc
Merge branch 'main' into fix/hermes-whatsapp-session-path
hunglp6d Aug 7, 2026
e879cb3
Merge branch 'main' into fix/hermes-whatsapp-session-path
sandl99 Aug 7, 2026
634d631
Merge branch 'main' into fix/hermes-whatsapp-session-path
sandl99 Aug 7, 2026
96ee9e0
test(docs): align Hermes WhatsApp rendering contract
sandl99 Aug 7, 2026
9060fc6
fix(messaging): update Hermes session recovery hint
apurvvkumaria Aug 7, 2026
e9b1ead
docs(messaging): align Hermes session recovery guidance
apurvvkumaria Aug 7, 2026
cbdf76e
Merge branch 'main' into fix/hermes-whatsapp-session-path
cv Aug 7, 2026
f39d67e
fix(hermes): address WhatsApp review findings
sandl99 Aug 7, 2026
e1dd866
Merge branch 'main' into fix/hermes-whatsapp-session-path
cv Aug 7, 2026
c3f631a
Merge branch 'main' into fix/hermes-whatsapp-session-path
hunglp6d Aug 7, 2026
fc60ee0
Merge branch 'main' into fix/hermes-whatsapp-session-path
hunglp6d Aug 7, 2026
3d3ce74
Merge branch 'main' into fix/hermes-whatsapp-session-path
hunglp6d Aug 7, 2026
b69f733
Merge branch 'main' into fix/hermes-whatsapp-session-path
hunglp6d Aug 8, 2026
110c16f
Merge branch 'main' into fix/hermes-whatsapp-session-path
hunglp6d Aug 8, 2026
2ced3af
Merge branch 'main' into fix/hermes-whatsapp-session-path
hunglp6d Aug 9, 2026
d642cac
Merge branch 'main' into fix/hermes-whatsapp-session-path
hunglp6d Aug 9, 2026
ef14268
Merge branch 'main' into fix/hermes-whatsapp-session-path
hunglp6d Aug 9, 2026
33a5fa1
Merge branch 'main' into fix/hermes-whatsapp-session-path
hunglp6d Aug 10, 2026
6908108
Merge branch 'main' into fix/hermes-whatsapp-session-path
hunglp6d Aug 10, 2026
f4038cd
Merge branch 'main' into fix/hermes-whatsapp-session-path
cv Aug 10, 2026
eda36e8
Merge branch 'main' into fix/hermes-whatsapp-session-path
apurvvkumaria Aug 10, 2026
b84247e
docs(messaging): name the supported cleanup for legacy WhatsApp sessions
hunglp6d Aug 11, 2026
d9a381d
fix(messaging): send the WhatsApp session split through channel removal
hunglp6d Aug 11, 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
15 changes: 15 additions & 0 deletions agents/hermes/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ RUN ./install-reviewed-runtime.sh \
RUN chown -R root:root /opt/mcp-tool-discovery-runtime \
&& chmod -R a=rX /opt/mcp-tool-discovery-runtime

FROM mcp-tool-discovery-runtime AS runtime-preload-builder
WORKDIR /opt/nemoclaw-root
COPY tsconfig.runtime-preloads.json /opt/nemoclaw-root/
COPY src/lib/messaging/channels/ /opt/nemoclaw-root/src/lib/messaging/channels/
RUN ln -s /opt/mcp-tool-discovery-runtime/node_modules /opt/nemoclaw-root/node_modules \
&& /opt/mcp-tool-discovery-runtime/node_modules/.bin/tsc -p tsconfig.runtime-preloads.json

FROM mcp-tool-discovery-runtime AS managed-startup-runtime-builder
WORKDIR /opt/nemoclaw-managed-startup-build
COPY src/lib/core/json-types.ts src/lib/core/ports.ts ./src/lib/core/
Expand Down Expand Up @@ -136,6 +143,7 @@ COPY agents/hermes/mcp-config-transaction.py /usr/local/lib/nemoclaw/hermes-mcp-
COPY src/lib/actions/sandbox/openshell-child-visible-credentials.v0.0.85.json /usr/local/lib/nemoclaw/openshell-child-visible-credentials.v0.0.85.json
COPY scripts/state-dir-guard.py /usr/local/lib/nemoclaw/state-dir-guard.py
COPY nemoclaw-blueprint/scripts/*.js /usr/local/lib/nemoclaw/preloads/
COPY --from=runtime-preload-builder /opt/nemoclaw-root/dist/lib/messaging/channels/ /usr/local/lib/nemoclaw/preloads-compiled-channels/

FROM scratch AS hermes-wrapper-payload

Expand Down Expand Up @@ -317,6 +325,12 @@ RUN chmod 755 /usr/local/bin/nemoclaw-start /usr/local/bin/nemoclaw-managed-star
&& chmod 444 /usr/local/lib/nemoclaw/entrypoint-env-wrapper.sh /usr/local/lib/nemoclaw/gateway-supervisor.sh /usr/local/lib/nemoclaw/build-hermes-mcp-digest.py \
&& chmod 444 /usr/local/lib/nemoclaw/patch-hermes-langfuse-credentials.mts \
&& chmod 444 /usr/local/lib/nemoclaw/openshell-child-visible-credentials.v0.0.85.json \
&& test -d /usr/local/lib/nemoclaw/preloads-compiled-channels \
&& runtime_preload_found="$(find /usr/local/lib/nemoclaw/preloads-compiled-channels -path '*/runtime/*.js' -type f -print -quit)" \
&& test -n "$runtime_preload_found" \
&& find /usr/local/lib/nemoclaw/preloads-compiled-channels -path '*/runtime/*.js' -type f \
-exec sh -c 'for file do target="/usr/local/lib/nemoclaw/preloads/$(basename "$file")"; [ ! -e "$target" ] || { echo "ERROR: duplicate messaging runtime preload basename: $target" >&2; exit 1; }; cp "$file" "$target"; done' sh {} + \
&& rm -rf /usr/local/lib/nemoclaw/preloads-compiled-channels \
&& if [ -d /usr/local/lib/nemoclaw/preloads ]; then \
chown -R 0:0 /usr/local/lib/nemoclaw/preloads \
&& find /usr/local/lib/nemoclaw/preloads -type f -exec chmod 444 {} + \
Expand Down Expand Up @@ -1082,6 +1096,7 @@ RUN check_metadata() { \
&& check_metadata /usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh 'root:root 444' \
&& check_metadata /usr/local/bin/nemoclaw-gateway-control 'root:root 700' \
&& check_metadata /usr/local/lib/nemoclaw/preloads/sandbox-safety-net.js 'root:root 444' \
&& check_metadata /usr/local/lib/nemoclaw/preloads/whatsapp-hermes-session.js 'root:root 444' \
&& check_metadata /usr/local/lib/nemoclaw/hermes-wrapper.py 'root:root 755' \
&& check_metadata /usr/local/lib/nemoclaw/validate-hermes-cli-adapter.py 'root:root 755' \
&& check_metadata /usr/local/share/nemoclaw/hermes-cli-adapter-v1.json 'root:root 444' \
Expand Down
32 changes: 28 additions & 4 deletions agents/hermes/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,14 @@ verify_hermes_config_integrity() {

# configure_messaging_channels is provided by sandbox-init.sh (shared).

prepare_hermes_messaging_runtime() {
write_messaging_runtime_setup_plan || return 1
apply_messaging_runtime_env_aliases || return 1
install_messaging_runtime_preloads || return 1
verify_messaging_runtime_secret_scans || return 1
write_runtime_shell_env
}

print_dashboard_urls() {
local api_url dashboard_url
api_url="http://127.0.0.1:${PUBLIC_PORT}/v1"
Expand Down Expand Up @@ -1770,6 +1778,12 @@ if [ -f /opt/hermes/ui-tui/dist/entry.js ]; then
export HERMES_TUI_DIR="/opt/hermes/ui-tui"
fi
TUIENVEOF
if type emit_messaging_connect_runtime_preload_exports >/dev/null 2>&1; then
emit_messaging_connect_runtime_preload_exports
fi
if type emit_messaging_connect_runtime_command_router >/dev/null 2>&1; then
emit_messaging_connect_runtime_command_router
fi
for _ca_env_name in SSL_CERT_FILE CURL_CA_BUNDLE REQUESTS_CA_BUNDLE GIT_SSL_CAINFO NODE_EXTRA_CA_CERTS; do
_ca_env_value="${!_ca_env_name:-}"
if [ -n "$_ca_env_value" ]; then
Expand All @@ -1779,7 +1793,7 @@ TUIENVEOF
cat <<'GUARDENVEOF'
# nemoclaw-configure-guard begin
hermes() {
case "$1" in
case "${1:-}" in
setup|doctor)
echo "Error: 'hermes $1' cannot modify config inside the sandbox." >&2
echo "NemoClaw manages sandbox config from the host for integrity checks." >&2
Expand All @@ -1789,6 +1803,14 @@ hermes() {
return 1
;;
esac
if type _nemoclaw_messaging_runtime_command_module >/dev/null 2>&1; then
local _nemoclaw_runtime_module
_nemoclaw_runtime_module="$(_nemoclaw_messaging_runtime_command_module hermes "$@")" || return $?
if [ -n "$_nemoclaw_runtime_module" ]; then
/usr/local/bin/node "$_nemoclaw_runtime_module"
return $?
fi
fi
command hermes "$@"
}
# nemoclaw-configure-guard end
Expand Down Expand Up @@ -2161,7 +2183,7 @@ validate_running_hermes_boundary() {
validate_hermes_runtime_env_secret_boundary || return 1
HERMES_RESTART_FAILURE_CODE=preload-missing
# shellcheck disable=SC2119
validate_tmp_permissions || return 1
validate_messaging_runtime_tmp_permissions || return 1
}

prepare_hermes_gateway_restart() {
Expand Down Expand Up @@ -2875,6 +2897,7 @@ prepare_hermes_nonroot_runtime() {
refresh_hermes_runtime_config_hashes compat || return 1
inspect_hermes_mcp_integrity "${HERMES_DIR}/.config-hash" || return 1
configure_messaging_channels || return 1
prepare_hermes_messaging_runtime || return 1
prepare_tirith_marker_retry || return 1
}

Expand All @@ -2887,6 +2910,7 @@ prepare_hermes_root_runtime() {
validate_hermes_runtime_env_secret_boundary || return 1
refresh_hermes_provider_placeholders both || return 1
configure_messaging_channels || return 1
prepare_hermes_messaging_runtime || return 1
prepare_tirith_marker_retry || return 1
}

Expand Down Expand Up @@ -3241,7 +3265,7 @@ if [ "$(id -u)" -ne 0 ]; then

# Defence-in-depth: verify /tmp file permissions before launching services.
# shellcheck disable=SC2119
validate_tmp_permissions
validate_messaging_runtime_tmp_permissions

# Start Hermes gateway. Messaging egress goes directly through OpenShell.
umask 0007
Expand Down Expand Up @@ -3284,7 +3308,7 @@ prepare_restricted_log /tmp/gateway.log gateway:gateway 600

# Defence-in-depth: verify /tmp file permissions before launching services.
# shellcheck disable=SC2119
validate_tmp_permissions
validate_messaging_runtime_tmp_permissions

# Start Hermes gateway. Messaging egress goes directly through OpenShell.
launch_hermes_gateway
Expand Down
2 changes: 1 addition & 1 deletion ci/test-file-size-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"src/lib/onboard/preflight.test.ts": 1904,
"test/generate-openclaw-config.test.ts": 1941,
"test/install-preflight.test.ts": 3921,
"test/nemoclaw-start.test.ts": 4817,
"test/nemoclaw-start.test.ts": 4765,
"test/onboard-messaging.test.ts": 2043,
"test/onboard-selection.test.ts": 4769
}
Expand Down
5 changes: 5 additions & 0 deletions docs/manage-sandboxes/set-up-whatsapp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@ openclaw channels login --channel whatsapp
```
</AgentOnly>
<AgentOnly variant="hermes">
In the NemoClaw-managed connect shell, run:
```bash
hermes whatsapp
```
This command opens QR pairing directly at `/sandbox/.hermes/platforms/whatsapp/session`.
It reads the WhatsApp mode and sender allowlist from the NemoClaw-managed `/sandbox/.hermes/.env` file without modifying that file.
Pairing therefore does not cause configuration drift during later host-side `config set --restart` operations or gateway restarts.
This route applies only to the managed connect shell; direct Hermes executable invocation uses upstream behavior.
</AgentOnly>

<AgentOnly variant="openclaw">
Expand Down
17 changes: 14 additions & 3 deletions scripts/check-messaging-plan-image-boundary.mts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export function createMessagingBoundaryPlan(agent: unknown) {
],
runtimeSetup: {
nodePreloads: [OPENCLAW_RUNTIME_PRELOAD],
commandRoutes: [],
envAliases: [],
secretScans: [],
},
Expand Down Expand Up @@ -175,7 +176,7 @@ export function createMessagingBoundaryPlan(agent: unknown) {
value: { manager: "hermes-uv-pip", spec: HERMES_AIOHTTP_PACKAGE_SPEC },
},
],
runtimeSetup: { nodePreloads: [], envAliases: [], secretScans: [] },
runtimeSetup: { nodePreloads: [], commandRoutes: [], envAliases: [], secretScans: [] },
};
}

Expand Down Expand Up @@ -313,14 +314,15 @@ function assertReducedRuntimeArtifact(text: string, agent: MessagingBoundaryAgen
if (
!isObject(runtimeSetup) ||
!Array.isArray(runtimeSetup.nodePreloads) ||
!Array.isArray(runtimeSetup.commandRoutes) ||
!Array.isArray(runtimeSetup.envAliases) ||
!Array.isArray(runtimeSetup.secretScans)
) {
throw new Error("reduced runtime plan is missing the normalized runtimeSetup arrays");
}
assertAllowedKeys(
runtimeSetup,
["nodePreloads", "envAliases", "secretScans"],
["nodePreloads", "commandRoutes", "envAliases", "secretScans"],
"reduced runtime plan runtimeSetup",
);
assertRuntimeSetupEntryAllowlist(
Expand All @@ -336,6 +338,11 @@ function assertReducedRuntimeArtifact(text: string, agent: MessagingBoundaryAgen
],
"nodePreloads",
);
assertRuntimeSetupEntryAllowlist(
runtimeSetup.commandRoutes,
["channelId", "command", "args", "module", "source"],
"commandRoutes",
);
assertRuntimeSetupEntryAllowlist(
runtimeSetup.envAliases,
["channelId", "envKey", "match", "value", "message"],
Expand All @@ -346,7 +353,11 @@ function assertReducedRuntimeArtifact(text: string, agent: MessagingBoundaryAgen
["channelId", "path", "pattern", "message", "exitCode"],
"secretScans",
);
if (runtimeSetup.envAliases.length !== 0 || runtimeSetup.secretScans.length !== 0) {
if (
runtimeSetup.commandRoutes.length !== 0 ||
runtimeSetup.envAliases.length !== 0 ||
runtimeSetup.secretScans.length !== 0
) {
throw new Error("reduced runtime plan contains unexpected Teams runtime setup entries");
}
if (agent === "openclaw") {
Expand Down
Loading
Loading