Skip to content

refactor(messaging): move build setup to manifest hooks#4949

Draft
sandl99 wants to merge 8 commits into
mainfrom
feat/issue-4395-manifest-hooks
Draft

refactor(messaging): move build setup to manifest hooks#4949
sandl99 wants to merge 8 commits into
mainfrom
feat/issue-4395-manifest-hooks

Conversation

@sandl99
Copy link
Copy Markdown
Contributor

@sandl99 sandl99 commented Jun 8, 2026

Summary

Migrate messaging build setup from legacy Docker build scripts and ad hoc build args into manifest-driven hooks. OpenClaw and Hermes now consume the staged messaging plan for package installs, static render outputs, and WeChat post-agent-install behavior.

Related Issue

Fixes #4395

Changes

  • Add common manifest hook support for static outputs and package-install build steps.
  • Replace the OpenClaw messaging build script with scripts/run-openclaw-build-hooks.mts and wire Docker builds through NEMOCLAW_MESSAGING_PLAN_B64.
  • Move Hermes messaging render/env handling to manifest hook application and remove agents/hermes/config/messaging-config.ts.
  • Remove old messaging build/env Docker args and deleted legacy script tests.
  • Update onboarding/rebuild/policy-channel tests and helpers for manifest plan based messaging setup.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Verification

Targeted checks run: npm run build:cli, npm run typecheck:cli, npx vitest run test/onboard-messaging.test.ts, npx vitest run src/lib/messaging/hooks/common/token-paste.test.ts, git diff --check.

  • npx prek run --all-files passes
  • npm test passes
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: San Dang sdang@nvidia.com

Summary by CodeRabbit

  • Refactor
    • Consolidated messaging build arguments into a single configuration plan parameter.
    • Migrated messaging provisioning from legacy scripts to a hook-based system.
    • Simplified WeChat account initialization during image build via manifest hooks.
    • Streamlined plugin installation and messaging artifact generation.

@sandl99 sandl99 self-assigned this Jun 8, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 8, 2026

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b06c7ccb-3f91-462d-ba36-445fda55433a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR migrates NemoClaw's messaging configuration from legacy per-channel script-based logic to a unified base64-encoded manifest-driven plan system. It replaces multiple Docker build arguments with NEMOCLAW_MESSAGING_PLAN_B64, removes two Python scripts in favor of a TypeScript hook runner, refactors Hermes config generation to use manifest renders, and updates all messaging channel manifests to declare plugin installs and platform enablement through hooks and template-driven renders.

Changes

Messaging Plan System & Docker Build Integration

Layer / File(s) Summary
Messaging plan schema & Docker build args consolidation
Dockerfile, agents/hermes/Dockerfile, scripts/run-openclaw-build-hooks.mts, scripts/generate-openclaw-config.mts
Single ARG NEMOCLAW_MESSAGING_PLAN_B64 build arg replaces per-channel/platform legacy args. New TypeScript hook runner (run-openclaw-build-hooks.mts) reads and decodes the plan, installs OpenClaw plugins, and runs doctor. OpenClaw config generation parses the plan and applies agentRender JSON fragments and build-file steps.
Hermes config refactoring
agents/hermes/config/build-env.ts, agents/hermes/config/hermes-config.ts, agents/hermes/config/hermes-env.ts, agents/hermes/config/manifest-hooks.ts, agents/hermes/generate-config.ts
Remove explicit messaging types and env construction from build settings. Refactor buildHermesConfig to separate platform setup from toolset finalization. New buildHermesEnvLines handles API host/port and broker presets. New readHermesManifestHookPlan and applyHermesManifestHookRender read/apply the manifest plan for config/env updates.
Channel manifest plugin installs
src/lib/messaging/channels/discord/manifest.ts, src/lib/messaging/channels/slack/manifest.ts, src/lib/messaging/channels/telegram/manifest.ts, src/lib/messaging/channels/wechat/manifest.ts, src/lib/messaging/channels/whatsapp/manifest.ts
Discord, Slack, Telegram, WeChat, and WhatsApp manifests now declare agent-install hooks to install and pin OpenClaw/npm packages via common.staticOutputs. Add conditional JSON fragments (e.g., guilds when discord.hasGuilds is true) and Hermes platform enablement renders.
Manifest compiler & template rendering
src/lib/messaging/compiler/engines/agent-render-engine.ts, src/lib/messaging/compiler/engines/build-step-engine.ts, src/lib/messaging/compiler/engines/template.ts, src/lib/messaging/compiler/manifest-compiler.ts, src/lib/messaging/manifest/types.ts
Refactor planAgentRender and planBuildSteps to be async and hook-driven with template context. Replace credential templates with render templates supporting {{ ... }} references, proxy URL generation, and Discord/Slack/Telegram/WeChat/allowedIds templating. Add static-outputs hook registration. Extend manifest types with when conditions, agent-install/render phases, and hook metadata on plans.
Onboarding & Dockerfile patching
src/lib/onboard.ts, src/lib/onboard/dockerfile-patch.ts, src/lib/onboard/messaging-config.ts
Simplify sandbox creation by removing reusable messaging provider/channel derivation. Patch Dockerfile with serialized NEMOCLAW_MESSAGING_PLAN_B64 using MessagingSetupApplier.readPlanFromEnv() instead of individual messaging config args.
Sandbox rebuild & streaming support
src/lib/actions/sandbox/rebuild.ts, src/lib/adapters/openshell/client.ts, src/lib/adapters/openshell/runtime.ts
Add reapplyMessagingManifestAfterOpenClawDoctor() to reapply manifest hooks after doctor rebuild. Support optional stdin input?: string in openshell command options for passing data to executed commands.
Test infrastructure
test/messaging-plan-test-helper.ts
New helper exports withLegacyMessagingPlanEnv() to encode/normalize legacy env into NEMOCLAW_MESSAGING_PLAN_B64 for test compatibility. Supports legacy base64 channel/config decoding and Discord guild/mention-mode handling.
Test suite updates
test/generate-hermes-config.test.ts, test/generate-openclaw-config.test.ts, test/onboard-messaging.test.ts, test/onboard.test.ts, test/run-openclaw-build-hooks.test.ts, test/sandbox-build-context.test.ts, test/sandbox-provisioning.test.ts
Update tests to use withLegacyMessagingPlanEnv() for environment setup. Assert platform enablement and toolsets in Hermes config. Update OpenClaw WeChat tests to use canonical sandbox install path. Replace Python script tests with TypeScript hook runner tests.
Documentation & references
src/ext/wechat/qr.ts, src/lib/actions/sandbox/policy-channel.ts, agents/hermes/policy-additions.yaml, scripts/lib/sandbox-init.sh, test/e2e/test-messaging-providers.sh, test/e2e/docs/parity-inventory.generated.json
Update comments and documentation to reference wechat.seedOpenClawAccount manifest post-agent-install hook instead of scripts/seed-wechat-accounts.py. Update policy and test documentation to reflect manifest render/L7 proxy token resolution.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • NVIDIA/NemoClaw#4050: Introduces the manifest-style messaging architecture (ChannelManifests/ManifestCompiler/MessagingSetupApplier) that this PR consumes for base64-encoded plan application.
  • NVIDIA/NemoClaw#4536: Updates sandbox rebuild flow to reapply messaging manifest hooks instead of legacy WeChat seeding, directly aligning with rebuild changes in this PR.
  • NVIDIA/NemoClaw#4714: Both modify hermes-config.ts platform wiring; main PR refactors platform setup while retrieved PR adds Slack platform enabling.

Suggested labels

refactor, enhancement: messaging, area: messaging, v0.0.61

Suggested reviewers

  • ericksoa
  • cv

🐰 A plan so complete, it takes mere minutes,
From legacy scripts to manifests that spin it,
Discord and Slack now hooks do install,
WeChat seeds flow—no seed script at all! 🎯

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-4395-manifest-hooks

Comment thread agents/hermes/config/manifest-hooks.ts Fixed
Comment thread scripts/generate-openclaw-config.mts Fixed
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 8, 2026

E2E Advisor Recommendation

Required E2E: messaging-providers-e2e, cloud-onboard-e2e, hermes-e2e, hermes-discord-e2e, hermes-slack-e2e, network-policy-e2e, rebuild-openclaw-e2e, rebuild-hermes-e2e, openclaw-discord-pairing-e2e, openclaw-slack-pairing-e2e
Optional E2E: inference-routing-e2e, sandbox-operations-e2e, rebuild-hermes-stale-base-e2e

