Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
afba216
refactor(policy): move messaging policies into channels
sandl99 Jul 1, 2026
2ab2f48
docs(policy): remove messaging policy flow note
sandl99 Jul 1, 2026
d97bbae
Merge branch 'main' into feat/messaging-channel-policies
sandl99 Jul 1, 2026
64b06a3
test(policy): avoid conditional in channel policy test
sandl99 Jul 1, 2026
92d4284
fix(policy): harden channel preset agent resolution
sandl99 Jul 1, 2026
05c44ac
test(policy): cover channel preset negative paths
sandl99 Jul 1, 2026
678f952
test(policy): stabilize channel preset fixtures
sandl99 Jul 1, 2026
a51eae2
Merge remote-tracking branch 'origin/main' into feat/messaging-channe…
sandl99 Jul 2, 2026
cb23ca4
fix(policy): hide unsupported messaging presets
sandl99 Jul 2, 2026
65c80ae
fix(policy): use Hermes presets for inferred policy path
sandl99 Jul 2, 2026
ad8800e
merge: main into messaging channel policies
sandl99 Jul 2, 2026
56b78ea
Revert "fix(policy): reject messaging channel presets on terminal-run…
sandl99 Jul 2, 2026
9bc89c9
test(policy): avoid conditional in initial policy regression
sandl99 Jul 2, 2026
b7eb0cd
fix(policy): scope setup presets to sandbox agent
sandl99 Jul 2, 2026
f11f99c
test(policy): keep setup regression under size budget
sandl99 Jul 2, 2026
aa394f7
Merge branch 'main' into feat/messaging-channel-policies
sandl99 Jul 3, 2026
ab75275
Merge branch 'main' into feat/messaging-channel-policies
sandl99 Jul 3, 2026
1e8f9ac
fix(policy): explain unsupported messaging presets
sandl99 Jul 3, 2026
c56fa2d
chore(policy): format unsupported preset test
sandl99 Jul 3, 2026
8729a10
Revert "chore(policy): format unsupported preset test"
sandl99 Jul 3, 2026
3674607
Revert "fix(policy): explain unsupported messaging presets"
sandl99 Jul 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 0 additions & 228 deletions agents/hermes/policy-additions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -149,231 +149,3 @@ network_policies:
- { path: /usr/local/bin/curl }
- { path: /usr/bin/python3* }
- { path: /opt/hermes/.venv/bin/python }

# ── Messaging policy templates ─────────────────────────────────
# These entries are agent-specific channel templates. During sandbox
# creation, NemoClaw filters out entries for messaging channels that were not
# selected, so a Discord-only Hermes sandbox does not retain Telegram, Slack,
# or WeChat egress.
telegram:
name: telegram
endpoints:
- host: api.telegram.org
port: 443
protocol: rest
enforcement: enforce
rules:
- allow: { method: GET, path: "/bot*/**" }
- allow: { method: POST, path: "/bot*/**" }
- allow: { method: GET, path: "/file/bot*/**" }
binaries:
- { path: /usr/local/bin/node }
- { path: /usr/bin/python3* }
- { path: /opt/hermes/.venv/bin/python }

discord:
name: discord
endpoints:
- host: discord.com
port: 443
protocol: rest
enforcement: enforce
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: POST, path: "/**" }
- allow: { method: GET, path: "/gateway*" }
- allow: { method: GET, path: "/api/v*/gateway/bot" }
- allow: { method: GET, path: "/api/v*/applications/@me" }
- allow: { method: PUT, path: "/api/v*/applications/*/commands" }
- allow: { method: PUT, path: "/api/v*/channels/*/messages/*/reactions/*/@me" }
- allow: { method: PATCH, path: "/api/v*/applications/*" }
- allow: { method: PATCH, path: "/api/v*/applications/*/commands/*" }
- allow: { method: PATCH, path: "/api/v*/channels/*/messages/*" }
- allow: { method: PATCH, path: "/api/v*/webhooks/*/*/messages/*" }
- allow: { method: DELETE, path: "/api/v*/applications/*/commands/*" }
- allow: { method: DELETE, path: "/api/v*/channels/*/messages/*" }
- allow: { method: DELETE, path: "/api/v*/channels/*/messages/*/reactions/*/*" }
- allow: { method: DELETE, path: "/api/v*/webhooks/*/*/messages/*" }
- host: gateway.discord.gg
port: 443
protocol: websocket
enforcement: enforce
websocket_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: WEBSOCKET_TEXT, path: "/**" }
- host: "*.discord.gg"
port: 443
protocol: websocket
enforcement: enforce
websocket_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: WEBSOCKET_TEXT, path: "/**" }
- host: cdn.discordapp.com
port: 443
protocol: rest
enforcement: enforce
rules:
- allow: { method: GET, path: "/**" }
binaries:
- { path: /usr/local/bin/node }
- { path: /usr/bin/python3* }
- { path: /opt/hermes/.venv/bin/python }

