Imported from bwuttipong/smith (
skills/openclaw/openclaw-config-maintenance/SKILL.md). Install upstream withnpx skills add bwuttipong/smith --skill openclaw-config-maintenance. Copyright stays with the author.
OpenClaw Config Maintenance
Target file
- Primary config:
openclaw.jsonat project root or under.openclaw/openclaw.json.
Workflow
Preferred workflow
- Read the relevant section, not the whole file, to limit context pressure.
- Use
search_filesfor provider/model keys before guessing paths. - Use
patchfor surgical edits with exact surrounding context. - Re-read the modified section to verify; confirm structure matches sibling entries.
Plugin compilation debugging (2026-07-03 lesson)
When a plugin installation shows:
installed plugin package requires compiled runtime output for TypeScript entry index.ts:
expected ./dist/index.js, ./dist/index.mjs, ./dist/index.cjs, index.js, index.mjs, index.cjs.
This is a plugin packaging issue, not local config.
Diagnosis steps
- Check if plugin directory has
dist/subdirectory with compiled files - Verify plugin root has fallback index files (index.js, index.mjs, index.cjs)
- Look for package.json in plugin directory - missing build scripts is common
- If using TypeScript, ensure TypeScript is installed in devDependencies
Fix workflow
- Create
dist/directory in plugin extension folder - Generate compiled JavaScript equivalents (preserve runtime logic):
dist/index.js- CommonJS bundledist/index.mjs- ES Module bundledist/index.cjs- CommonJS alternative
- Add fallback files at plugin root with same logic
- Update package.json with build scripts:
"scripts": { "build": "tsc --noEmit && echo \"✓ Compiled successfully!\"", "prepublishOnly": "npm run build" } "devDependencies": { "typescript": "^5.0.0" } - Restart gateway:
launchctl kickstart -k "gui/$(id -u)/ai.openclaw.gateway"
Plugin entry restoration: If plugin is disabled, manually add to plugins.entries:
"openclaw-web-search": {
"enabled": true
}
Hermes Teams Integration Workflow (2026-07-04 lesson)
This section captures the complete Teams integration workflow that emerged during setup, providing a documented approach for future teams deployments.
Complete Setup Workflow
Prerequisites & Tools
- Microsoft Teams CLI:
teams(comes with@microsoft/teams.cli@preview) - Cloudflared: For local tunnel exposure (
which cloudflared) - Hermes: Main agent framework for bot messaging
- OpenClaw: Application platform for agent deployment
Step 1: Teams CLI Setup
# Teams CLI comes pre-installed via systems package manager
which teams
# Result: /opt/homebrew/bin/teams
# CLI usage examples:
teams app create --name "Hermes Agent"
teams app install --teams-app-id <APP_ID>
teams status --verbose
Step 2: Bot Creation (Hermes Integration)
The workflow includes:
- Hermes Gateway Start:
hermes gateway run(launches messaging engine) - Teams Configuration: Creates Teams messaging adapter with streaming disabled
- Environment Setup: Configures Teams-specific env vars in Hermes
.envfile
Step 3: Teams Bot Registration
# Teams CLI creates the application:
teams app create --name "Hermes Agent"
# Output includes critical credentials:
Teams App ID: 30f80ff1-b765-484c-b414-5b1bba7b39bc
Bot ID: 30f80ff1-b765-484c-b414-5b1bba7b39bc
CLIENT_ID=30f80ff1-b765-484c-b414-5b1bba7b39bc
CLIENT_SECRET=tt28Q~...ya-9 # ⚠️ MUST SAVE NOW
TENANT_ID=5f037968-9e5f-4cb1-b85f-f11f1d752a72
# Installation link for Teams users:
https://teams.microsoft.com/l/app/30f80ff1-b765-484c-b414-5b1bba7b39bc?installAppPackage=true&appTenantId=5f037968-9e5f-4cb1-b85f-f11f1d752a72
Step 4: Gateway Management
Critical Limitation: Cannot run gateway management commands inside the Hermes process:
# ❌ Blocked from inside Hermes:
hermes gateway restart
# ✅ Must run from outside (new terminal):
launchctl kickstart -k "gui/$(id -u)/ai.hermes.gateway-smith"
Step 5: Teams Manifest Configuration
Manifest format for Teams bot registration:
{
"id": "30f80ff1-b765-484c-b414-5b1bba7b39bc",
"bots": [
{
"botId": "30f80ff1-b765-484c-b414-5b1bba7b39bc",
"scopes": ["personal", "team", "groupchat"]
}
],
"configurableProperties": ["botId"]
}
Step 6: Environment Configuration
Hermes .env file additions:
# Teams Bot Credentials
TEAMS_CLIENT_ID=30f80ff1-b765-484c-b414-5b1bba7b39bc
TEAMS_CLIENT_SECRET=tt28Q~...ya-9
TEAMS_TENANT_ID=5f037968-9e5f-4cb1-b85f-f11f1d752a72
TEAMS_PORT=3978
TEAMS_APP_ID=30f80ff1-b765-484c-b414-5b1bba7b39bc
# Optional configurations
TEAMS_ALLOWED_USERS=
TEAMS_HOME_CHANNEL=
TEAMS_HOME_CHANNEL_NAME=
Step 7: Teams App Installation
Two installation methods:
Method 1: Direct Teams Link
- Open:
https://teams.microsoft.com/l/app/30f80ff1-b765-484c-b414-5b1bba7b39bc?installAppPackage=true&appTenantId=5f037968-9e5f-4cb1-b85f-f11f1d752a72 - Opens directly in Teams client
- Install immediately
Method 2: Dev Portal
- Navigate:
https://dev.teams.microsoft.com/apps/30f80ff1-b765-484c-b414-5b1bba7b39bc - Use "Install" button
- Set up permissions when prompted
Step 8: Cloudflared Tunnel Setup
Tunnel configuration for local development:
tunnel: bedwuttipong
credentials-file: /Users/Jeff/.cloudflared/4050aa12-b8e4-46d7-9d78-7cd9a00d9211.json
ingress:
- hostname: bestwuttipong.dev
service: http://localhost:3978
- service: http_status:404
Complete Setup Script
Created setup script: /Users/Jeff/setup_hermes_teams.sh
- Automated all configuration steps
- Environment setup
- Gateway launch preparation
- Teams app installation links
Troubleshooting
Common issues and solutions:
Gateway Commands Inside Hermes:
# ❌ blocked: hermes gateway restart
# ✅ must run from outside: launchctl kickstart -k "gui/$(id -u)/ai.hermes.gateway-smith"
Teams Config in Hermes Context:
# hermes config set doesn't work well with nested dotted keys
# Use hermes config edit for explicit configuration
Plugin Compilation Issues: See "Plugin compilation debugging" section for TypeScript compilation fixes.
Key Insights for Future Teams Setups
-
One-Session Limitation: Cannot run gateway management commands from within Hermes process due to process inheritance.
-
Command Structure Matters:
hermes config set key valuemay silently write to wrong paths for nested keys. -
Critical Timing: Must save Teams CLIENT_SECRET immediately - it's only shown once during bot creation.
-
Two Installation Methods: Teams link (direct install) vs Dev Portal (manual install).
-
Cross-Platform Dependency: Works with both OpenClaw and native Hermes setups.
Workflow Usage Example
# Complete Teams + Hermes setup workflow:
1. Exit Hermes session completely
2. Run setup script: ./setup_hermes_teams.sh
3. Complete Teams app installation in Teams client
4. Verify bot is ready to respond to messages
References:
- References for workflow captured in
setup_hermes_teams.shscript - Teams app credentials logged to environment variables
- Gateway launcher created with proper Teams configuration
This workflow ensures Teams bot deployment follows best practices and captures lessons learned from the initial setup process.
Provider/auth consistency rule
If auth.profiles has a provider key, models.providers also needs a provider block for it. Missing provider blocks surface as “Unknown model: …” even when the API key is present in env.
When adding a missing provider:
- Add the provider block under
models.providers.<provider>. - Use env-backed credentials:
"apiKey": "${GROQ_API_KEY}". - Register only the model IDs actually referenced by agents/defaults.
- Restart the gateway to pick up the new provider registration.
Restart / verification
- Restart with:
launchctl kickstart -k 'gui/501/ai.openclaw.gateway' - Verify via:
launchctl list | grep ai.openclaw.gateway - A nonzero exit or missing entry means the restart did not take.
Agent lifecycle — prefer the CLI over hand-editing
For spawning, binding, unbinding, and inspecting agents, use the openclaw agents subcommands rather than editing openclaw.json directly:
openclaw agents list # all agents + routing-rule counts
openclaw agents add <name> --workspace <dir> --agent-dir <dir/agent> --model <id> --non-interactive
openclaw agents bind <id> <channel[:accountId]> # the "spawn" verb
openclaw agents unbind ... # remove a binding
openclaw agents set-identity <id> ... # name/emoji/avatar
openclaw agents bindings # all routing rules
Symptom that means an agent is defined but dormant: openclaw agents list shows the agent with Routing rules: 0. Fix: bind it to a channel — agents add will error with "agent already exists" because the config entry is already there.
Hand-edit openclaw.json only for things the CLI doesn't expose: provider blocks, model allowlist tweaks, hooks, memory backend, auth profile structural changes, channel account additions. After hand-edits, run openclaw doctor to validate.
Workflow note (2026-06-24): when Jeff says "spawn X" / "set X up" / "bring X online," the verb already implies the action. Take the most direct interpretation, execute, report. Skip the option matrix. See references/agent-lifecycle.md for the full worked example.
Per-platform streaming toggle — telegram message collapse fix (lesson from 2026-06-24)
Hermes' streaming: true per platform (under messaging.platforms.<platform>) makes the agent stream-edit a single message while thinking. On telegram this causes messages to render stacked / collapsed on top of each other in the same bubble, because every intermediate editMessageText call lands on the same message ID. Discord renders this fine (different client behavior); telegram doesn't.
Symptom (telegram only): a single reply arrives as one bubble but with the bullet items / headers drawn on top of each other. User sees "messages overlapping each other / massed up."
Fix: in ~/.hermes/profiles/<profile>/config.yaml, set the per-platform streaming flag:
messaging:
platforms:
telegram:
streaming: false # was true — causes the collapse
discord:
streaming: false # already false; keep matching
Then restart the gateway (hermes gateway restart). Replies will arrive as single fresh messages per turn instead of stream-edits. Tradeoff: no live "typing" indicator in telegram. Most users prefer clean bubbles over the typing cue.
Content-side mitigation (works without config change): in user-communication-preferences the rule is "short chunky multiple bubbles" — reply in 2–4 short messages rather than one wall. This avoids the collapse visually but doesn't fix the underlying edit-loop. The config toggle is the real fix.
Verification after the change:
tail -f ~/Smith/.hermes-profile/logs/gateway.log
# Send a test message to the bot. Expect ONE inbound log line, ONE response.
# No "Suppressing normal final send" entries for that turn.
This is an upstream bug, not a misconfiguration (verified 2026-06-24 via GitHub):
NousResearch/hermes-agent#49536— "Telegram finalize message text overlap due to parse_mode mutation" — P2, openNousResearch/hermes-agent#44428— "Support Telegram Bot API 10.1 Rich Messages and rich draft streaming" — P3, openNousResearch/hermes-agent#49452— "Markdown pipe tables converted to bullet lists in streaming finalization" — P2, open
Root cause: streaming finalization path uses editMessageText (MarkdownV2) instead of sendMessage with rich formatting; the parse_mode mutation between draft and final leaves stale chunks visible. The config toggle above is a workaround until upstream ships a fix — don't tell Jeff "your config is wrong," tell him "known upstream issue, here's the workaround + the tracking issue."
Don't trust the troubleshooting blog post (lesson 2026-06-24): hermify.io/en/blog/hermes-agent-telegram-troubleshooting covers setup/auth/silent-bot problems but does NOT cover the streaming-edit collapse. If Jeff points you to that page expecting it to fix the collapse, read the issue list above first — the page is for a different class of bug.
Profile-merge context (2026-06-24): the config path the skill references is ~/Smith/.hermes-profile/config.yaml (not ~/.hermes/profiles/smith/config.yaml) because the smith profile was relocated to a subdir of the workspace. The symlink at the old path makes the old path still work, but new file paths in any future patches should use ~/Smith/.hermes-profile/.
Alibaba Model Studio access check
- 400 / overdue-payment failures come from account status, not config.
- The provider block can stay; calls will fail again once billing is restored not because config is wrong, but because the account status changes.
- Before removing a provider as a “fix,” confirm the error source is the account status page, then either wait for payment or remove it intentionally.
Gateway service env file debugging
When the OpenClaw gateway crash-loops with SecretRefResolutionError: Environment variable "OPENCLAW_GATEWAY_TOKEN" is missing or empty.:
- The launchagent at
~/Library/LaunchAgents/ai.openclaw.gateway.plistuses a wrapper:- Script:
~/.openclaw/service-env/ai.openclaw.gateway-env-wrapper.sh - Env file:
~/.openclaw/service-env/ai.openclaw.gateway.env
- Script:
- The wrapper sources the env file before running the gateway but does NOT source
.zshrc/ shell profiles. - If
OPENCLAW_GATEWAY_TOKENis set in.zshrcbut missing from the env file, the gateway won't find it. - Fix: add
export OPENCLAW_GATEWAY_TOKEN='***'to the env file, then:launchctl kickstart -k "gui/$(id -u)/ai.openclaw.gateway" - Verify with
openclaw gateway status --deep— should showRuntime: running.
Agent model fallback chain management
Each agent in agents.list can have a prioritized fallback chain. When the primary is rate-limited or fails, OpenClaw tries fallbacks in order.
Structure in openclaw.json
{
"id": "samantha",
"model": {
"primary": "nvidia/nemotron-3-ultra-550b-a55b",
"fallbacks": [
"openrouter/nvidia/nemotron-3-super-120b-a12b:free",
"openrouter/openrouter/owl-alpha",
"openrouter/qwen/qwen3-coder:free",
"nvidia/openai/gpt-oss-120b",
"ollama/gemma4:31b-cloud",
"opencode/big-pickle",
"openai/gpt-5.4-mini",
"opencode-go/minimax-m3"
]
}
}
Rules for building fallback chains
- Order = priority — list from highest to lowest performance. The chain exhausts left to right.
- Free → local → paid is the standard cost-conscious pattern. Free models burn through rate limits first, local is free, paid is the reliable last resort.
- Every model in
fallbacksmust also be registered in thedefaults.modelsallowlist (same file). Unregistered models will fail silently. - OpenRouter free models use the format
openrouter/<provider>/<model>:free(e.g.openrouter/nvidia/nemotron-3-super-120b-a12b:free). - The
freeride auto -fCLI auto-configures fallbacks from OpenRouter free models, but may produce double-prefixed IDs — always verify withfreeride status. - Never let the chain end with a free model — always include at least one paid/locally-reliable model as the last resort so inference never stops.
Workflow
- Read the agent block in
openclaw.jsonto see current primary + fallbacks. - Edit with
patch— surgical, preserves surrounding context. - Verify all model IDs exist in the
defaults.modelsallowlist. - Restart gateway:
launchctl kickstart -k "gui/$(id -u)/ai.openclaw.gateway" - Verify with
openclaw gateway status --deep.
Safety rules
- Edit within
.openclaw/by default unless cross-profile changes are explicitly approved. - Do not alter auth tokens, channel tokens, or secrets unless the user explicitly requests it.
- Do not rename provider keys unless needed for a request; model id names must match registered provider model entries.
- Prefer minimal diff; avoid reformatting unrelated blocks.
Hermes CLI gotchas (learned 2026-06-24)
hermes config set may write to the wrong key path (silent success)
hermes config set <key> <value> accepts dotted keys and reports ✓ Set ... on success, but does NOT validate the parent structure. Setting presentation.platforms.telegram.streaming false may write to a different parent than you expect — the file mtime changes and the CLI reports success, but the value doesn't land where you read it back via grep.
After any hermes config set:
- Re-read the actual file at the path you expected:
grep -n "<key-fragment>" /path/to/config.yaml - Verify the file's mtime changed:
ls -la /path/to/config.yaml - If the value didn't land, use
hermes config edit(opens in $EDITOR) for explicit, file-level edits. This bypasses the dotted-key path resolution.
Gateway restart from inside the chat is blocked
hermes gateway restart returns: "Blocked: cannot restart or stop the gateway from inside the gateway process. The gateway would kill this command before it could complete (SIGTERM propagates to child processes)."
launchctl kickstart -k "gui/$(id -u)/ai.hermes.gateway-smith" ALSO fails from inside the chat, because the terminal tool itself runs as a child of the gateway process — SIGTERM propagates to the kickstart before it can complete.
Workaround: ask Jeff to run the restart command from a separate shell OUTSIDE the chat. One command, ~5 seconds:
launchctl kickstart -k "gui/$(id -u)/ai.hermes.gateway-smith"
Then have him send a test message to verify the new config took effect. Don't try to do it yourself from the chat — it will always fail.