Dispatch hint: messaging-providers-e2e,cloud-onboard-e2e,hermes-e2e,hermes-discord-e2e,hermes-slack-e2e,network-policy-e2e,rebuild-openclaw-e2e,rebuild-hermes-e2e,openclaw-discord-pairing-e2e,openclaw-slack-pairing-e2e

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • messaging-providers-e2e (high): Primary required coverage for the shared messaging build-plan migration: verifies Telegram, Discord, Slack, WeChat, and WhatsApp provider creation, placeholder configuration, no-secret leakage, OpenClaw channel/plugin activation, network reachability, and L7 credential rewrite.
  • cloud-onboard-e2e (medium): Validates the OpenClaw install/onboard path after Dockerfile, openclaw.json generation, Dockerfile patching, onboard messaging config, sandbox build-context, and startup changes.
  • hermes-e2e (medium): Validates the base Hermes install/onboard/runtime path after Hermes Dockerfile and config-generation changes, independent of the Discord/Slack-specific messaging tests.
  • hermes-discord-e2e (medium): Hermes Discord config moved from Hermes-specific messaging-config.ts to shared manifest render hooks; this E2E verifies Hermes Discord schema, policy, placeholder/token isolation, and gateway rewrite behavior.
  • hermes-slack-e2e (medium): Hermes Slack now depends on shared messaging manifests/applier and Hermes policy additions; this validates Slack providers, placeholders, policy, and credential rewrite for the Hermes agent.
  • network-policy-e2e (medium): Policy-channel and Hermes policy assets changed in a way that can affect enforced egress and credential rewrite boundaries; run restricted-policy E2E to catch policy regressions.
  • rebuild-openclaw-e2e (high): OpenClaw image build phases and rebuild logic changed; this verifies rebuild preserves state, policies, gateway token handling, and post-rebuild runtime health.
  • rebuild-hermes-e2e (high): Hermes image build phases and rebuild behavior changed; this verifies Hermes state survives rebuild and that the updated image/config path boots correctly after upgrade.
  • openclaw-discord-pairing-e2e (medium): Discord manifest/template and policy-channel changes can affect no-allowlist pairing flows; this verifies Discord Gateway placeholder rewrite and pairing approval against shared OpenClaw state.
  • openclaw-slack-pairing-e2e (medium): Slack manifest/template and policy-channel changes can affect Socket Mode and pairing flows; this verifies xapp placeholder rewrite, pending request creation, and approval persistence.

Optional E2E

  • inference-routing-e2e (medium): Useful adjacent confidence because OpenClaw config generation and sandbox startup changed, but the diff is primarily messaging/config-build rather than inference routing.
  • sandbox-operations-e2e (medium): Optional coverage for touched OpenShell adapter/runtime code and sandbox lifecycle command behavior beyond rebuild-specific coverage.
  • rebuild-hermes-stale-base-e2e (high): Extra Hermes confidence for stale base image rebuilds after changing the Hermes Dockerfile build phases; not merge-blocking if rebuild-hermes-e2e passes.

New E2E recommendations

  • Hermes multi-channel messaging parity (high): Existing Hermes messaging E2E covers Discord and Slack, but this PR moves all channel rendering to shared manifests. There is no evident Hermes E2E for Telegram, WeChat, or WhatsApp-style manifest render outputs and tokenless/QR-only behavior.
    • Suggested test: Add a Hermes messaging-providers E2E matrix that validates shared build-plan/applier outputs for Telegram, WeChat, and WhatsApp in Hermes images with no secret leakage.
  • shared messaging build-plan image phase coverage (medium): The new build applier has agent-install and post-agent-install phases for both OpenClaw and Hermes. Existing E2E validates user-facing behavior, but a focused image-phase assertion would catch missing files, permissions, and hook ordering before runtime.
    • Suggested test: Add an E2E image-build smoke that builds OpenClaw and Hermes with a synthetic all-channel NEMOCLAW_MESSAGING_PLAN_B64 and asserts rendered files, permissions, and hook outputs inside the image.

Dispatch hint

  • Workflow: .github/workflows/nightly-e2e.yaml
  • jobs input: messaging-providers-e2e,cloud-onboard-e2e,hermes-e2e,hermes-discord-e2e,hermes-slack-e2e,network-policy-e2e,rebuild-openclaw-e2e,rebuild-hermes-e2e,openclaw-discord-pairing-e2e,openclaw-slack-pairing-e2e

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 8, 2026

E2E Scenario Advisor Recommendation

Required scenario E2E: ubuntu-repo-cloud-openclaw, ubuntu-repo-cloud-hermes, ubuntu-repo-cloud-openclaw-telegram, ubuntu-repo-cloud-openclaw-discord, ubuntu-repo-cloud-openclaw-slack, ubuntu-repo-cloud-hermes-discord, ubuntu-repo-cloud-hermes-slack, ubuntu-repo-cloud-openclaw-token-rotation
Optional scenario E2E: wsl-repo-cloud-openclaw, macos-repo-cloud-openclaw

Dispatch required scenario E2E:

  • gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw
  • gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-hermes
  • gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw-telegram
  • gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw-discord
  • gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw-slack
  • gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-hermes-discord
  • gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-hermes-slack
  • gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw-token-rotation

Workflow run

Full scenario advisor summary

E2E Scenario Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required scenario E2E

  • ubuntu-repo-cloud-openclaw: OpenClaw Dockerfile, openclaw config generation, sandbox init, build context, OpenShell adapter, onboarding, and messaging applier changes affect the baseline repo-current OpenClaw sandbox build and boot path even with no messaging channel enabled.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw
  • ubuntu-repo-cloud-hermes: Hermes Dockerfile and config generation were refactored to use the shared messaging applier/build-plan path; the baseline Hermes scenario validates image build, config.yaml/.env generation, sandbox boot, and Hermes-specific health without channel-specific setup.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-hermes
  • ubuntu-repo-cloud-openclaw-telegram: Telegram manifest, template resolver, workflow planning, onboarding messaging config, and OpenClaw messaging render/install hooks changed; this is the dispatchable scenario that exercises OpenClaw Telegram messaging.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw-telegram
  • ubuntu-repo-cloud-openclaw-discord: Discord manifest/template resolver and OpenClaw messaging plugin installation/rendering changed; this scenario exercises the OpenClaw Discord messaging suite and gateway path.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw-discord
  • ubuntu-repo-cloud-openclaw-slack: Slack manifest/template resolver and OpenClaw messaging build-plan rendering changed; this scenario exercises Slack placeholder configuration, provider state, and bridge reachability for OpenClaw.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw-slack
  • ubuntu-repo-cloud-hermes-discord: Hermes messaging configuration moved out of Hermes-specific config code into shared manifest render hooks; this scenario exercises Discord messaging on the Hermes agent path.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-hermes-discord
  • ubuntu-repo-cloud-hermes-slack: Hermes Slack setup now depends on the shared messaging build applier and channel manifests; this scenario exercises Slack messaging on the Hermes agent path.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-hermes-slack
  • ubuntu-repo-cloud-openclaw-token-rotation: Messaging credential/onboarding and hook-rendering changes can affect provider credential isolation and token refresh behavior; this scenario is the targeted token-rotation coverage in the scenario catalog.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw-token-rotation

Optional scenario E2E

  • wsl-repo-cloud-openclaw: Shared sandbox init, Dockerfile/build-context, and OpenShell adapter changes may have platform-specific effects under WSL, but the primary affected surfaces are already covered on Ubuntu; WSL uses a special runner and is optional unless platform parity is desired.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=wsl-repo-cloud-openclaw
  • macos-repo-cloud-openclaw: The PR touches shared onboarding and sandbox build code that can affect macOS CLI/platform smoke, but macOS is a special-runner adjacent scenario and does not exercise the primary messaging suites.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=macos-repo-cloud-openclaw

Relevant changed files

  • Dockerfile
  • agents/hermes/Dockerfile
  • agents/hermes/config/build-env.ts
  • agents/hermes/config/hermes-config.ts
  • agents/hermes/config/hermes-env.ts
  • agents/hermes/config/messaging-config.ts
  • agents/hermes/generate-config.ts
  • agents/hermes/policy-additions.yaml
  • scripts/generate-openclaw-config.mts
  • scripts/lib/sandbox-init.sh
  • scripts/openclaw-build-messaging-plugins.py
  • scripts/seed-wechat-accounts.py
  • src/ext/wechat/qr.ts
  • src/lib/actions/sandbox/policy-channel.ts
  • src/lib/actions/sandbox/rebuild.ts
  • src/lib/adapters/openshell/client.ts
  • src/lib/adapters/openshell/runtime.ts
  • src/lib/messaging/applier/agent-config.ts
  • src/lib/messaging/applier/build/messaging-build-applier.mts
  • src/lib/messaging/channels/discord/manifest.ts
  • src/lib/messaging/channels/discord/template-resolver.ts
  • src/lib/messaging/channels/index.ts
  • src/lib/messaging/channels/slack/manifest.ts
  • src/lib/messaging/channels/slack/template-resolver.ts
  • src/lib/messaging/channels/telegram/manifest.ts
  • src/lib/messaging/channels/telegram/template-resolver.ts
  • src/lib/messaging/channels/template-resolver-utils.ts
  • src/lib/messaging/channels/template-resolver.ts
  • src/lib/messaging/channels/wechat/manifest.ts
  • src/lib/messaging/channels/wechat/template-resolver.ts
  • src/lib/messaging/channels/whatsapp/manifest.ts
  • src/lib/messaging/channels/whatsapp/template-resolver.ts
  • src/lib/messaging/compiler/engines/agent-render-engine.ts
  • src/lib/messaging/compiler/engines/build-step-engine.ts
  • src/lib/messaging/compiler/engines/template.ts
  • src/lib/messaging/compiler/manifest-compiler.ts
  • src/lib/messaging/compiler/workflow-planner.ts
  • src/lib/messaging/hooks/common/index.ts
  • src/lib/messaging/hooks/common/static-outputs.ts
  • src/lib/messaging/hooks/hook-runner.test.ts
  • src/lib/messaging/manifest/types.ts
  • src/lib/onboard.ts
  • src/lib/onboard/dockerfile-patch.ts
  • src/lib/onboard/messaging-channel-setup.ts
  • src/lib/onboard/messaging-config.ts
  • src/lib/onboard/wechat-config.ts
  • src/lib/sandbox/build-context.ts

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 8, 2026

PR Review Advisor

Findings: 7 needs attention, 7 worth checking, 0 nice ideas
Since last review: 0 prior items resolved, 13 still apply, 0 new items found

Review findings