slack:
name: slack
endpoints:
- host: slack.com
port: 443
protocol: rest
enforcement: enforce
request_body_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: POST, path: "/**" }
- host: api.slack.com
port: 443
protocol: rest
enforcement: enforce
request_body_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: POST, path: "/**" }
- host: hooks.slack.com
port: 443
protocol: rest
enforcement: enforce
request_body_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: POST, path: "/**" }
- host: wss-primary.slack.com
port: 443
protocol: websocket
enforcement: enforce
websocket_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: WEBSOCKET_TEXT, path: "/**" }
- host: wss-backup.slack.com
port: 443
protocol: websocket
enforcement: enforce
websocket_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: WEBSOCKET_TEXT, path: "/**" }
binaries:
- { path: /usr/local/bin/hermes }
- { path: /usr/bin/python3* }
- { path: /opt/hermes/.venv/bin/python }

teams:
name: teams
endpoints:
- host: login.microsoftonline.com
port: 443
protocol: rest
enforcement: enforce
request_body_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: POST, path: "/**" }
- host: login.botframework.com
port: 443
protocol: rest
enforcement: enforce
request_body_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: POST, path: "/**" }
- host: api.botframework.com
port: 443
protocol: rest
enforcement: enforce
request_body_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: POST, path: "/**" }
# The SDK follows Bot Connector serviceUrl values from inbound Teams
# activities, so this host remains method-scoped while Graph/media hosts
# stay read-only.
- host: smba.trafficmanager.net
port: 443
protocol: rest
enforcement: enforce
request_body_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: POST, path: "/**" }
- allow: { method: PUT, path: "/**" }
- allow: { method: DELETE, path: "/**" }
- host: graph.microsoft.com
port: 443
protocol: rest
enforcement: enforce
request_body_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- host: teams.microsoft.com
port: 443
protocol: rest
enforcement: enforce
rules:
- allow: { method: GET, path: "/**" }
- host: teams.cdn.office.net
port: 443
protocol: rest
enforcement: enforce
rules:
- allow: { method: GET, path: "/**" }
- host: statics.teams.cdn.office.net
port: 443
protocol: rest
enforcement: enforce
rules:
- allow: { method: GET, path: "/**" }
- host: "*.sharepoint.com"
port: 443
protocol: rest
enforcement: enforce
rules:
- allow: { method: GET, path: "/**" }
- host: 1drv.ms
port: 443
protocol: rest
enforcement: enforce
rules:
- allow: { method: GET, path: "/**" }
binaries:
- { path: /usr/local/bin/hermes }
- { path: /usr/bin/python3* }
- { path: /opt/hermes/.venv/bin/python }

