Summary
The proactive channel-messaging rate limits (10 messages/hour/channel, 100 messages/hour/agent for Slack and Telegram; 10 messages/hour/recipient for proactive DMs) are hardcoded module constants. They were introduced as anti-spam guardrails (#349, #350, #321), but they cannot be tuned, which blocks legitimate high-volume autonomous use cases — e.g. an agent that posts a Slack message for each inbound support request. Make them admin-configurable with the current values as defaults.
Context
A community report hit these caps building an autonomous Slack-posting agent and asked whether they are configurable (they are not) and whether the enterprise edition lifts them (it does not — they are OSS guardrails, not entitlement gates).
The hardcoded constants:
src/backend/routers/slack.py — _SLACK_PROACTIVE_PER_CHANNEL = 10, _SLACK_PROACTIVE_PER_AGENT = 100 (gates POST /api/agents/{name}/slack/channels/{channel_id}/messages, backing MCP send_group_message)
src/backend/routers/telegram.py — _PROACTIVE_RATE_LIMIT_PER_GROUP = 10, _PROACTIVE_RATE_LIMIT_PER_AGENT = 100 (same caps, bespoke in-memory bucket)
src/backend/services/proactive_message_service.py — RATE_LIMIT_MAX_PER_HOUR = 10 per agent-recipient pair (backing MCP send_message)
The codebase already has the pattern to follow: the inbound channel limiter reads channel_rate_limit_max / channel_rate_limit_window from settings_service with hardcoded fallbacks (src/backend/adapters/message_router.py), and max_parallel_tasks_ceiling (#506) established the admin-tunable-guardrail shape (runtime-resolved from system_settings, range-validated, no per-process cache so --workers 2 stays consistent, no migration needed).
Note: replies to inbound Slack/Telegram messages (DM/@mention/thread via the channel adapter) are NOT gated by these caps — only agent-initiated proactive sends are. That distinction should land in the docs as part of this issue.
Acceptance Criteria
Technical Notes
Summary
The proactive channel-messaging rate limits (10 messages/hour/channel, 100 messages/hour/agent for Slack and Telegram; 10 messages/hour/recipient for proactive DMs) are hardcoded module constants. They were introduced as anti-spam guardrails (#349, #350, #321), but they cannot be tuned, which blocks legitimate high-volume autonomous use cases — e.g. an agent that posts a Slack message for each inbound support request. Make them admin-configurable with the current values as defaults.
Context
A community report hit these caps building an autonomous Slack-posting agent and asked whether they are configurable (they are not) and whether the enterprise edition lifts them (it does not — they are OSS guardrails, not entitlement gates).
The hardcoded constants:
src/backend/routers/slack.py—_SLACK_PROACTIVE_PER_CHANNEL = 10,_SLACK_PROACTIVE_PER_AGENT = 100(gatesPOST /api/agents/{name}/slack/channels/{channel_id}/messages, backing MCPsend_group_message)src/backend/routers/telegram.py—_PROACTIVE_RATE_LIMIT_PER_GROUP = 10,_PROACTIVE_RATE_LIMIT_PER_AGENT = 100(same caps, bespoke in-memory bucket)src/backend/services/proactive_message_service.py—RATE_LIMIT_MAX_PER_HOUR = 10per agent-recipient pair (backing MCPsend_message)The codebase already has the pattern to follow: the inbound channel limiter reads
channel_rate_limit_max/channel_rate_limit_windowfromsettings_servicewith hardcoded fallbacks (src/backend/adapters/message_router.py), andmax_parallel_tasks_ceiling(#506) established the admin-tunable-guardrail shape (runtime-resolved fromsystem_settings, range-validated, no per-process cache so--workers 2stays consistent, no migration needed).Note: replies to inbound Slack/Telegram messages (DM/@mention/thread via the channel adapter) are NOT gated by these caps — only agent-initiated proactive sends are. That distinction should land in the docs as part of this issue.
Acceptance Criteria
settings_service(no restart required,--workers 2-consistent)0= disabled is allowed (if so, it warns) or a floor is enforcedsrc/backend/services/rate_limiter.py, refactor(backend): unified Redis sliding-window rate limiter (replace ad-hoc per-endpoint limiters) #1023), matching what Slack already uses (also fixes multi-worker inconsistency of the in-memory buckets).env-only), with honest display of current effective valuesTechnical Notes
max_parallel_tasks_ceilingshape: dedicated GET/PUT settings routes (registered before the/{key}catch-all, Invariant fix: add missing logging_config.py to backend Dockerfile #4), stored insystem_settings, fail-open getter with the constant as fallbackrate_limiter.enforce(...)— the change there is only sourcing the limit from settings instead of the constantservices/proactive_message_service.pyhas its own Redis counter (INCR+EXPIRE); either keep it and read the limit from settings, or fold it into the shared limiter while preserving the fail-open behaviorsystem_settingskey-value)