🛠️ Needs attention

  • Serialized messaging plans can request unexpected OpenClaw plugin installs (src/lib/messaging/applier/build/messaging-build-applier.mts:221): The build applier installs active serialized plan package-install outputs after checking only broad object shape, manager, spec string, pin type, and unresolved templates. The deleted Python installer hardcoded the external messaging package set, but this new trusted Docker build boundary consumes NEMOCLAW_MESSAGING_PLAN_B64 as authority. A stale or tampered plan can install an unexpected OpenClaw plugin or npm registry spec during image build.
    • Recommendation: Revalidate package-install steps at consumption time against an allowlist derived from the built-in manifests, including expected channel/spec pairs and the {{openclaw.version}} pinning contract. Add a negative test where a tampered serialized plan attempts to install an unexpected npm package or registry spec.
    • Evidence: collectOpenClawMessagingPluginInstallSpecs() iterates enabledBuildStepsForPhase(plan, "agent-install"), readOpenClawPackageInstall() checks shape/manager/spec/pin, resolveOpenClawPackageSpec() resolves templates, and installOpenClawMessagingPlugins() invokes `openclaw plugins install <spec> --pin`.
  • Plan-controlled credential bindings can inject doctor environment variables (src/lib/messaging/applier/build/messaging-build-applier.mts:242): openClawDoctorEnvOverrides() copies providerEnvKey and placeholder strings from the serialized plan into the child environment for `openclaw doctor --fix --non-interactive`. A tampered or stale plan can target process-control, package-manager, home-directory, or proxy variables such as PATH, HOME, NODE_OPTIONS, NPM_CONFIG_REGISTRY, HTTP_PROXY, or HTTPS_PROXY at a trusted build boundary.
    • Recommendation: Allowlist expected credential env keys per built-in manifest, or at minimum reject reserved process-control, package-manager, and proxy-related names before constructing the doctor environment. Add negative tests proving those names are rejected.
    • Evidence: The function assigns overrides[binding.providerEnvKey] = binding.placeholder for active plan bindings when both fields are strings; runOpenClawMessagingDoctor() merges those overrides into the child process environment.
  • Serialized plan rendering can mutate security-sensitive config trees (src/lib/messaging/applier/build/messaging-build-applier.mts:609): The local build applier protects against prototype-pollution keys and filesystem traversal, but it still treats serialized render and build-file entries as authority to write arbitrary safe-key JSON/YAML paths inside OpenClaw and Hermes config files. A tampered plan could alter gateway auth, device-auth, proxy, web fetch/search, plugin load paths, or other non-messaging security settings. This also weakens the acceptance requirement that render plans and snapshots contain placeholders only, because consumption does not enforce placeholder-only values in serialized render/env/build-file data.
    • Recommendation: Restrict render and build-file merges to messaging-owned subtrees and known file outputs, or revalidate serialized plan render/build-file entries against current built-in manifest declarations before applying them. Enforce placeholder-only secret-bearing outputs. Add negative tests for gateway.auth.token, gateway.controlUi.dangerouslyDisableDeviceAuth, proxy.enabled, tools.web.fetch, plugins.load.paths, and raw-secret-looking env/render values.
    • Evidence: setJsonPath() rejects empty/prototype-style path segments but otherwise writes any dot path. applyBuildFileOutputToLocalAgentRoot() constrains file location under the agent root, but build-file merge semantics are not constrained to manifest-owned config subtrees.
  • Rebuild reuses stored messaging plans without revalidating current manifest authority (src/lib/messaging/compiler/workflow-planner.ts:109): Rebuild planning reads the registry-stored serialized plan and toggles disabled-channel flags rather than recompiling or validating it against the current manifest registry. Stale or tampered package-install specs, provider env keys, render paths, and build-file outputs can persist across rebuilds.
    • Recommendation: On rebuild, either recompile the plan from current manifests using persisted state, or validate every stored plan entry against the current manifest registry before staging it for Docker build and post-doctor reapply.
    • Evidence: buildRebuildPlanFromSandboxEntry() calls readSandboxEntryPlan() and setPlanDisabledChannels() on the existing plan; readSandboxEntryPlan() checks only schemaVersion, sandboxName, and agent before clonePlan().
  • Post-doctor messaging manifest reapply failures are swallowed (src/lib/actions/sandbox/rebuild.ts:259): The rebuild path catches every failure from post-doctor messaging manifest reapply and only logs that the reapply was skipped. That reapply is intended to restore manifest-rendered OpenClaw channel config and WeChat seed files after `openclaw doctor --fix` may rewrite openclaw.json. Continuing as a successful rebuild can leave an active messaging sandbox with missing or stale generated config.
    • Recommendation: When an active OpenClaw messaging plan exists, fail the rebuild or mark it incomplete if MessagingSetupApplier.applyAgentConfigAtOpenShell() fails after doctor. Add a regression test where the applier fails after doctor and rebuild does not report full success.
    • Evidence: reapplyMessagingManifestAfterOpenClawDoctor() catches err, logs `Messaging manifest reapply failed`, prints `Messaging manifest config reapply skipped (...)`, and does not rethrow or downgrade the final rebuild result.
  • Agent rendering still has production channel-specific branches (src/lib/messaging/channels/template-resolver.ts:19): Issue Phase 5: migrate messaging agent rendering and build inputs to manifests #4395 requires agent rendering production code to be manifest-driven with no channel-specific branches after cutover. This PR removes legacy branches from the config generators, but production rendering still dispatches through channel-specific template resolvers for Discord, Telegram, WeChat, Slack, and WhatsApp.
    • Recommendation: Move channel-specific render derivation into manifest declarations or manifest-owned hook outputs, or update the acceptance scope with a verified rationale if these resolver files are intentionally the remaining source of channel semantics.
    • Evidence: createBuiltInRenderTemplateResolver() loops over BUILT_IN_TEMPLATE_REFERENCE_RESOLVERS including resolveTelegramTemplateReference, resolveDiscordTemplateReference, resolveWechatTemplateReference, resolveSlackTemplateReference, and resolveWhatsappTemplateReference.
  • Large-file hotspots grew in security-sensitive rebuild and compiler paths (src/lib/actions/sandbox/rebuild.ts:1): The PR adds more logic to existing large files in sandbox lifecycle and manifest compiler code, making the migration harder to audit and maintain in security-sensitive areas.
    • Recommendation: Extract the new rebuild reapply helpers and plan-validation/install-boundary logic into focused modules, or offset the growth by moving adjacent logic out of the hotspots before merging.
    • Evidence: Deterministic drift context flags src/lib/actions/sandbox/rebuild.ts +42 lines, src/lib/messaging/compiler/manifest-compiler.ts +46 lines, src/lib/messaging/compiler/manifest-compiler.test.ts +56 lines, and src/lib/messaging/compiler/workflow-planner.test.ts +26 lines as current large-file hotspot growth.

🔎 Worth checking

  • Source-of-truth review needed: Serialized messaging plan installer/config authority: The advisor marked localized patch analysis as missing.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: collectOpenClawMessagingPluginInstallSpecs(), openClawDoctorEnvOverrides(), setJsonPath(), applyBuildFileOutputToLocalAgentRoot(), and buildRebuildPlanFromSandboxEntry() consume stored plan data without manifest-derived revalidation.
  • Source-of-truth review needed: Post-doctor OpenClaw messaging manifest reapply during rebuild: The advisor marked localized patch analysis as needs_followup.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: reapplyMessagingManifestAfterOpenClawDoctor() reruns manifest apply after doctor but catches all errors and logs that reapply was skipped.
  • Source-of-truth review needed: Generated YAML parser/serializer in messaging build applier: The advisor marked localized patch analysis as needs_followup.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: parseGeneratedYamlObject(), parseGeneratedYamlMap(), parseGeneratedYamlArray(), parseGeneratedYamlScalar(), and serializeGeneratedYamlObject() implement a local YAML subset parser/serializer.
  • Generated YAML parser lacks a documented source-of-truth contract (src/lib/messaging/applier/build/messaging-build-applier.mts:701): The build applier adds a local YAML subset parser/serializer for generated Hermes YAML. The intended input source, accepted subset, invalid states, source-fix constraint, regression proof, and removal condition are not documented, and inspected coverage exercises only simple shapes.
    • Recommendation: Document that this parser only accepts NemoClaw-generated YAML, fail closed on unsupported shapes, add tests for rejected anchors, tags, multiline scalars, and ambiguous indentation, or use the same safe YAML library/path as the runtime applier if available.
    • Evidence: parseGeneratedYamlObject(), parseGeneratedYamlMap(), parseGeneratedYamlArray(), parseGeneratedYamlScalar(), and serializeGeneratedYamlObject() implement a custom YAML subset parser/serializer in the build applier.
  • Runtime validation is still needed for the Docker/Hermes/OpenClaw build-path migration (Dockerfile:559): This PR changes Dockerfile build ordering, OpenClaw plugin installation, Hermes config generation, WeChat seed-file creation, and rebuild reapplication. Unit and subprocess tests cover many fragments, but the highest-risk acceptance points depend on actual image-build and sandbox lifecycle behavior.
    • Recommendation: Add or identify targeted runtime/integration validation that exercises a production NEMOCLAW_MESSAGING_PLAN_B64 through the OpenClaw and Hermes build scripts and verifies generated config, plugin installs, seed files, and placeholder-only outputs before startup. Also cover the rebuild post-doctor reapply failure path.
    • Evidence: The Dockerfiles copy src/lib/messaging, run messaging-build-applier.mts --phase agent-install, and run --phase post-agent-install. Inspected tests run generators/appliers with temporary HOME directories, fake binaries, or fake OpenShell runners rather than a real final image/startup path.
  • High-value migration tests rely on file-wide type checking suppression (test/messaging-build-applier.test.ts:1): The core migration tests for the build applier use file-wide // @ts-nocheck, reducing confidence that test fixtures and assertions continue matching the production serialized plan and applier contracts during this large trusted-boundary refactor.
    • Recommendation: Remove file-wide suppression or narrow it to specific unavoidable call sites with an explanation. Prefer typed fixtures for serialized plans and applier subprocess results.
    • Evidence: test/messaging-build-applier.test.ts begins with // @ts-nocheck; test/generate-openclaw-config.test.ts also begins with // @ts-nocheck while exercising the OpenClaw render contract.
  • PR description references a build script that is not in the diff: The PR body says the OpenClaw messaging build script is replaced with `scripts/run-openclaw-build-hooks.mts`, but the diff uses `src/lib/messaging/applier/build/messaging-build-applier.mts` and grep did not find that script name in the repository.
    • Recommendation: Update the PR description and any release notes/docs to match the actual build entry point, or add the intended wrapper if that is the desired public/maintenance surface.
    • Evidence: PR body states `scripts/run-openclaw-build-hooks.mts`; changed Dockerfiles invoke `/src/lib/messaging/applier/build/messaging-build-applier.mts`.

🌱 Nice ideas

  • None.
Consider writing more tests for
  • **Runtime validation** — messaging-build-applier rejects a tampered package-install spec that is not the built-in manifest package for the active channel. Runtime/sandbox/infrastructure paths need behavioral validation because this PR changes Dockerfile build ordering, build-time OpenClaw plugin installation, Hermes config generation, WeChat seed-file creation, and rebuild post-doctor reapplication. Existing tests are mostly unit/subprocess tests with temp HOME directories, fake binaries, or helper-generated legacy plans.
  • **Runtime validation** — messaging-build-applier rejects plan credential bindings for reserved env vars such as NODE_OPTIONS, PATH, HOME, NPM_CONFIG_REGISTRY, HTTP_PROXY, and HTTPS_PROXY. Runtime/sandbox/infrastructure paths need behavioral validation because this PR changes Dockerfile build ordering, build-time OpenClaw plugin installation, Hermes config generation, WeChat seed-file creation, and rebuild post-doctor reapplication. Existing tests are mostly unit/subprocess tests with temp HOME directories, fake binaries, or helper-generated legacy plans.
  • **Runtime validation** — messaging-build-applier rejects serialized render paths outside manifest-owned messaging subtrees, including gateway.auth.token, gateway.controlUi.dangerouslyDisableDeviceAuth, proxy.enabled, tools.web.fetch, and plugins.load.paths. Runtime/sandbox/infrastructure paths need behavioral validation because this PR changes Dockerfile build ordering, build-time OpenClaw plugin installation, Hermes config generation, WeChat seed-file creation, and rebuild post-doctor reapplication. Existing tests are mostly unit/subprocess tests with temp HOME directories, fake binaries, or helper-generated legacy plans.
  • **Runtime validation** — rebuild planning recompiles or rejects a stored messaging plan whose package install, render, or build-file entries no longer match the current built-in manifest. Runtime/sandbox/infrastructure paths need behavioral validation because this PR changes Dockerfile build ordering, build-time OpenClaw plugin installation, Hermes config generation, WeChat seed-file creation, and rebuild post-doctor reapplication. Existing tests are mostly unit/subprocess tests with temp HOME directories, fake binaries, or helper-generated legacy plans.
  • **Runtime validation** — rebuild reports failure or incomplete status when post-doctor MessagingSetupApplier.applyAgentConfigAtOpenShell fails for an active OpenClaw messaging plan. Runtime/sandbox/infrastructure paths need behavioral validation because this PR changes Dockerfile build ordering, build-time OpenClaw plugin installation, Hermes config generation, WeChat seed-file creation, and rebuild post-doctor reapplication. Existing tests are mostly unit/subprocess tests with temp HOME directories, fake binaries, or helper-generated legacy plans.
  • **Runtime validation is still needed for the Docker/Hermes/OpenClaw build-path migration** — Add or identify targeted runtime/integration validation that exercises a production NEMOCLAW_MESSAGING_PLAN_B64 through the OpenClaw and Hermes build scripts and verifies generated config, plugin installs, seed files, and placeholder-only outputs before startup. Also cover the rebuild post-doctor reapply failure path.
  • **High-value migration tests rely on file-wide type checking suppression** — Remove file-wide suppression or narrow it to specific unavoidable call sites with an explanation. Prefer typed fixtures for serialized plans and applier subprocess results.
  • **Acceptance clause:** OpenClaw `openclaw.json` snapshots match current Telegram, Discord, Slack, WeChat, and WhatsApp behavior. — add test evidence or identify existing coverage. OpenClaw render declarations exist in channel manifests and tests cover many generated config behaviors, including placeholders, enabled flags, proxy behavior, Slack allowlists, and WeChat seed files. However, much coverage routes through withLegacyMessagingPlanEnv(), fake binaries, and temp HOME rather than validating a production persisted/rebuild plan through the real Docker/sandbox path.
Since last review details

Current findings:

  • Source-of-truth review needed: Serialized messaging plan installer/config authority: The advisor marked localized patch analysis as missing.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: collectOpenClawMessagingPluginInstallSpecs(), openClawDoctorEnvOverrides(), setJsonPath(), applyBuildFileOutputToLocalAgentRoot(), and buildRebuildPlanFromSandboxEntry() consume stored plan data without manifest-derived revalidation.
  • Source-of-truth review needed: Post-doctor OpenClaw messaging manifest reapply during rebuild: The advisor marked localized patch analysis as needs_followup.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: reapplyMessagingManifestAfterOpenClawDoctor() reruns manifest apply after doctor but catches all errors and logs that reapply was skipped.
  • Source-of-truth review needed: Generated YAML parser/serializer in messaging build applier: The advisor marked localized patch analysis as needs_followup.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: parseGeneratedYamlObject(), parseGeneratedYamlMap(), parseGeneratedYamlArray(), parseGeneratedYamlScalar(), and serializeGeneratedYamlObject() implement a local YAML subset parser/serializer.
  • Serialized messaging plans can request unexpected OpenClaw plugin installs (src/lib/messaging/applier/build/messaging-build-applier.mts:221): The build applier installs active serialized plan package-install outputs after checking only broad object shape, manager, spec string, pin type, and unresolved templates. The deleted Python installer hardcoded the external messaging package set, but this new trusted Docker build boundary consumes NEMOCLAW_MESSAGING_PLAN_B64 as authority. A stale or tampered plan can install an unexpected OpenClaw plugin or npm registry spec during image build.
    • Recommendation: Revalidate package-install steps at consumption time against an allowlist derived from the built-in manifests, including expected channel/spec pairs and the {{openclaw.version}} pinning contract. Add a negative test where a tampered serialized plan attempts to install an unexpected npm package or registry spec.
    • Evidence: collectOpenClawMessagingPluginInstallSpecs() iterates enabledBuildStepsForPhase(plan, "agent-install"), readOpenClawPackageInstall() checks shape/manager/spec/pin, resolveOpenClawPackageSpec() resolves templates, and installOpenClawMessagingPlugins() invokes `openclaw plugins install <spec> --pin`.
  • Plan-controlled credential bindings can inject doctor environment variables (src/lib/messaging/applier/build/messaging-build-applier.mts:242): openClawDoctorEnvOverrides() copies providerEnvKey and placeholder strings from the serialized plan into the child environment for `openclaw doctor --fix --non-interactive`. A tampered or stale plan can target process-control, package-manager, home-directory, or proxy variables such as PATH, HOME, NODE_OPTIONS, NPM_CONFIG_REGISTRY, HTTP_PROXY, or HTTPS_PROXY at a trusted build boundary.
    • Recommendation: Allowlist expected credential env keys per built-in manifest, or at minimum reject reserved process-control, package-manager, and proxy-related names before constructing the doctor environment. Add negative tests proving those names are rejected.
    • Evidence: The function assigns overrides[binding.providerEnvKey] = binding.placeholder for active plan bindings when both fields are strings; runOpenClawMessagingDoctor() merges those overrides into the child process environment.
  • Serialized plan rendering can mutate security-sensitive config trees (src/lib/messaging/applier/build/messaging-build-applier.mts:609): The local build applier protects against prototype-pollution keys and filesystem traversal, but it still treats serialized render and build-file entries as authority to write arbitrary safe-key JSON/YAML paths inside OpenClaw and Hermes config files. A tampered plan could alter gateway auth, device-auth, proxy, web fetch/search, plugin load paths, or other non-messaging security settings. This also weakens the acceptance requirement that render plans and snapshots contain placeholders only, because consumption does not enforce placeholder-only values in serialized render/env/build-file data.
    • Recommendation: Restrict render and build-file merges to messaging-owned subtrees and known file outputs, or revalidate serialized plan render/build-file entries against current built-in manifest declarations before applying them. Enforce placeholder-only secret-bearing outputs. Add negative tests for gateway.auth.token, gateway.controlUi.dangerouslyDisableDeviceAuth, proxy.enabled, tools.web.fetch, plugins.load.paths, and raw-secret-looking env/render values.
    • Evidence: setJsonPath() rejects empty/prototype-style path segments but otherwise writes any dot path. applyBuildFileOutputToLocalAgentRoot() constrains file location under the agent root, but build-file merge semantics are not constrained to manifest-owned config subtrees.
  • Rebuild reuses stored messaging plans without revalidating current manifest authority (src/lib/messaging/compiler/workflow-planner.ts:109): Rebuild planning reads the registry-stored serialized plan and toggles disabled-channel flags rather than recompiling or validating it against the current manifest registry. Stale or tampered package-install specs, provider env keys, render paths, and build-file outputs can persist across rebuilds.
    • Recommendation: On rebuild, either recompile the plan from current manifests using persisted state, or validate every stored plan entry against the current manifest registry before staging it for Docker build and post-doctor reapply.
    • Evidence: buildRebuildPlanFromSandboxEntry() calls readSandboxEntryPlan() and setPlanDisabledChannels() on the existing plan; readSandboxEntryPlan() checks only schemaVersion, sandboxName, and agent before clonePlan().
  • Post-doctor messaging manifest reapply failures are swallowed (src/lib/actions/sandbox/rebuild.ts:259): The rebuild path catches every failure from post-doctor messaging manifest reapply and only logs that the reapply was skipped. That reapply is intended to restore manifest-rendered OpenClaw channel config and WeChat seed files after `openclaw doctor --fix` may rewrite openclaw.json. Continuing as a successful rebuild can leave an active messaging sandbox with missing or stale generated config.
    • Recommendation: When an active OpenClaw messaging plan exists, fail the rebuild or mark it incomplete if MessagingSetupApplier.applyAgentConfigAtOpenShell() fails after doctor. Add a regression test where the applier fails after doctor and rebuild does not report full success.
    • Evidence: reapplyMessagingManifestAfterOpenClawDoctor() catches err, logs `Messaging manifest reapply failed`, prints `Messaging manifest config reapply skipped (...)`, and does not rethrow or downgrade the final rebuild result.
  • Agent rendering still has production channel-specific branches (src/lib/messaging/channels/template-resolver.ts:19): Issue Phase 5: migrate messaging agent rendering and build inputs to manifests #4395 requires agent rendering production code to be manifest-driven with no channel-specific branches after cutover. This PR removes legacy branches from the config generators, but production rendering still dispatches through channel-specific template resolvers for Discord, Telegram, WeChat, Slack, and WhatsApp.
    • Recommendation: Move channel-specific render derivation into manifest declarations or manifest-owned hook outputs, or update the acceptance scope with a verified rationale if these resolver files are intentionally the remaining source of channel semantics.
    • Evidence: createBuiltInRenderTemplateResolver() loops over BUILT_IN_TEMPLATE_REFERENCE_RESOLVERS including resolveTelegramTemplateReference, resolveDiscordTemplateReference, resolveWechatTemplateReference, resolveSlackTemplateReference, and resolveWhatsappTemplateReference.
  • Large-file hotspots grew in security-sensitive rebuild and compiler paths (src/lib/actions/sandbox/rebuild.ts:1): The PR adds more logic to existing large files in sandbox lifecycle and manifest compiler code, making the migration harder to audit and maintain in security-sensitive areas.
    • Recommendation: Extract the new rebuild reapply helpers and plan-validation/install-boundary logic into focused modules, or offset the growth by moving adjacent logic out of the hotspots before merging.
    • Evidence: Deterministic drift context flags src/lib/actions/sandbox/rebuild.ts +42 lines, src/lib/messaging/compiler/manifest-compiler.ts +46 lines, src/lib/messaging/compiler/manifest-compiler.test.ts +56 lines, and src/lib/messaging/compiler/workflow-planner.test.ts +26 lines as current large-file hotspot growth.
  • Generated YAML parser lacks a documented source-of-truth contract (src/lib/messaging/applier/build/messaging-build-applier.mts:701): The build applier adds a local YAML subset parser/serializer for generated Hermes YAML. The intended input source, accepted subset, invalid states, source-fix constraint, regression proof, and removal condition are not documented, and inspected coverage exercises only simple shapes.
    • Recommendation: Document that this parser only accepts NemoClaw-generated YAML, fail closed on unsupported shapes, add tests for rejected anchors, tags, multiline scalars, and ambiguous indentation, or use the same safe YAML library/path as the runtime applier if available.
    • Evidence: parseGeneratedYamlObject(), parseGeneratedYamlMap(), parseGeneratedYamlArray(), parseGeneratedYamlScalar(), and serializeGeneratedYamlObject() implement a custom YAML subset parser/serializer in the build applier.
  • Runtime validation is still needed for the Docker/Hermes/OpenClaw build-path migration (Dockerfile:559): This PR changes Dockerfile build ordering, OpenClaw plugin installation, Hermes config generation, WeChat seed-file creation, and rebuild reapplication. Unit and subprocess tests cover many fragments, but the highest-risk acceptance points depend on actual image-build and sandbox lifecycle behavior.
    • Recommendation: Add or identify targeted runtime/integration validation that exercises a production NEMOCLAW_MESSAGING_PLAN_B64 through the OpenClaw and Hermes build scripts and verifies generated config, plugin installs, seed files, and placeholder-only outputs before startup. Also cover the rebuild post-doctor reapply failure path.
    • Evidence: The Dockerfiles copy src/lib/messaging, run messaging-build-applier.mts --phase agent-install, and run --phase post-agent-install. Inspected tests run generators/appliers with temporary HOME directories, fake binaries, or fake OpenShell runners rather than a real final image/startup path.
  • High-value migration tests rely on file-wide type checking suppression (test/messaging-build-applier.test.ts:1): The core migration tests for the build applier use file-wide // @ts-nocheck, reducing confidence that test fixtures and assertions continue matching the production serialized plan and applier contracts during this large trusted-boundary refactor.
    • Recommendation: Remove file-wide suppression or narrow it to specific unavoidable call sites with an explanation. Prefer typed fixtures for serialized plans and applier subprocess results.
    • Evidence: test/messaging-build-applier.test.ts begins with // @ts-nocheck; test/generate-openclaw-config.test.ts also begins with // @ts-nocheck while exercising the OpenClaw render contract.
  • PR description references a build script that is not in the diff: The PR body says the OpenClaw messaging build script is replaced with `scripts/run-openclaw-build-hooks.mts`, but the diff uses `src/lib/messaging/applier/build/messaging-build-applier.mts` and grep did not find that script name in the repository.
    • Recommendation: Update the PR description and any release notes/docs to match the actual build entry point, or add the intended wrapper if that is the desired public/maintenance surface.
    • Evidence: PR body states `scripts/run-openclaw-build-hooks.mts`; changed Dockerfiles invoke `/src/lib/messaging/applier/build/messaging-build-applier.mts`.

Workflow run details

This is an automated advisory review. A human maintainer must make the final merge decision.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/lib/onboard.ts (1)

3383-3387: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve Telegram mention policy across channels stop/start.

Line 3383 now gates telegramConfig on activeMessagingChannels, which excludes temporarily disabled channels. A rebuild after channels stop telegram will therefore persist current.telegramConfig = null, so a later channels start telegram loses the prior TELEGRAM_REQUIRE_MENTION behavior unless the operator re-exports it. This should key off the configured channel set (enabledChannels / persisted messaging channels), not only the currently attached providers.

