Skip to content

gokapso/hermes-agent-plugin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Kapso Hermes Platform Plugin

Kapso WhatsApp platform adapter for Hermes Agent. It receives Kapso platform webhooks, turns inbound WhatsApp messages into Hermes MessageEvents, and sends Hermes replies through Kapso's WhatsApp Cloud API proxy.

Install

Prerequisites

  • A Hermes Agent install with the gateway enabled.
  • A Kapso account. Sign up at kapso.ai, create or open a project, and create a project API key in the Kapso dashboard.
  • A connected Kapso WhatsApp number. If you do not have one yet, run kapso setup after installing the Kapso CLI, or complete WhatsApp onboarding in the Kapso dashboard.
  • A public HTTPS URL that can reach the Hermes Kapso adapter. Tailscale Funnel works well and should proxy to http://127.0.0.1:8648.
  • Node.js/npm if you want the setup command to install and use the Kapso CLI.

Install and enable the plugin from GitHub:

hermes plugins install gokapso/hermes-agent-plugin --enable

The installer prompts only for KAPSO_API_KEY. Webhook setup happens in the next step.

Hermes clones Git plugins into ~/.hermes/plugins/kapso and loads them on the next session or gateway restart.

Quickstart

Recommended setup:

hermes kapso setup --install-cli --funnel-url https://<your-funnel-host>

That command can:

  • install the Kapso CLI with npm install -g @kapso/cli
  • list connected WhatsApp numbers from Kapso and ask which one Hermes should use
  • ask for your WhatsApp user number/wa_id for KAPSO_HOME_CHANNEL and KAPSO_ALLOWED_USERS
  • generate and save KAPSO_WEBHOOK_SECRET
  • create the Kapso phone-number webhook with payload version v2

The webhook created by the helper points at:

https://<your-funnel-host>/kapso/webhook

For a non-interactive setup:

hermes kapso setup \
  --api-key "$KAPSO_API_KEY" \
  --phone-number-id "1041695002363992" \
  --home-channel "15551234567" \
  --allowed-users "15551234567" \
  --funnel-url "https://<your-funnel-host>" \
  --configure-webhook \
  --install-cli \
  --no-prompt

Restart the gateway after setup:

hermes gateway restart
hermes gateway status

Expose the adapter to Kapso. The adapter listens on 0.0.0.0:8648 and accepts POST /kapso/webhook by default. With Tailscale Funnel, point the public URL at that local port:

tailscale funnel reset
tailscale funnel --bg http://127.0.0.1:8648
tailscale funnel status

Set the Kapso webhook URL to:

https://<your-funnel-host>/kapso/webhook

Recommended Kapso webhook settings:

Setting Value
Events whatsapp.message.received
Payload version v2
Secret Same value as KAPSO_WEBHOOK_SECRET

Verify the setup:

hermes kapso status
curl http://127.0.0.1:8648/health
curl https://<your-funnel-host>/health

If CLI webhook creation fails, create the webhook manually in Kapso with these settings:

Setting Value
Scope WhatsApp phone number
Endpoint URL https://<your-funnel-host>/kapso/webhook
Events whatsapp.message.received
Kind kapso
Payload version v2
Secret Same value as KAPSO_WEBHOOK_SECRET

Configuration

The setup command writes these values to ~/.hermes/.env:

KAPSO_API_KEY=...
KAPSO_WEBHOOK_SECRET=...
KAPSO_PHONE_NUMBER_ID=...      # recommended for outbound and cron delivery
KAPSO_HOME_CHANNEL=15551234567 # optional default recipient for deliver=kapso
KAPSO_ALLOWED_USERS=15551234567 # recommended for production allowlisting
KAPSO_WEBHOOK_URL=https://...  # status/debug only

To allow a specific WhatsApp user later:

hermes kapso setup --allowed-users 15551234567 --no-prompt
hermes gateway restart

For development-only open access:

hermes kapso setup --allow-all-users --no-prompt
hermes gateway restart

Useful Kapso CLI checks after --install-cli:

kapso status
kapso whatsapp numbers list --output json

Signature verification is on by default. For unsigned local fixtures only:

