Skip to content

feat: generalized channel config + openclaw passthrough#38

Merged
TimBeyer merged 12 commits intomainfrom
feat/generalized-channels
Apr 7, 2026
Merged

feat: generalized channel config + openclaw passthrough#38
TimBeyer merged 12 commits intomainfrom
feat/generalized-channels

Conversation

@TimBeyer
Copy link
Copy Markdown
Owner

@TimBeyer TimBeyer commented Apr 7, 2026

Summary

  • ChannelDef system: Data-driven channel definitions (like CapabilityDef) that drive validation, wizard rendering, bootstrap, and secret sanitization from a single source. Ships with Telegram, Discord, Slack, WhatsApp.
  • channels config key: Replaces the old top-level telegram key. Each channel has an explicit enabled toggle and channel-specific fields.
  • openclaw passthrough: Arbitrary dotpath-to-value config applied via openclaw config set during bootstrap — users can configure anything OpenClaw supports without waiting for a ChannelDef.
  • DynamicSection abstraction: Unifies capabilities and channels in the wizard. One registry, typed lookups, no scattered string prefix parsing.
  • toggle field type: New configDef field type rendering as [x]/[ ] checkbox, used for channel enabled toggles.
  • Wizard group headers: Non-interactive visual separators (Infrastructure, Channels, Identity) for better section grouping.

Adding a new channel

Add a ~15-line ChannelDef entry in packages/types/src/channels.ts. Everything else (wizard, validation, bootstrap, sanitization) is automatic.

Config shape

{
  "channels": {
    "telegram": { "enabled": true, "botToken": "123:ABC-..." },
    "discord": { "enabled": true, "token": "MTIz..." },
    "whatsapp": { "enabled": true }
  },
  "openclaw": {
    "channels.discord.streaming": "partial",
    "session.dmScope": "per-channel-peer"
  }
}

Test plan

  • bun test — 260 pass, 0 fail
  • clawctl-dev create — interactive wizard renders channel sections with group headers and toggle fields
  • clawctl-dev create --config examples/config.full.json — headless mode with channel config
  • Verify disabled channel (enabled: false) is skipped during bootstrap

🤖 Generated with Claude Code

TimBeyer and others added 12 commits April 1, 2026 12:13
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce data-driven channel definitions modeled after CapabilityDef.
Each ChannelDef declares essential fields (credential + key settings)
that drive validation, wizard rendering, bootstrap, and sanitization.

- ChannelDef type + registry with Telegram, Discord, Slack, WhatsApp
- channels and openclaw keys on InstanceConfig
- Zod schemas for new config sections
- Backward compat: top-level telegram key kept (deprecated)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- buildChannelsSchema() derives Zod schemas from ChannelDef fields
  with passthrough for extra fields
- loadConfig() migrates deprecated top-level telegram to channels.telegram
- validateConfig() applies channel schemas like capability schemas
- sanitizeConfig() generically strips channel secrets via ChannelDef

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace hardcoded Telegram bootstrap with a generic channel command
generator that works for any channel. Each channel's config is walked
and applied via openclaw config set commands. ChannelDef postCommands
handle channel-specific ordering (e.g., Telegram dmPolicy after allowFrom).

Add openclaw passthrough: arbitrary dotpath-to-value mappings applied
during bootstrap for config not covered by ChannelDefs.

Generalize infra-secrets patching to handle any channel's secret refs,
not just Telegram.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove all backward-compatibility code for the deprecated top-level
`telegram` config key. Channels now live exclusively under `channels.*`.

Introduce DynamicSection abstraction that unifies capabilities and
channels in the wizard. Both are driven by configDef and rendered by
the same CapabilitySection component. All focus-list, sidebar, and
select-field logic operates on DynamicSection uniformly — no more
scattered string prefix parsing.

- Delete telegramSchema (orphaned)
- Remove telegram migration in loadConfig
- Remove hardcoded telegram wizard section and state
- Single dynamicValues state for both capabilities and channels
- Update tests and examples to use channels.telegram
- Update config-review for dynamic channel rows

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rough

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Non-interactive visual separators that group related sections:
- Infrastructure: Resources, Provider, Network, capabilities
- Channels: Telegram, Discord, Slack, WhatsApp
- Identity: Agent Identity / bootstrap

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add "toggle" to ConfigFieldType — renders as [x]/[ ] checkbox in the
wizard, toggles on Enter, derives z.boolean() in Zod schemas.

Every channel now has an explicit "enabled" toggle as its first field:
- JSON: { "whatsapp": { "enabled": true } } instead of empty object
- Wizard: expand channel, first item is [x] Enabled toggle
- Bootstrap: skips channels with enabled: false

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents emitting channels with enabled: false or channels where the
user expanded the section but never toggled enabled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace hardcoded allowFrom/groups check in bootstrap with a
handledKeys array on the ChannelDef postCommands object. The generic
loop reads handled keys from the definition rather than maintaining
channel-specific knowledge in bootstrap code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@TimBeyer TimBeyer merged commit 960fb48 into main Apr 7, 2026
4 checks passed
@TimBeyer TimBeyer deleted the feat/generalized-channels branch April 7, 2026 07:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant