feat: add SMS (Telnyx) platform adapter

Implement SMS as a first-class messaging platform following
ADDING_A_PLATFORM.md checklist. All 16 integration points covered:

- gateway/platforms/sms.py: Core adapter with aiohttp webhook server,
  Telnyx REST API send, markdown stripping, 1600-char chunking,
  echo loop prevention, multi-number reply-from tracking
- gateway/config.py: Platform.SMS enum + env override block
- gateway/run.py: Adapter factory + auth maps (SMS_ALLOWED_USERS,
  SMS_ALLOW_ALL_USERS)
- toolsets.py: hermes-sms toolset + included in hermes-gateway
- cron/scheduler.py: SMS in platform_map for cron delivery
- tools/send_message_tool.py: SMS routing + _send_sms() standalone sender
- tools/cronjob_tools.py: 'sms' in deliver description
- gateway/channel_directory.py: SMS in session-based discovery
- agent/prompt_builder.py: SMS platform hint (plain text, concise)
- hermes_cli/status.py: SMS in platforms status display
- hermes_cli/gateway.py: SMS in setup wizard with Telnyx instructions
- pyproject.toml: sms optional dependency group (aiohttp>=3.9.0)
- tests/gateway/test_sms.py: Unit tests for config, format, truncate,
  echo prevention, requirements, toolset integration

Co-authored-by: sunsakis <teo@sunsakis.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Teknium
2026-03-17 02:52:34 -07:00
committed by GitHub
parent 71c6b1ee99
commit ef67037f8e
13 changed files with 645 additions and 4 deletions

View File

@@ -1013,6 +1013,30 @@ _PLATFORMS = [
"emoji": "📡",
"token_var": "SIGNAL_HTTP_URL",
},
{
"key": "sms",
"label": "SMS (Telnyx)",
"emoji": "📱",
"token_var": "TELNYX_API_KEY",
"setup_instructions": [
"1. Create a Telnyx account at https://portal.telnyx.com/",
"2. Buy a phone number with SMS capability",
"3. Create an API key: API Keys → Create API Key",
"4. Set up a Messaging Profile and assign your number to it",
"5. Configure the webhook URL: https://your-server/webhooks/telnyx",
],
"vars": [
{"name": "TELNYX_API_KEY", "prompt": "Telnyx API key", "password": True,
"help": "Paste the API key from step 3 above."},
{"name": "TELNYX_FROM_NUMBERS", "prompt": "From numbers (comma-separated E.164, e.g. +15551234567)", "password": False,
"help": "The Telnyx phone number(s) Hermes will send SMS from."},
{"name": "SMS_ALLOWED_USERS", "prompt": "Allowed phone numbers (comma-separated E.164)", "password": False,
"is_allowlist": True,
"help": "Only messages from these phone numbers will be processed."},
{"name": "SMS_HOME_CHANNEL", "prompt": "Home channel phone (for cron/notification delivery, or empty)", "password": False,
"help": "A phone number where cron job outputs are delivered."},
],
},
{
"key": "email",
"label": "Email",

View File

@@ -252,6 +252,7 @@ def show_status(args):
"Signal": ("SIGNAL_HTTP_URL", "SIGNAL_HOME_CHANNEL"),
"Slack": ("SLACK_BOT_TOKEN", None),
"Email": ("EMAIL_ADDRESS", "EMAIL_HOME_ADDRESS"),
"SMS": ("TELNYX_API_KEY", "SMS_HOME_CHANNEL"),
}
for name, (token_var, home_var) in platforms.items():