KAPSO_VERIFY_WEBHOOK_SIGNATURES=false

Voice Notes

Inbound WhatsApp voice notes use Kapso's transcript when the webhook payload includes one. In that path, the adapter sends the transcript text straight to Hermes and skips audio attachment, so users do not need faster-whisper or an OpenAI transcription key for those messages.

If Kapso has not provided a transcript in the webhook payload, the adapter falls back to downloading the voice note through Kapso, caching it as .ogg, and passing it to Hermes as audio media. Hermes then transcribes it with its configured STT provider before the agent turn.

For local, no-key transcription with faster-whisper:

sudo apt update
sudo apt install -y ffmpeg python3-venv python3-pip

~/.hermes/hermes-agent/venv/bin/python -m ensurepip --upgrade || true
~/.hermes/hermes-agent/venv/bin/python -m pip install -U pip setuptools wheel
~/.hermes/hermes-agent/venv/bin/python -m pip install -U faster-whisper

hermes config set stt.provider local
hermes config set stt.local.model base
hermes gateway restart

If the Hermes venv has no pip and ensurepip is unavailable:

curl -sS https://bootstrap.pypa.io/get-pip.py -o /tmp/get-pip.py
~/.hermes/hermes-agent/venv/bin/python /tmp/get-pip.py
~/.hermes/hermes-agent/venv/bin/python -m pip install -U faster-whisper

For OpenAI transcription:

printf '\nVOICE_TOOLS_OPENAI_KEY=sk-...\n' >> ~/.hermes/.env
hermes config set stt.provider openai
hermes config set stt.openai.model gpt-4o-mini-transcribe
hermes gateway restart

Other OpenAI model options include whisper-1 and gpt-4o-transcribe.

Verify voice-note processing:

tail -f ~/.hermes/logs/gateway.log ~/.hermes/logs/agent.log \
  | grep --line-buffered -iE 'kapso|audio|voice|stt|transcrib|transcript|whisper|openai'

Healthy Kapso-transcript logs look like:

[kapso] using Kapso transcript for inbound audio ...

Healthy STT fallback logs look like:

[kapso] cached inbound audio ... at ~/.hermes/audio_cache/audio_....ogg
Transcribed audio_....ogg via local whisper (base, lang=en, ...)

Documents

Inbound WhatsApp documents are downloaded through Kapso, cached locally, and attached to Hermes as document media. Hermes receives the local file path so it can inspect PDFs, DOCX/XLSX/PPTX files, archives, and other supported document types with its normal document/tooling flow.

Small text-like documents (.txt, .md, .csv, .json, .yaml, and similar) are also injected into the message text when they are under 100 KB, so the agent can answer simple questions without opening an extra file tool.

For PDFs and richer document formats, Hermes currently receives the cached file path and may ask for tool approval before extracting or inspecting the file. That approval prompt means the document reached Hermes; it is Hermes' document tooling path, not a Kapso webhook failure. Native "send this PDF directly to the model" behavior depends on Hermes core/provider document routing and is not enabled by this adapter alone.

Chat IDs

Inbound sessions use encoded IDs:

kapso:<base64url(phone_number_id)>:<base64url(wa_id)>[:<base64url(conversation_id)>]

For manual sends or cron delivery, you can use either a plain WhatsApp recipient when KAPSO_PHONE_NUMBER_ID is configured:

15551234567

or an explicit phone number ID and recipient:

<phone_number_id>:15551234567

Outbound Files

Hermes outbound MEDIA:/absolute/path attachments are sent through Kapso's Meta proxy, then delivered with the returned WhatsApp media ID.

Images, audio, video, and documents use the matching WhatsApp media message type. Other local files are sent as WhatsApp document messages. WhatsApp stickers are supported only when explicitly sent as sticker media; Hermes MEDIA:/path.webp attachments are treated as images by Hermes.

Config YAML

Environment variables are the easiest path, but this also works:

gateway:
  platforms:
    kapso:
      enabled: true
      extra:
        api_key: "..."
        webhook_secret: "..."
        phone_number_id: "..."

Quick Troubleshooting

If Hermes is not receiving messages:

hermes gateway status
hermes kapso status
curl http://127.0.0.1:8648/health
tailscale funnel status
journalctl --user -u hermes-gateway.service -f

Check that Tailscale Funnel proxies to http://127.0.0.1:8648, not an older bridge process. The public webhook should end in /kapso/webhook.

Check that Kapso is sending whatsapp.message.received events with payload version v2, and that the webhook secret in Kapso matches KAPSO_WEBHOOK_SECRET.

If the gateway log says no user allowlist is configured, add your WhatsApp ID:

hermes kapso setup --allowed-users 15551234567 --no-prompt
hermes gateway restart

If outbound replies fail, confirm KAPSO_API_KEY and KAPSO_PHONE_NUMBER_ID are set:

hermes kapso status
kapso whatsapp numbers list --output json

If images do not reach the agent, tail the gateway logs while sending a photo:

journalctl --user -u hermes-gateway.service -f | grep -i kapso

Successful image ingestion logs cached inbound image ... and writes the file under ~/.hermes/image_cache or ~/.hermes/cache/images, depending on the Hermes runtime version. If you only see image message ... has no downloadable media URL yet, confirm the webhook payload includes either kapso.mediaUrl/kapso.media_url or an image media id.

If voice notes do not transcribe, first confirm whether Kapso supplied a transcript or the plugin cached the audio fallback:

grep -R "using Kapso transcript\|cached inbound audio\|User sent audio\|STT" ~/.hermes/logs/*.log
find ~/.hermes/cache/audio ~/.hermes/audio_cache -type f -mmin -10 -ls 2>/dev/null

Successful Kapso transcript ingestion logs using Kapso transcript ... and does not require a local or OpenAI STT provider. If no transcript is present, successful fallback ingestion logs cached inbound audio .... The cached file is usually under ~/.hermes/audio_cache or ~/.hermes/cache/audio. Hermes then uses its configured STT provider to transcribe the cached file. For a no-key local STT provider:

~/.hermes/hermes-agent/venv/bin/python -m pip install -U faster-whisper
hermes gateway restart

For OpenAI Whisper/transcribe instead, add VOICE_TOOLS_OPENAI_KEY to ~/.hermes/.env and restart the gateway. If old logs show voice notes cached as .opus, update the plugin; Kapso voice notes are cached as .ogg for OpenAI STT compatibility.

If documents do not reach the agent, confirm they were cached:

grep -R "cached inbound document" ~/.hermes/logs/*.log
find ~/.hermes/cache/documents ~/.hermes/document_cache -type f -mmin -10 -ls 2>/dev/null

Successful document ingestion logs cached inbound document .... The cached file is usually under ~/.hermes/document_cache or ~/.hermes/cache/documents, depending on the Hermes runtime version.

If the agent responds with a dangerous-command approval prompt while reading a PDF, the document was delivered correctly and Hermes is trying to run its local extraction/inspection tool. Approving that command allows Hermes to read the cached file. Denying it leaves the agent with only the filename and caption.

Implementation Notes

  • hermes plugins install ... --enable prompts only for KAPSO_API_KEY. hermes kapso setup handles the webhook secret and phone number setup.
  • hermes kapso setup --install-cli is safe to rerun when you need to rotate keys or add KAPSO_PHONE_NUMBER_ID.
  • Hermes can run the Kapso CLI after installation, so you can ask the agent to inspect numbers or webhook state. Enter secrets through the installer/setup prompts rather than pasting them into chat.
  • Text messages are split at WhatsApp's 4096-character limit.
  • Outbound Markdown links are converted to label (url), and **bold** is converted to WhatsApp's *bold* style.
  • Inbound images are downloaded through Kapso, cached locally, and attached to Hermes MessageEvent.media_urls for native vision processing.
  • Inbound audio and voice notes are downloaded through Kapso, cached locally, and attached to Hermes MessageEvent.media_urls for native STT processing.
  • Inbound documents are downloaded through Kapso, cached locally, and attached to Hermes MessageEvent.media_urls for native document processing.
  • Other unsupported media currently lands as captions or descriptive placeholders.

About

Official Kapso plugin for Hermes Agent

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages