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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Start with the manifest. Add core code only when the manifest vocabulary cannot
3. Add hooks under `channels/<channel>/hooks/` only for enrollment, external reachability checks, QR capture, conflict checks, runtime status, or health probes that cannot be static manifest data.
4. Register the manifest in `channels/built-ins.ts`, template resolver in `channels/template-resolver.ts`, and hook handlers in `hooks/builtins.ts`.
5. Add `nemoclaw-blueprint/policies/presets/<channel>.yaml` when the manifest declares a policy preset. Keep messaging-specific egress opt-in unless the project policy says otherwise.
6. Update `agents/openclaw/manifest.yaml` and/or `agents/hermes/manifest.yaml` so supported platforms match the manifest `supportedAgents`.
6. Declare channel support only in `src/lib/messaging/channels/<channel>/manifest.ts` through `supportedAgents`. Do not edit agent manifests for channel availability unless a separate agent contract changed.
7. Add agent package install metadata when the channel needs an external agent plugin. For OpenClaw plugin packages, use this shape unless source evidence says otherwise:

```ts
Expand Down
20 changes: 0 additions & 20 deletions agents/hermes/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -103,26 +103,6 @@ device_pairing: false
web_auth_method: bearer_token
web_auth_env: API_SERVER_KEY

# ── Messaging platforms ─────────────────────────────────────────
# Hermes natively supports 14 platforms. We enable the platforms with
# OpenShell policy and NemoClaw config support. WeChat uses the iLink
# gateway (host-side QR login during onboard); the in-sandbox Hermes
# adapter reads WEIXIN_TOKEN + WEIXIN_ACCOUNT_ID per
# https://hermes-agent.nousresearch.com/docs/user-guide/messaging/weixin.
# WhatsApp pairs in the sandbox via `hermes whatsapp`; the selected channel
# bakes WHATSAPP_ENABLED/WHATSAPP_MODE into .env and preserves session state.
# Microsoft Teams uses the Bot Framework webhook adapter at /api/messages.
messaging_platforms:
supported:
- telegram
- discord
- slack
- wechat
- whatsapp
- teams
# Future: signal, matrix, mattermost, email, etc.
# Each needs a network policy entry before enabling.

# ── Inference ───────────────────────────────────────────────────
# Hermes supports custom OpenAI-compatible endpoints natively via
# provider: "custom" + base_url in config.yaml. This is how we
Expand Down
10 changes: 1 addition & 9 deletions agents/langchain-deepagents-code/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ COPY agents/langchain-deepagents-code/patch-managed-deepagents-code.py /opt/nemo
COPY agents/langchain-deepagents-code/dcode-wrapper.sh /usr/local/lib/nemoclaw/dcode-wrapper.sh
COPY agents/langchain-deepagents-code/start.sh /usr/local/bin/nemoclaw-start
COPY nemoclaw-blueprint/ /opt/nemoclaw-blueprint/
COPY src/lib/messaging/ /src/lib/messaging/
RUN chmod 444 /opt/nemoclaw-deepagents-code/generate-config.ts /opt/nemoclaw-deepagents-code/patch-managed-deepagents-code.py \
&& chmod 755 /usr/local/bin/nemoclaw-start /usr/local/lib/nemoclaw/dcode-wrapper.sh \
&& chmod -R a+rX /opt/nemoclaw-blueprint /src/lib/messaging \
&& chmod -R a+rX /opt/nemoclaw-blueprint \
&& python3 /opt/nemoclaw-deepagents-code/patch-managed-deepagents-code.py \
&& rm -f /usr/local/bin/dcode /usr/local/bin/deepagents-code /opt/venv/bin/dcode /opt/venv/bin/deepagents-code \
&& install -m 0755 /usr/local/lib/nemoclaw/dcode-wrapper.sh /usr/local/bin/dcode \
Expand All @@ -44,7 +43,6 @@ ARG NEMOCLAW_UPSTREAM_PROVIDER=nvidia
ARG NEMOCLAW_INFERENCE_BASE_URL=https://inference.local/v1
ARG NEMOCLAW_INFERENCE_API=openai-completions
ARG NEMOCLAW_BUILD_ID=default
ARG NEMOCLAW_MESSAGING_PLAN_B64=
ARG NEMOCLAW_DARWIN_VM_COMPAT=0

ENV HOME=/sandbox \
Expand All @@ -56,7 +54,6 @@ ENV HOME=/sandbox \
NEMOCLAW_INFERENCE_BASE_URL=${NEMOCLAW_INFERENCE_BASE_URL} \
NEMOCLAW_INFERENCE_API=${NEMOCLAW_INFERENCE_API} \
NEMOCLAW_BUILD_ID=${NEMOCLAW_BUILD_ID} \
NEMOCLAW_MESSAGING_PLAN_B64=${NEMOCLAW_MESSAGING_PLAN_B64} \
DEEPAGENTS_CODE_NO_UPDATE_CHECK=1 \
DEEPAGENTS_CODE_AUTO_UPDATE=0 \
DEEPAGENTS_CODE_OPENAI_API_KEY=nemoclaw-managed-inference \
Expand All @@ -70,11 +67,6 @@ RUN mkdir -p /sandbox/.nemoclaw/blueprints/0.1.0 \
&& node --experimental-strip-types /opt/nemoclaw-deepagents-code/generate-config.ts \
&& chmod 660 /sandbox/.deepagents/config.toml

# Apply messaging render and post-agent-install build-file hooks. DeepAgents has
# no agent-install package step today, so only post-agent-install runs.
# hadolint ignore=DL3059
RUN node --experimental-strip-types /src/lib/messaging/applier/build/messaging-build-applier.mts --agent langchain-deepagents-code --phase post-agent-install

USER root
RUN chown root:root /sandbox/.nemoclaw \
&& chmod 1755 /sandbox/.nemoclaw \
Expand Down
6 changes: 0 additions & 6 deletions agents/langchain-deepagents-code/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,6 @@ user_managed_files:

device_pairing: false

# Artifact-only messaging render exists for build validation, but DeepAgents
# does not run a channel bridge today. Keep public channel support disabled
# until inbound Telegram/Discord/Slack messages can reach dcode.
messaging_platforms:
supported: []

# ── Inference ───────────────────────────────────────────────────
# V1 routes NVIDIA/OpenAI-compatible selections through OpenShell's managed
# inference.local endpoint using Deep Agents Code's OpenAI-compatible provider.
Expand Down
38 changes: 0 additions & 38 deletions agents/langchain-deepagents-code/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,33 +51,6 @@ write_proxy_export_pair() {
write_export_if_set "$secondary"
}

is_messaging_env_key_allowed() {
case "$1" in
TELEGRAM_BOT_TOKEN | TELEGRAM_ALLOWED_USERS | DISCORD_BOT_TOKEN | NEMOCLAW_DISCORD_GUILD_IDS) return 0 ;;
DISCORD_ALLOWED_USERS | DISCORD_ALLOW_ALL_USERS | SLACK_BOT_TOKEN | SLACK_APP_TOKEN) return 0 ;;
SLACK_ALLOWED_USERS | SLACK_ALLOWED_CHANNELS) return 0 ;;
*) return 1 ;;
esac
}

load_messaging_env() {
local env_file="/sandbox/.deepagents/.env"
local line key value
[ -r "$env_file" ] || return 0
while IFS= read -r line || [ -n "$line" ]; do
line="${line%$'\r'}"
[ -n "$line" ] || continue
case "$line" in \#*) continue ;; esac
key="${line%%=*}"
if [ "$key" = "$line" ] || ! is_messaging_env_key_allowed "$key"; then
printf 'Skipping invalid Deep Agents Code messaging env line for key %s.\n' "$key" >&2
continue
fi
value="${line#*=}"
export "$key=$value"
done <"$env_file"
}

prepare_runtime_env() {
local target=/tmp/nemoclaw-proxy-env.sh
local tmp
Expand All @@ -101,22 +74,11 @@ prepare_runtime_env() {
write_export_if_set LANGSMITH_TRACING
write_export_if_set LANGSMITH_PROJECT
write_export_if_set DEEPAGENTS_CODE_LANGSMITH_PROJECT
write_export_if_set TELEGRAM_BOT_TOKEN
write_export_if_set TELEGRAM_ALLOWED_USERS
write_export_if_set DISCORD_BOT_TOKEN
write_export_if_set NEMOCLAW_DISCORD_GUILD_IDS
write_export_if_set DISCORD_ALLOWED_USERS
write_export_if_set DISCORD_ALLOW_ALL_USERS
write_export_if_set SLACK_BOT_TOKEN
write_export_if_set SLACK_APP_TOKEN
write_export_if_set SLACK_ALLOWED_USERS
write_export_if_set SLACK_ALLOWED_CHANNELS
} >"$tmp"
chmod 400 "$tmp"
mv -f "$tmp" "$target"
}

load_messaging_env
prepare_runtime_env

# With no command, this invocation IS the sandbox's long-running entrypoint.
Expand Down
10 changes: 0 additions & 10 deletions agents/openclaw/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,6 @@ user_managed_files:
device_pairing: true
web_auth_method: device_pairing

# ── Messaging platforms ─────────────────────────────────────────
messaging_platforms:
supported:
- telegram
- discord
- slack
- wechat
- whatsapp
- teams

# ── Inference ───────────────────────────────────────────────────
inference:
provider_type: gateway_managed
Expand Down
2 changes: 1 addition & 1 deletion ci/platform-matrix.json
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@
{
"name": "WhatsApp",
"status": "caveated",
"notes": "Supported by both OpenClaw and Hermes (see `messaging_platforms` in `agents/openclaw/manifest.yaml` and `agents/hermes/manifest.yaml`). Pairing happens in the sandbox through WhatsApp Web by scanning a QR code at first run; the Hermes flow exposes this as `hermes whatsapp` and persists session credentials under `~/.hermes/platforms/whatsapp/session` (`agents/hermes/manifest.yaml:69-71`). Sandbox egress goes through the `whatsapp` policy preset, which carries the WebSocket / Noise / h1-ALPN caveats documented in `nemoclaw-blueprint/policies/presets/whatsapp.yaml`. No Meta Business API integration today; that path is out of scope for this matrix."
"notes": "Supported by both OpenClaw and Hermes through the channel manifest `supportedAgents` declaration in `src/lib/messaging/channels/whatsapp/manifest.ts`. Pairing happens in the sandbox through WhatsApp Web by scanning a QR code at first run; the Hermes flow exposes this as `hermes whatsapp` and persists session credentials under `~/.hermes/platforms/whatsapp/session` (`agents/hermes/manifest.yaml:69-71`). Sandbox egress goes through the `whatsapp` policy preset, which carries the WebSocket / Noise / h1-ALPN caveats documented in `nemoclaw-blueprint/policies/presets/whatsapp.yaml`. No Meta Business API integration today; that path is out of scope for this matrix."
},
{
"name": "Microsoft Teams",
Expand Down
3 changes: 2 additions & 1 deletion docs/about/release-notes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ NemoClaw v0.0.68 improves onboarding recovery, messaging setup, agent-specific C
Scripted installs propagate onboarding exit codes, interrupted installer runs start fresh when no sandbox exists to resume, and resume behavior stays tied to onboarding sessions with a real sandbox.
For more information, refer to [NemoClaw Quickstart with OpenClaw](../get-started/quickstart) and [Troubleshooting](../reference/troubleshooting).
- Messaging setup adds experimental Microsoft Teams channel onboarding for OpenClaw and Hermes, including Bot Framework credentials, webhook forwarding, the `teams` network policy preset, and local webhook port conflict checks.
NemoClaw also treats `messaging_platforms: []` as an explicit deny-all declaration and rejects `channels add` before policy, provider, registry, credential, or rebuild mutations when the selected agent does not support messaging.
NemoClaw also derives channel availability from each channel manifest's `supportedAgents` declaration and rejects `channels add` before policy, provider, registry, credential, or rebuild mutations when no channel manifest supports the selected agent.
LangChain Deep Agents Code therefore fails closed for messaging until a real channel bridge and matching manifest support land.
For more information, refer to [Messaging Channels](../manage-sandboxes/messaging-channels) and [Platform Support and Launch Claims](../reference/platform-support).
- Agent-specific CLI commands now route more accurately and fail earlier with clearer local guidance.
`sessions export` routes by sandbox agent kind, Hermes `gateway-token` points users to `dashboard-url`, bare OpenClaw `agent` invocations print wrapper help locally, and omitted extra-agent `workspace` and `agentDir` fields use canonical OpenClaw paths.
Expand Down
3 changes: 2 additions & 1 deletion docs/reference/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ When the policy or phase changes during a session, the plugin sends a smaller up
The context tells the agent to try allowed network and filesystem operations before reporting them unavailable, and to distinguish policy denials from DNS, timeout, TLS, or filesystem errors.

The Hermes integration follows the generic agent-manifest path instead of the OpenClaw plugin package path.
The manifest declares Hermes' binary, health probe, config directory, state directories, messaging support, and OpenAI-compatible API endpoint.
The manifest declares Hermes' binary, health probe, config directory, state directories, and OpenAI-compatible API endpoint.
Messaging channel availability is declared by each channel manifest's `supportedAgents` list under `src/lib/messaging/channels/`, not by the Hermes agent manifest.
The build-time config generator turns NemoClaw onboarding choices into Hermes YAML and environment files, and the Hermes plugin manifest exposes NemoClaw tools and an `on_session_start` hook.

## NemoClaw Blueprint
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/platform-support.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ NemoClaw configures messaging channels during onboarding. The OpenShell gateway
| Discord | Tested | Configured through an OpenShell-managed channel during onboarding. Sandbox egress allowed by the `discord` policy preset. |
| Telegram | Tested | Configured through an OpenShell-managed channel during onboarding. |
| WeChat | Tested with limitations | Channel hook available. Verify regional account access before relying on this path. |
| WhatsApp | Tested with limitations | Supported by both OpenClaw and Hermes (see `messaging_platforms` in `agents/openclaw/manifest.yaml` and `agents/hermes/manifest.yaml`). Pairing happens in the sandbox through WhatsApp Web by scanning a QR code at first run; the Hermes flow exposes this as `hermes whatsapp` and persists session credentials under `~/.hermes/platforms/whatsapp/session` (`agents/hermes/manifest.yaml:69-71`). Sandbox egress goes through the `whatsapp` policy preset, which carries the WebSocket / Noise / h1-ALPN caveats documented in `nemoclaw-blueprint/policies/presets/whatsapp.yaml`. No Meta Business API integration today; that path is out of scope for this matrix. |
| WhatsApp | Tested with limitations | Supported by both OpenClaw and Hermes through the channel manifest `supportedAgents` declaration in `src/lib/messaging/channels/whatsapp/manifest.ts`. Pairing happens in the sandbox through WhatsApp Web by scanning a QR code at first run; the Hermes flow exposes this as `hermes whatsapp` and persists session credentials under `~/.hermes/platforms/whatsapp/session` (`agents/hermes/manifest.yaml:69-71`). Sandbox egress goes through the `whatsapp` policy preset, which carries the WebSocket / Noise / h1-ALPN caveats documented in `nemoclaw-blueprint/policies/presets/whatsapp.yaml`. No Meta Business API integration today; that path is out of scope for this matrix. |
| Microsoft Teams | Experimental | Supported by both OpenClaw and Hermes through the manifest-first messaging channel contract. Requires Bot Framework app credentials, a tenant ID, and a public HTTPS endpoint that reaches the sandbox webhook path `/api/messages`. Sandbox egress goes through the `teams` policy preset, and only one active Teams sandbox can use a given local `MSTEAMS_PORT` forward. |
{/* integration-status:end */}

Expand Down
4 changes: 0 additions & 4 deletions src/lib/actions/sandbox/channel-status.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ const PROBED_AT = new Date("2026-05-28T04:00:00.000Z");
function fakeAgent(name: "openclaw" | "hermes" = "openclaw"): AgentDefinition {
const configDir = name === "openclaw" ? "/sandbox/.openclaw" : "/sandbox/.hermes";
const stateDirs = name === "openclaw" ? ["whatsapp"] : ["platforms"];
const messagingPlatforms = ["telegram", "discord", "slack", "wechat", "whatsapp", "teams"];
return {
name,
agentDir: `/fake/${name}`,
Expand Down Expand Up @@ -89,9 +88,6 @@ function fakeAgent(name: "openclaw" | "hermes" = "openclaw"): AgentDefinition {
get phoneHomeHosts() {
return [];
},
get messagingPlatforms() {
return messagingPlatforms;
},
get dockerfileBasePath() {
return null;
},
Expand Down
15 changes: 13 additions & 2 deletions src/lib/actions/sandbox/channel-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ import {
collectBuiltInMessagingChannelDiagnostics,
type MessagingChannelDiagnosticSpec,
} from "../../messaging/diagnostics";
import {
createBuiltInChannelManifestRegistry,
getMessagingManifestAvailabilityContext,
} from "../../messaging";
import * as policies from "../../policy";
import {
type DiagnosticSeverity,
Expand Down Expand Up @@ -95,6 +99,7 @@ export type ChannelStatusReport =
// channels status from inheriting that hang.
const WHATSAPP_PROBE_TIMEOUT_MS = 8_000;
const CHANNEL_STATUS_DIAGNOSTICS = collectBuiltInMessagingChannelDiagnostics();
const channelManifestRegistry = createBuiltInChannelManifestRegistry();

const SHELL_OK = "NEMOCLAW_WA_DIAG_OK";
const HEARTBEAT_BEGIN = "NEMOCLAW_WA_HEARTBEAT_BEGIN";
Expand Down Expand Up @@ -507,11 +512,11 @@ function buildBasicChannelReport(
});
// Reference the agent in a hint so the deep-diagnostic section is
// discoverable per agent without needing extra plumbing.
if (!agent.messagingPlatforms.includes(channelName)) {
if (!channelSupportedByAgent(channelName, agent)) {
signals.unshift({
label: "Agent support",
severity: "warn",
detail: `agent '${agent.name}' does not declare support for ${channelName}`,
detail: `channel '${channelName}' does not support agent '${agent.name}'`,
});
}
return {
Expand All @@ -523,6 +528,12 @@ function buildBasicChannelReport(
};
}

function channelSupportedByAgent(channelName: string, agent: AgentDefinition): boolean {
return channelManifestRegistry
.listAvailable(getMessagingManifestAvailabilityContext(agent, channelManifestRegistry.list()))
.some((manifest) => manifest.id === channelName);
}

/**
* Run the WhatsApp diagnostic or a thin per-channel summary for the named
* sandbox. The function never throws: any unexpected condition is rendered
Expand Down
Loading
Loading