Suggested fix
-  if (activeMessagingChannels.includes("telegram")) {
+  const telegramConfigured =
+    enabledChannels?.includes("telegram") ?? activeMessagingChannels.includes("telegram");
+  if (telegramConfigured) {
     const telegramRequireMention = computeTelegramRequireMention();
     if (telegramRequireMention !== null) {
       telegramConfig.requireMention = telegramRequireMention;
     }
   }

Based on learnings, channel state is expected to persist durably across rebuilds, and the PR objective explicitly calls out rebuild-hydration parity for Telegram config.

🤖 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/onboard.ts` around lines 3383 - 3387, The current guard uses
activeMessagingChannels to decide whether to set telegramConfig.requireMention,
which drops the persisted Telegram mention policy when providers are temporarily
stopped; change the check to use the persisted/configured channel set (e.g.,
enabledChannels or enabledMessagingChannels / persisted messaging channels)
instead of activeMessagingChannels so computeTelegramRequireMention() runs
whenever Telegram is configured, not only when a provider is attached; update
the block around telegramConfig and computeTelegramRequireMention to key off
that persisted/enable set (ensure you reference telegramConfig,
computeTelegramRequireMention, and the enabledChannels/enabledMessagingChannels
variable) so TELEGRAM_REQUIRE_MENTION survives channels stop/start cycles.

Source: Learnings

test/generate-openclaw-config.test.ts (1)

1-4: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Update the stale legacy test-size budget entry to unblock CI.

The pipeline is failing because this file is now 2105 lines while the recorded legacy budget remains 2106. Please lower the corresponding budget entry to match the current line count so codebase-growth-guardrails passes.

🤖 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 `@test/generate-openclaw-config.test.ts` around lines 1 - 4, The recorded
legacy "test-size" budget entry is off by one (recorded 2106 vs actual 2105
lines) causing CI failures; update the legacy test-size budget value to 2105 to
match test/generate-openclaw-config.test.ts current line count so
codebase-growth-guardrails passes—locate the budget entry named "legacy
test-size" (or the test-size entry used by the codebase-growth-guardrails
config) and lower its numeric value from 2106 to 2105.

Source: Pipeline failures

src/lib/messaging/compiler/manifest-compiler.ts (1)

333-343: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

validValues is bypassed for config env values.

At Line 337, config-resolved values return before the validValues gate at Line 342. That lets out-of-range config values pass manifest constraints.

Suggested fix
 function readInputEnvValue(input: ChannelInputSpec): MessagingSerializableValue | undefined {
+  const normalize = (raw: string | undefined): string | undefined => {
+    const normalized = raw?.replace(/\r/g, "").trim();
+    if (!normalized || normalized.length === 0) return undefined;
+    if (input.validValues && !input.validValues.includes(normalized)) return undefined;
+    return normalized;
+  };
+
   if (!input.envKey) return undefined;
   if (input.kind === "config") {
     const resolved = resolveMessagingChannelConfigEnvValue(input.envKey, process.env);
-    if (resolved.value) return resolved.value;
+    const normalizedResolved = normalize(resolved.value);
+    if (normalizedResolved !== undefined) return normalizedResolved;
   }
-  const value = process.env[input.envKey];
-  const normalized = value?.replace(/\r/g, "").trim();
-  if (!normalized || normalized.length === 0) return undefined;
-  if (input.validValues && !input.validValues.includes(normalized)) return undefined;
-  return normalized;
+  return normalize(process.env[input.envKey]);
 }
🤖 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/messaging/compiler/manifest-compiler.ts` around lines 333 - 343, In
readInputEnvValue, config-resolved values returned early bypass the validValues
check; after calling resolveMessagingChannelConfigEnvValue(input.envKey,
process.env) assign its value to a local, normalize it the same way as env
lookup (strip \r and trim), reject if empty, and then apply
input.validValues.includes(normalized) before returning—i.e., replace the early
return of resolved.value with the same normalization and validValues gating used
for process.env[input.envKey] so both code paths enforce constraints (refer to
readInputEnvValue, resolveMessagingChannelConfigEnvValue, and
input.validValues).
🧹 Nitpick comments (3)
src/lib/actions/sandbox/rebuild.ts (1)

273-273: Run the rebuild-specific E2E suite for this behavior shift.

Please run the channels-stop-start-e2e workflow for this rebuild-path change to validate disabled-channel persistence and post-rebuild messaging reapply behavior.

As per coding guidelines, "src/lib/actions/sandbox/rebuild.ts ... E2E test recommendation: channels-stop-start-e2e."

🤖 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/actions/sandbox/rebuild.ts` at line 273, The rebuildSandbox change
needs its specific end-to-end validation: run the channels-stop-start-e2e
workflow to exercise rebuild-specific scenarios for disabled-channel persistence
and post-rebuild message reapply behavior; trigger that CI job and attach the
resulting logs to this PR, ensuring the rebuildSandbox function's behavior
around channel disable/persist and message reapply is confirmed by the E2E
results.

Source: Coding guidelines

Dockerfile (1)

413-550: Run the Dockerfile E2E subset before merge.

These changes move critical build behavior into manifest hook execution at image-build time, so unit tests alone won’t fully validate runtime delivery. Please run the recommended selective nightly E2E jobs for this path.

As per coding guidelines, Dockerfile changes are only fully testable with a real container build and should use the recommended selective E2E workflows.

🤖 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 `@Dockerfile` around lines 413 - 550, The Dockerfile change runs
generate-openclaw-config.mts and run-openclaw-build-hooks.mts at build time
which requires a real container build to validate; before merging, perform the
Dockerfile E2E subset by building the image and running the repository's
selective nightly E2E jobs that exercise build-hook and config-generation paths
(specifically exercise run-openclaw-build-hooks.mts and
generate-openclaw-config.mts execution), verify the produced openclaw.json and
gateway token behavior in both root and non-root modes, and surface any failures
back to the PR so we can fix build-hook or config generation issues before
merge.

Source: Coding guidelines

src/lib/messaging/compiler/engines/template.ts (1)

161-197: 🏗️ Heavy lift

Break up resolveTemplateReference to reduce branching complexity

This resolver now centralizes many channel-specific branches, which makes it brittle for future additions. Split into a keyed resolver map (or per-channel resolver modules) and keep this function as a thin dispatcher.

As per coding guidelines, **/*.{js,ts,tsx,jsx}: "Keep function complexity low in JavaScript and TypeScript code."

🤖 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/messaging/compiler/engines/template.ts` around lines 161 - 197, The
resolveTemplateReference function has grown many channel-specific branches;
refactor it into a thin dispatcher that routes to per-channel resolver functions
or a keyed resolver map (e.g., a map keyed by the top-level token like
"discord", "telegramConfig", "wechatConfig", "slackConfig", "allowedIds",
"proxyUrl") and move the existing logic into those handlers (extract logic
currently in resolveTemplateReference for discord: discordGuilds,
discordAllowedUsers, discordRequireMention, for allowedIds:
resolveAllowedIdsTemplate usage, for telegramConfig: parseBoolean/stateValue,
for wechatConfig: stateValue, for slackConfig: slackAllowedChannels, and
proxyUrl) so resolveTemplateReference only picks the handler and returns
handler(reference, context) or the default template string; ensure semantics and
return types of RenderTemplateValue remain unchanged.

Source: Coding guidelines

🤖 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 `@agents/hermes/config/manifest-hooks.ts`:
- Around line 83-90: The code currently treats any non-"json-fragment" render as
env-lines; update the logic in manifest-hooks.ts where render.kind is checked
(around the applyJsonRender/applyEnvRender calls) to explicitly handle allowed
kinds (e.g., "json-fragment" and "env-lines") and fail-closed for anything else:
if render.kind === "json-fragment" call applyJsonRender and push
appliedHooks/appliedTargets, else if render.kind === "env-lines" call
applyEnvRender and push appliedTargets, otherwise throw a descriptive Error (or
call the project’s error/assert utility) including the invalid render.kind and
render.target so schema drift/typos surface immediately.

In `@src/lib/messaging/compiler/engines/agent-render-engine.ts`:
- Around line 31-42: The default parameter creates an empty
MessagingHookRegistry which lacks required hooks (notably common.staticOutputs)
causing run-time failures when runMessagingHook is invoked; fix by ensuring the
passed-or-default registry contains the shared hooks before use — e.g., if hooks
is empty, register common.staticOutputs (and any other shared hooks) into the
MessagingHookRegistry instance used here (look for the hooks parameter,
MessagingHookRegistry, common.staticOutputs, renderHookForManifestEntry and
runMessagingHook) so render planning succeeds when callers omit hooks.

In `@test/e2e/docs/parity-inventory.generated.json`:
- Around line 8805-8807: The JSON entry's normalized_id is out of sync with its
text: update the "normalized_id" value for the object whose "text" is "M-W9:
Real WeChat token spliced into accounts/${WECHAT_ACCOUNT}.json — manifest seed
placeholder regression" so it reflects the manifest-seed regression semantics
(remove or replace the "seed.wechat.accounts.py" fragment and normalize to match
the text), ensuring the normalized_id string corresponds to the same source
semantics as the "text" field.

In `@test/onboard.test.ts`:
- Line 2718: Update the legacyMaxLines entry for the test file in the
test-file-size budget JSON: locate the JSON object key "test/onboard.test.ts" in
the test-file-size budget configuration and change its value from 4887 to 4879
so the legacyMaxLines matches the current file length (4879).

---

Outside diff comments:
In `@src/lib/messaging/compiler/manifest-compiler.ts`:
- Around line 333-343: In readInputEnvValue, config-resolved values returned
early bypass the validValues check; after calling
resolveMessagingChannelConfigEnvValue(input.envKey, process.env) assign its
value to a local, normalize it the same way as env lookup (strip \r and trim),
reject if empty, and then apply input.validValues.includes(normalized) before
returning—i.e., replace the early return of resolved.value with the same
normalization and validValues gating used for process.env[input.envKey] so both
code paths enforce constraints (refer to readInputEnvValue,
resolveMessagingChannelConfigEnvValue, and input.validValues).

In `@src/lib/onboard.ts`:
- Around line 3383-3387: The current guard uses activeMessagingChannels to
decide whether to set telegramConfig.requireMention, which drops the persisted
Telegram mention policy when providers are temporarily stopped; change the check
to use the persisted/configured channel set (e.g., enabledChannels or
enabledMessagingChannels / persisted messaging channels) instead of
activeMessagingChannels so computeTelegramRequireMention() runs whenever
Telegram is configured, not only when a provider is attached; update the block
around telegramConfig and computeTelegramRequireMention to key off that
persisted/enable set (ensure you reference telegramConfig,
computeTelegramRequireMention, and the enabledChannels/enabledMessagingChannels
variable) so TELEGRAM_REQUIRE_MENTION survives channels stop/start cycles.

In `@test/generate-openclaw-config.test.ts`:
- Around line 1-4: The recorded legacy "test-size" budget entry is off by one
(recorded 2106 vs actual 2105 lines) causing CI failures; update the legacy
test-size budget value to 2105 to match test/generate-openclaw-config.test.ts
current line count so codebase-growth-guardrails passes—locate the budget entry
named "legacy test-size" (or the test-size entry used by the
codebase-growth-guardrails config) and lower its numeric value from 2106 to
2105.

---

Nitpick comments:
In `@Dockerfile`:
- Around line 413-550: The Dockerfile change runs generate-openclaw-config.mts
and run-openclaw-build-hooks.mts at build time which requires a real container
build to validate; before merging, perform the Dockerfile E2E subset by building
the image and running the repository's selective nightly E2E jobs that exercise
build-hook and config-generation paths (specifically exercise
run-openclaw-build-hooks.mts and generate-openclaw-config.mts execution), verify
the produced openclaw.json and gateway token behavior in both root and non-root
modes, and surface any failures back to the PR so we can fix build-hook or
config generation issues before merge.

In `@src/lib/actions/sandbox/rebuild.ts`:
- Line 273: The rebuildSandbox change needs its specific end-to-end validation:
run the channels-stop-start-e2e workflow to exercise rebuild-specific scenarios
for disabled-channel persistence and post-rebuild message reapply behavior;
trigger that CI job and attach the resulting logs to this PR, ensuring the
rebuildSandbox function's behavior around channel disable/persist and message
reapply is confirmed by the E2E results.

In `@src/lib/messaging/compiler/engines/template.ts`:
- Around line 161-197: The resolveTemplateReference function has grown many
channel-specific branches; refactor it into a thin dispatcher that routes to
per-channel resolver functions or a keyed resolver map (e.g., a map keyed by the
top-level token like "discord", "telegramConfig", "wechatConfig", "slackConfig",
"allowedIds", "proxyUrl") and move the existing logic into those handlers
(extract logic currently in resolveTemplateReference for discord: discordGuilds,
discordAllowedUsers, discordRequireMention, for allowedIds:
resolveAllowedIdsTemplate usage, for telegramConfig: parseBoolean/stateValue,
for wechatConfig: stateValue, for slackConfig: slackAllowedChannels, and
proxyUrl) so resolveTemplateReference only picks the handler and returns
handler(reference, context) or the default template string; ensure semantics and
return types of RenderTemplateValue remain unchanged.
🪄 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: 4beb4b5f-e93c-4052-9c9a-7af56ccfdd8d

📥 Commits

Reviewing files that changed from the base of the PR and between ec408c8 and 59c6309.

📒 Files selected for processing (56)
  • Dockerfile
  • agents/hermes/Dockerfile
  • agents/hermes/config/build-env.ts
  • agents/hermes/config/hermes-config.ts
  • agents/hermes/config/hermes-env.ts
  • agents/hermes/config/manifest-hooks.ts
  • agents/hermes/config/messaging-config.ts
  • agents/hermes/generate-config.ts
  • agents/hermes/policy-additions.yaml
  • scripts/generate-openclaw-config.mts
  • scripts/lib/sandbox-init.sh
  • scripts/openclaw-build-messaging-plugins.py
  • scripts/run-openclaw-build-hooks.mts
  • scripts/seed-wechat-accounts.py
  • src/ext/wechat/qr.ts
  • src/lib/actions/sandbox/policy-channel.ts
  • src/lib/actions/sandbox/rebuild.ts
  • src/lib/adapters/openshell/client.ts
  • src/lib/adapters/openshell/runtime.ts
  • src/lib/messaging/applier/agent-config.ts
  • src/lib/messaging/applier/setup-applier.test.ts
  • src/lib/messaging/channels/discord/manifest.ts
  • src/lib/messaging/channels/manifests.test.ts
  • src/lib/messaging/channels/slack/manifest.ts
  • src/lib/messaging/channels/telegram/manifest.ts
  • src/lib/messaging/channels/wechat/manifest.ts
  • src/lib/messaging/channels/whatsapp/manifest.ts
  • src/lib/messaging/compiler/engines/agent-render-engine.ts
  • src/lib/messaging/compiler/engines/build-step-engine.ts
  • src/lib/messaging/compiler/engines/template.ts
  • src/lib/messaging/compiler/manifest-compiler.test.ts
  • src/lib/messaging/compiler/manifest-compiler.ts
  • src/lib/messaging/compiler/workflow-planner.test.ts
  • src/lib/messaging/hooks/common/index.ts
  • src/lib/messaging/hooks/common/static-outputs.ts
  • src/lib/messaging/hooks/common/token-paste.test.ts
  • src/lib/messaging/hooks/hook-runner.test.ts
  • src/lib/messaging/manifest/types.ts
  • src/lib/onboard.ts
  • src/lib/onboard/dockerfile-patch.test.ts
  • src/lib/onboard/dockerfile-patch.ts
  • src/lib/onboard/messaging-config.test.ts
  • src/lib/onboard/messaging-config.ts
  • src/lib/onboard/wechat-config.ts
  • src/lib/sandbox/build-context.ts
  • test/e2e/docs/parity-inventory.generated.json
  • test/e2e/test-messaging-providers.sh
  • test/generate-hermes-config.test.ts
  • test/generate-openclaw-config.test.ts
  • test/messaging-plan-test-helper.ts
  • test/onboard-messaging.test.ts
  • test/onboard.test.ts
  • test/run-openclaw-build-hooks.test.ts
  • test/sandbox-build-context.test.ts
  • test/sandbox-provisioning.test.ts
  • test/seed-wechat-accounts.test.ts
💤 Files with no reviewable changes (6)
  • src/lib/onboard/messaging-config.test.ts
  • scripts/openclaw-build-messaging-plugins.py
  • test/seed-wechat-accounts.test.ts
  • scripts/seed-wechat-accounts.py
  • agents/hermes/config/messaging-config.ts
  • agents/hermes/config/build-env.ts

Comment thread agents/hermes/config/manifest-hooks.ts Outdated
Comment on lines +83 to +90
if (render.kind === "json-fragment") {
applyJsonRender(config, render);
appliedTargets.push(render.target);
if (render.hookId) appliedHooks.push(`${render.channelId}:${render.hookId}`);
continue;
}
applyEnvRender(envLines, render);
appliedTargets.push(render.target);
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject unknown manifest render kinds explicitly (fail-closed).

At Line 83, any non-json-fragment kind is treated as env-lines. An unexpected kind (schema drift/typo) should hard-fail instead of silently entering the env path.

Suggested fix
-    if (render.kind === "json-fragment") {
+    if (render.kind === "json-fragment") {
       applyJsonRender(config, render);
       appliedTargets.push(render.target);
       if (render.hookId) appliedHooks.push(`${render.channelId}:${render.hookId}`);
       continue;
     }
-    applyEnvRender(envLines, render);
-    appliedTargets.push(render.target);
-    if (render.hookId) appliedHooks.push(`${render.channelId}:${render.hookId}`);
+    if (render.kind === "env-lines") {
+      applyEnvRender(envLines, render);
+      appliedTargets.push(render.target);
+      if (render.hookId) appliedHooks.push(`${render.channelId}:${render.hookId}`);
+      continue;
+    }
+    throw new Error(
+      `Hermes manifest hook render '${render.renderId ?? render.channelId}' has unsupported kind '${String(render.kind)}'.`,
+    );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (render.kind === "json-fragment") {
applyJsonRender(config, render);
appliedTargets.push(render.target);
if (render.hookId) appliedHooks.push(`${render.channelId}:${render.hookId}`);
continue;
}
applyEnvRender(envLines, render);
appliedTargets.push(render.target);
if (render.kind === "json-fragment") {
applyJsonRender(config, render);
appliedTargets.push(render.target);
if (render.hookId) appliedHooks.push(`${render.channelId}:${render.hookId}`);
continue;
}
if (render.kind === "env-lines") {
applyEnvRender(envLines, render);
appliedTargets.push(render.target);
if (render.hookId) appliedHooks.push(`${render.channelId}:${render.hookId}`);
continue;
}
throw new Error(
`Hermes manifest hook render '${render.renderId ?? render.channelId}' has unsupported kind '${String(render.kind)}'.`,
);
🤖 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/config/manifest-hooks.ts` around lines 83 - 90, The code
currently treats any non-"json-fragment" render as env-lines; update the logic
in manifest-hooks.ts where render.kind is checked (around the
applyJsonRender/applyEnvRender calls) to explicitly handle allowed kinds (e.g.,
"json-fragment" and "env-lines") and fail-closed for anything else: if
render.kind === "json-fragment" call applyJsonRender and push
appliedHooks/appliedTargets, else if render.kind === "env-lines" call
applyEnvRender and push appliedTargets, otherwise throw a descriptive Error (or
call the project’s error/assert utility) including the invalid render.kind and
render.target so schema drift/typos surface immediately.

Comment thread src/lib/messaging/compiler/engines/agent-render-engine.ts Outdated
Comment thread test/e2e/docs/parity-inventory.generated.json Outdated
Comment thread test/onboard.test.ts
@sandl99 sandl99 marked this pull request as draft June 8, 2026 13:43
@copy-pr-bot
Copy link
Copy Markdown

copy-pr-bot Bot commented Jun 8, 2026

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

Comment thread test/openclaw-config-render.test.ts Fixed
Comment thread src/lib/messaging/applier/build/messaging-build-applier.mts Fixed
Comment thread test/security-c2-dockerfile-injection.test.ts Fixed
Comment thread test/openclaw-config-render.test.ts Fixed
Comment thread src/lib/messaging/compiler/engines/agent-render-engine.ts Fixed
@NVIDIA NVIDIA deleted a comment from github-actions Bot Jun 8, 2026
@wscurran wscurran added area: messaging Messaging channels, bridges, manifests, or channel lifecycle refactor PR restructures code without intended behavior change labels Jun 8, 2026
@wscurran
Copy link
Copy Markdown
Contributor

wscurran commented Jun 8, 2026

…ort, function or class'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
Dockerfile (1)

559-597: Run the Dockerfile E2E subset before merge.

This path changes build-time messaging hook orchestration and post-install file generation, so it should be validated with the recommended nightly E2E job subset for Dockerfile-impacting changes.

As per coding guidelines: Dockerfile ... E2E test recommendation: cloud-e2e, sandbox-survival-e2e, hermes-e2e, rebuild-openclaw-e2e, openclaw-tui-chat-correlation-e2e.

🤖 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 `@Dockerfile` around lines 559 - 597, This change updates build-time messaging
hook orchestration (the RUN node --experimental-strip-types
/src/lib/messaging/applier/build/messaging-build-applier.mts calls for --phase
agent-install and --phase post-agent-install and the openclaw plugins
install/enable/inspect steps), so before merging run the Dockerfile-impact E2E
subset: cloud-e2e, sandbox-survival-e2e, hermes-e2e, rebuild-openclaw-e2e, and
openclaw-tui-chat-correlation-e2e; verify the messaging post-agent-install
outputs are generated into the sandbox as expected, that openclaw plugins
install/enable/inspect succeeds and registers the /nemoclaw runtime command, and
that no network resolution occurred during the plugin install (confirm
NPM_CONFIG_OFFLINE behavior and pruned plugin-runtime-deps artifacts).

Source: Coding guidelines

agents/hermes/Dockerfile (1)

135-147: Run the Hermes E2E subset before merge.

These changes affect Hermes onboarding/build hooks and post-install render/build-file application, so they should be validated with the Hermes-focused nightly jobs.

As per coding guidelines: agents/hermes/** ... E2E test recommendation: hermes-e2e, hermes-inference-switch-e2e, hermes-discord-e2e, hermes-slack-e2e, hermes-onboard-security-posture-e2e, rebuild-hermes-e2e, rebuild-hermes-stale-base-e2e.

🤖 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/Dockerfile` around lines 135 - 147, These Dockerfile changes
touch Hermes onboarding and post-install hooks (see the RUN commands invoking
/src/lib/messaging/applier/build/messaging-build-applier.mts with --agent hermes
--phase agent-install and --phase post-agent-install and the nemoclaw plugin
copy into /sandbox/.hermes/plugins/nemoclaw/), so ensure the Hermes-focused E2E
subset is executed before merging by updating CI to include the recommended jobs
(hermes-e2e, hermes-inference-switch-e2e, hermes-discord-e2e, hermes-slack-e2e,
hermes-onboard-security-posture-e2e, rebuild-hermes-e2e,
rebuild-hermes-stale-base-e2e) in the PR/check pipeline or by scheduling a
blocking nightly run that validates these changes; modify the workflow matrix or
job list accordingly so these tests run against this Dockerfile change.

Source: Coding guidelines

🤖 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/messaging/applier/build/messaging-build-applier.mts`:
- Around line 385-396: The current resolveAgentRenderTarget allows path
traversal via inputs like "~/.openclaw/../..." because it only checks prefixes;
fix by normalizing/ resolving the sliced subpath and validating it stays inside
the intended agent root before returning. In resolveAgentRenderTarget (variables
target, agent, home and throws MessagingBuildApplierError), compute the
agentRoot (join(home, ".openclaw") or join(home, ".hermes")), resolve the
candidate path against agentRoot and ensure the resolved path starts with
agentRoot (or that no path segment is ".."/contains upward traversal) and throw
MessagingBuildApplierError if validation fails; only return the resolved safe
path when the check passes.

---

Nitpick comments:
In `@agents/hermes/Dockerfile`:
- Around line 135-147: These Dockerfile changes touch Hermes onboarding and
post-install hooks (see the RUN commands invoking
/src/lib/messaging/applier/build/messaging-build-applier.mts with --agent hermes
--phase agent-install and --phase post-agent-install and the nemoclaw plugin
copy into /sandbox/.hermes/plugins/nemoclaw/), so ensure the Hermes-focused E2E
subset is executed before merging by updating CI to include the recommended jobs
(hermes-e2e, hermes-inference-switch-e2e, hermes-discord-e2e, hermes-slack-e2e,
hermes-onboard-security-posture-e2e, rebuild-hermes-e2e,
rebuild-hermes-stale-base-e2e) in the PR/check pipeline or by scheduling a
blocking nightly run that validates these changes; modify the workflow matrix or
job list accordingly so these tests run against this Dockerfile change.

In `@Dockerfile`:
- Around line 559-597: This change updates build-time messaging hook
orchestration (the RUN node --experimental-strip-types
/src/lib/messaging/applier/build/messaging-build-applier.mts calls for --phase
agent-install and --phase post-agent-install and the openclaw plugins
install/enable/inspect steps), so before merging run the Dockerfile-impact E2E
subset: cloud-e2e, sandbox-survival-e2e, hermes-e2e, rebuild-openclaw-e2e, and
openclaw-tui-chat-correlation-e2e; verify the messaging post-agent-install
outputs are generated into the sandbox as expected, that openclaw plugins
install/enable/inspect succeeds and registers the /nemoclaw runtime command, and
that no network resolution occurred during the plugin install (confirm
NPM_CONFIG_OFFLINE behavior and pruned plugin-runtime-deps artifacts).
🪄 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: 4959dcd2-ec8c-461d-8cd7-1045da67a1c3

📥 Commits

Reviewing files that changed from the base of the PR and between 59c6309 and 19ab1d1.

📒 Files selected for processing (20)
  • Dockerfile
  • agents/hermes/Dockerfile
  • agents/hermes/generate-config.ts
  • ci/test-file-size-budget.json
  • scripts/generate-openclaw-config.mts
  • src/lib/messaging/applier/build/messaging-build-applier.mts
  • src/lib/messaging/compiler/engines/agent-render-engine.ts
  • src/lib/messaging/compiler/manifest-compiler.ts
  • src/lib/onboard.ts
  • src/lib/sandbox/build-context.ts
  • test/e2e/docs/parity-inventory.generated.json
  • test/fetch-guard-patch-regression.test.ts
  • test/generate-hermes-config.test.ts
  • test/generate-openclaw-config.test.ts
  • test/helpers/messaging-plan-fixtures.ts
  • test/messaging-build-applier.test.ts
  • test/onboard-messaging.test.ts
  • test/sandbox-build-context.test.ts
  • test/sandbox-provisioning.test.ts
  • test/security-c2-dockerfile-injection.test.ts
✅ Files skipped from review due to trivial changes (1)
  • test/e2e/docs/parity-inventory.generated.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • agents/hermes/generate-config.ts
  • test/generate-hermes-config.test.ts
  • src/lib/messaging/compiler/manifest-compiler.ts
  • test/onboard-messaging.test.ts
  • scripts/generate-openclaw-config.mts

Comment on lines +385 to +396
if (target.startsWith("~/.openclaw/")) {
if (agent !== "openclaw") {
throw new MessagingBuildApplierError(`Messaging render target ${target} does not match ${agent}.`);
}
return join(home, ".openclaw", target.slice("~/.openclaw/".length));
}
if (target.startsWith("~/.hermes/")) {
if (agent !== "hermes") {
throw new MessagingBuildApplierError(`Messaging render target ${target} does not match ${agent}.`);
}
return join(home, ".hermes", target.slice("~/.hermes/".length));
}
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent render-target path traversal in resolveAgentRenderTarget.

At Line [385] and Line [391], prefix-only checks allow ~/.openclaw/../... or ~/.hermes/../... to escape the agent root and write outside intended config trees during render application.

Suggested fix
 function resolveAgentRenderTarget(
   agent: MessagingAgentId,
   target: string,
   options: { readonly homeDir?: string } = {},
 ): string {
   const home = options.homeDir ?? homedir();
+  const agentRoot = agent === "hermes" ? join(home, ".hermes") : join(home, ".openclaw");
+  const normalizedRoot = resolve(agentRoot);
   if (agent === "openclaw" && target === "openclaw.json") {
-    return join(home, ".openclaw", "openclaw.json");
+    return join(agentRoot, "openclaw.json");
   }
+  let relativePath: string | null = null;
   if (target.startsWith("~/.openclaw/")) {
     if (agent !== "openclaw") {
       throw new MessagingBuildApplierError(`Messaging render target ${target} does not match ${agent}.`);
     }
-    return join(home, ".openclaw", target.slice("~/.openclaw/".length));
+    relativePath = target.slice("~/.openclaw/".length);
   }
-  if (target.startsWith("~/.hermes/")) {
+  if (target.startsWith("~/.hermes/")) {
     if (agent !== "hermes") {
       throw new MessagingBuildApplierError(`Messaging render target ${target} does not match ${agent}.`);
     }
-    return join(home, ".hermes", target.slice("~/.hermes/".length));
+    relativePath = target.slice("~/.hermes/".length);
+  }
+  if (relativePath !== null) {
+    const resolvedTarget = resolve(agentRoot, relativePath);
+    if (resolvedTarget !== normalizedRoot && !resolvedTarget.startsWith(`${normalizedRoot}${sep}`)) {
+      throw new MessagingBuildApplierError(
+        `Messaging render target ${target} must stay inside ${agentRoot}.`,
+      );
+    }
+    return resolvedTarget;
   }
   throw new MessagingBuildApplierError(`Unsupported messaging render target ${target}.`);
 }
🤖 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/messaging/applier/build/messaging-build-applier.mts` around lines 385
- 396, The current resolveAgentRenderTarget allows path traversal via inputs
like "~/.openclaw/../..." because it only checks prefixes; fix by normalizing/
resolving the sliced subpath and validating it stays inside the intended agent
root before returning. In resolveAgentRenderTarget (variables target, agent,
home and throws MessagingBuildApplierError), compute the agentRoot (join(home,
".openclaw") or join(home, ".hermes")), resolve the candidate path against
agentRoot and ensure the resolved path starts with agentRoot (or that no path
segment is ".."/contains upward traversal) and throw MessagingBuildApplierError
if validation fails; only return the resolved safe path when the check passes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: messaging Messaging channels, bridges, manifests, or channel lifecycle refactor PR restructures code without intended behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase 5: migrate messaging agent rendering and build inputs to manifests

3 participants