# WeChat (personal) via Tencent's iLink Bot API. The Hermes adapter uses
# HTTP long-polling (no WebSocket). WEIXIN_TOKEN is L7-resolved at egress
# from WECHAT_BOT_TOKEN (same credential slot OpenClaw's bridge uses) via
# manifest hook render outputs. See nemoclaw-blueprint/policies/presets/wechat.yaml
# for the shared host set.
wechat_bridge:
name: wechat_bridge
endpoints:
- host: ilinkai.weixin.qq.com
port: 443
protocol: rest
enforcement: enforce
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: POST, path: "/**" }
- host: ilinkai.wechat.com
port: 443
protocol: rest
enforcement: enforce
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: POST, path: "/**" }
binaries:
- { path: /usr/local/bin/hermes }
- { path: /usr/bin/python3* }
- { path: /opt/hermes/.venv/bin/python }
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 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."
"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 `src/lib/messaging/channels/whatsapp/policy/openclaw.yaml` and `src/lib/messaging/channels/whatsapp/policy/hermes.yaml`. No Meta Business API integration today; that path is out of scope for this matrix."
},
{
"name": "Microsoft Teams",
Expand Down
2 changes: 1 addition & 1 deletion ci/test-file-size-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
"test/onboard-messaging.test.ts": 2062,
"test/onboard-selection.test.ts": 6867,
"test/onboard.test.ts": 4774,
"test/policies.test.ts": 2475
"test/policies.test.ts": 2332
}
}
1 change: 1 addition & 0 deletions docs/network-policy/customize-network-policy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ For guided post-install examples, refer to [Common Integration Policy Examples](

During onboarding, the [policy tier](../reference/network-policies#policy-tiers) you select determines which presets are enabled by default.
You can add or remove individual presets in the interactive preset screen that follows tier selection.
Built-in preset choices are scoped to the sandbox's active agent, so unsupported messaging channel presets do not appear in `policy-list` or the interactive `policy-add` picker for agents without matching channel policy files.

Available presets:

Expand Down
1 change: 1 addition & 0 deletions docs/network-policy/integration-policy-examples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ An approval updates the running policy, but it does not create a reviewable Nemo
## Supported Integration Presets

NemoClaw ships maintained policy presets for common services in `nemoclaw-blueprint/policies/presets/`.
Messaging channel presets are scoped to the sandbox's active agent; if an agent does not have a matching channel policy, that channel preset is omitted from `policy-list` and `policy-add <preset>` reports it as unknown.

| Workflow | Preset |
|----------|--------|
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/commands-nemohermes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -828,8 +828,7 @@ nemohermes my-assistant policy-add pypi --yes
The positional form is required in scripted workflows.
Set `NEMOCLAW_NON_INTERACTIVE=1` instead of `--yes` if you want the same behavior from an environment variable.
If the preset name is unknown or already applied, the command exits non-zero with a clear error.
Messaging channel presets such as `telegram`, `discord`, `slack`, `wechat`, and `whatsapp` apply only to agents that support those channels.
On a terminal-runtime agent such as DeepAgents, which has no inbound messaging gateway, `policy-add` rejects the preset with a clear error before any endpoint disclosure or prompt, matching `channels add`.
Built-in preset choices are scoped to the sandbox's active agent. Messaging channel presets appear only when NemoClaw has a matching channel policy for that agent; unavailable channel presets use the standard unknown-preset error before endpoint preview or confirmation.
Custom preset files are tracked with the sandbox that applied them.
`policy-list`, `policy-add`, and `policy-remove` compare the local registry and live gateway state using that sandbox-scoped preset metadata, so custom presets do not appear missing just because they are not part of the built-in preset catalog.
Before `policy-add` writes a merged policy, it reads and parses the current live policy from OpenShell.
Expand Down Expand Up @@ -869,6 +868,7 @@ Custom presets bypass the built-in preset review process and can widen sandbox e
### `nemohermes <name> policy-list`

List available policy presets and show which ones are applied to the sandbox.
The available built-in rows are scoped to the sandbox's active agent, so unsupported messaging channel policies are not listed for agents without matching channel policy files.
The command cross-references the local registry against the live gateway state (via `openshell policy get`), so it flags presets that are applied in one place but not the other.
This catches desync caused by external edits to the gateway policy or stale registry entries after a manual rollback.
Preset summaries come only from the YAML `preset.description` field.
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1132,8 +1132,7 @@ $$nemoclaw my-assistant policy-add pypi --yes
The positional form is required in scripted workflows.
Set `NEMOCLAW_NON_INTERACTIVE=1` instead of `--yes` if you want the same behavior from an environment variable.
If the preset name is unknown or already applied, the command exits non-zero with a clear error.
Messaging channel presets such as `telegram`, `discord`, `slack`, `wechat`, and `whatsapp` apply only to agents that support those channels.
On a terminal-runtime agent such as DeepAgents, which has no inbound messaging gateway, `policy-add` rejects the preset with a clear error before any endpoint disclosure or prompt, matching `channels add`.
Built-in preset choices are scoped to the sandbox's active agent. Messaging channel presets appear only when NemoClaw has a matching channel policy for that agent; unavailable channel presets use the standard unknown-preset error before endpoint preview or confirmation.
Custom preset files are tracked with the sandbox that applied them.
`policy-list`, `policy-add`, and `policy-remove` compare the local registry and live gateway state using that sandbox-scoped preset metadata, so custom presets do not appear missing just because they are not part of the built-in preset catalog.
Before `policy-add` writes a merged policy, it reads and parses the current live policy from OpenShell.
Expand Down Expand Up @@ -1173,6 +1172,7 @@ Custom presets bypass the built-in preset review process and can widen sandbox e
### `$$nemoclaw <name> policy-list`

List available policy presets and show which ones are applied to the sandbox.
The available built-in rows are scoped to the sandbox's active agent, so unsupported messaging channel policies are not listed for agents without matching channel policy files.
The command cross-references the local registry against the live gateway state (via `openshell policy get`), so it flags presets that are applied in one place but not the other.
This catches desync caused by external edits to the gateway policy or stale registry entries after a manual rollback.
Preset summaries come only from the YAML `preset.description` field.
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/network-policies.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ The baseline policy is always applied regardless of the selected tier.
| Open | `npm`, `pypi`, `huggingface`, `brew`, `brave when supported`, `weather`, `public-reference`, `slack`, `discord`, `telegram`, `wechat` (experimental), `whatsapp` (experimental), `jira`, `outlook` | Broad access across third-party services including messaging, productivity, weather, and public-reference APIs. |

After selecting a tier, a combined preset and access-mode screen lets you include or exclude individual presets and toggle each between read (GET only) and read-write (GET + POST/PUT/PATCH) access.
Tier-default presets are pre-selected; additional presets can be added from the full list.
NemoClaw filters tier defaults by the active agent's supported integrations.
Tier-default presets are pre-selected; additional presets can be added from the built-in preset list available to the sandbox's active agent.
NemoClaw filters tier defaults and built-in preset choices by the active agent's supported integrations.
For example, Hermes onboarding omits the Brave Search preset because Hermes does not use NemoClaw's OpenClaw web-search configuration.
Hermes managed-tool gateway selections can add Hermes-specific presets, such as Nous-hosted web, image, audio, browser, or code tools, without applying unsupported OpenClaw-only presets.
OpenClaw onboarding also adds the `openclaw-pricing` preset on top of tier defaults so session-cost records can populate from LiteLLM and OpenRouter without manual configuration.
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 @@ -117,7 +117,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 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. |
| 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 `src/lib/messaging/channels/whatsapp/policy/openclaw.yaml` and `src/lib/messaging/channels/whatsapp/policy/hermes.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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
".version",
"bin/",
"dist/",
"src/lib/messaging/channels/**/policy/*.{yaml,yml}",
"nemoclaw/dist/",
"nemoclaw/openclaw.plugin.json",
"nemoclaw/package.json",
Expand Down
2 changes: 1 addition & 1 deletion schemas/policy-preset.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/NVIDIA/NemoClaw/schemas/policy-preset.schema.json",
"title": "NemoClaw Policy Preset",
"description": "Schema for policy presets (nemoclaw-blueprint/policies/presets/*.yaml) — named network policy bundles that can be merged into the base sandbox policy.",
"description": "Schema for policy presets (nemoclaw-blueprint/policies/presets/*.yaml and src/lib/messaging/channels/*/policy/*.yaml) — named network policy bundles that can be merged into the base sandbox policy.",
"type": "object",
"required": ["preset", "network_policies"],
"additionalProperties": false,
Expand Down
Loading
Loading