Heartbeat
Proactive agent runs that periodically check a task file (HEARTBEAT.md) in the agent’s working directory and prompt the agent to act on pending items.
How it works:
- A per-agent interval timer fires at the configured frequency
- The runner checks active hours, then reads
~/ravi/{agent}/HEARTBEAT.md - If the file exists and is non-empty, a heartbeat prompt is published to the agent’s main session
- If the agent responds with only
HEARTBEAT_OK, the message is suppressed (no channel delivery) - Config changes are picked up automatically via NATS refresh signals (
ravi.heartbeat.refresh)
- Timers are managed per-agent and automatically recreated when intervals change
- Active hours are enforced: heartbeats outside the window are silently skipped
- The heartbeat uses the agent’s main session, reusing the last known channel routing for delivery
- Manual triggers bypass the enabled check but still respect active hours
Cron Jobs
Scheduled jobs that send prompts to agents at specified times. Uses a timer-based approach where the runner arms a singlesetTimeout for the next due job, then re-arms after execution.
Schedule types:
--cron "0 9 * * *"— Standard cron expression (with optional--tzfor timezone)--every 30m— Interval-based (supports30s,5m,1h,2d). Uses anti-drift: next run is calculated from the scheduled time, not from when execution finishes--at "2025-02-01T15:00"— One-shot at a specific ISO datetime. Automatically deleted after execution
main(default) — Runs in the agent’s shared session. IfreplySessionis set, the prompt is delivered there insteadisolated— Dedicated session per job (agent:{agentId}:cron:{jobId})
[Cron: {name} {timestamp}] followed by the configured message.
Event Triggers
Event-driven triggers that subscribe to NATS topics and fire agent prompts when matching events occur. Supports filters and message templates. How it works:- The runner subscribes to all enabled trigger topics, grouping by topic to share subscriptions
- When an event fires, the runner checks cooldown, evaluates the filter expression, and resolves message templates
- The prompt is published to the target session with full event data included
- Blocked prefixes: topics starting with
ravi.session.can be saved by the CLI for compatibility, but the runner refuses to arm them - Session filter: events from trigger sessions (
:trigger:in topic) are skipped - Data flag: events with
_trigger: trueare skipped - Cooldown: per-trigger cooldown (default 5s) prevents rapid re-firing. Cooldown is set in-memory immediately on first match to prevent race conditions with concurrent events
Trigger Filters
A restricted expression syntax for filtering events. Noeval or new Function() — uses a safe parser.
Syntax: data.<path> <operator> "<value>", optionally composed with &&, ||, unary !, and parentheses.
Operators: ==, !=, startsWith, endsWith, includes
! before && before ||. If the filter is empty or missing, the trigger always fires. CLI commands reject invalid filter syntax before persisting it. Runtime evaluation still fails open for legacy invalid filters and logs a warning. If the data path doesn’t exist, the filter returns false (no match).
Message Templates
Trigger messages support{{variable}} placeholders resolved against event data.
Available variables:
{{topic}}— The NATS topic that fired the trigger{{data.<path>}}— Dot-notation path into the event data (e.g.,{{data.cwd}},{{data.prompt}})
Text-to-Speech (TTS)
Generates audio from text using the ElevenLabs API. Supports multiple voices, models, and output formats including WhatsApp-compatible voice notes (PTT). Key capabilities:- Multilingual support via
eleven_multilingual_v2model (default) - Configurable voice ID, speed (0.5-2.0), and language code
- Output formats: MP3 (default), PCM, WAV
- WhatsApp PTT mode: converts output to OGG/Opus via ffmpeg for push-to-talk voice notes
ffmpeg installed locally for PTT conversion.
Image Generation
Generates images from text prompts using Google Gemini models. Supports image editing with a source image input. Models:fast— Gemini 3.1 Flash Image Preview (default)quality— Gemini 3 Pro Image Preview
- Text-to-image generation with aspect ratio control (
1:1,16:9,9:16, etc.) - Image size:
1K,2K,4K - Image editing: provide a source image (PNG, JPEG, WebP, GIF) along with a text prompt
- Multiple images can be returned per generation
Video Analysis
Analyzes videos using Google Gemini, supporting both YouTube URLs and local video files. Returns structured analysis with title, duration, summary, topics, transcript, and timestamped visual descriptions. Supported inputs:- YouTube URLs (analyzed directly via Gemini’s file data API)
- Local video files: MP4, MPEG, MOV, AVI, FLV, WebM, WMV, 3GPP (uploaded to Gemini Files API first)
- Title, Duration, Summary (2-3 paragraphs)
- Topics (bullet list)
- Complete transcript with speaker labels
- Timestamped visual descriptions (
[MM:SS-MM:SS]format) - Full markdown document combining all sections
Audio Transcription
Transcribes audio using Groq (preferred) or OpenAI Whisper. Automatically chunks long audio files for reliable transcription. Provider selection: Groq is preferred whenGROQ_API_KEY is set (uses whisper-large-v3-turbo). Falls back to OpenAI (whisper-1) if only OPENAI_API_KEY is available.
Chunking behavior:
- Audio under 10 minutes: transcribed directly in a single API call
- Audio over 10 minutes: split into 10-minute segments with 15-second overlap using ffmpeg, then transcribed chunk-by-chunk and concatenated
- Duration detection uses ffprobe; if detection fails, direct transcription is attempted
ffmpeg and ffprobe installed locally for chunking and duration detection.
Spec Mode
A collaborative specification mode implemented as an in-process MCP server. When activated, destructive tools (Edit, Write, Bash) are blocked — the agent can only explore code and ask questions until the spec is approved. Three tools:enter_spec_mode— Activates spec mode and loads the spec prompt (fromSPEC_INSTRUCTIONS.mdin the agent’s CWD, or a built-in default)update_spec— Registers progress (0-100%) with a summary. Maintains a history of all progress updates with timestampsexit_spec_mode— Generates the final spec and requests user approval. If approved, destructive tools are unblocked. If rejected, the agent stays in spec mode to revise
SPEC_INSTRUCTIONS.md file in the agent’s working directory to override the default spec prompt.
Ephemeral Sessions
Sessions with a time-to-live (TTL) that are automatically cleaned up after expiry. The ephemeral runner checks every 60 seconds for expiring and expired sessions. Lifecycle:- A session is marked ephemeral with a TTL via
ravi sessions set-ttl - 10 minutes before expiry: a warning prompt is sent to the session with options to extend, keep, or delete
- On expiry: the SDK subprocess is aborted and the session is deleted from the database
- Message metadata older than 7 days is also cleaned up in each cycle
Cross-Session Messaging
Typed messaging between agent sessions, enabling multi-agent coordination and inter-session communication. Message types:
Features:
- Self-send detection prevents deadlocks (sending to your own session is blocked)
- Channel routing is inherited from the target session’s last known channel/account
- Channel and target overrides via
--channeland--toflags - Interactive mode (
-i) for back-and-forth conversation - Wait mode (
-w) streams the response back to the caller - Approval source cascading: when agent A sends to agent B, A’s channel becomes B’s approval source
- Scope isolation: sessions are filtered by REBAC permissions
Reactions
Agents can send emoji reactions to messages. Inbound messages include[mid:ID] tags that identify the message for reactions.
The gateway subscribes to ravi.outbound.reaction events and delivers them via the omni sender API. Queue group (ravi-gateway) ensures only one gateway daemon sends each reaction.
Media Pipeline
The gateway handles media file delivery from agents to channels via theravi.media.send NATS topic.
Supported media types: image, video, audio, document
Media send payload:
filePath— Local path to the filemimetype— MIME typetype— Media category (image/video/audio/document)filename— Display filenamecaption— Optional caption textvoiceNote— Boolean flag for push-to-talk audio (OGG/Opus)
ravi.session.*.claude events and sends typing-on during agent processing, typing-off when the agent produces a result or goes silent.
Leader Election
Distributed leader election via NATS JetStream KV for daemon coordination. Only one daemon per role runs singleton workloads (heartbeat runner, cron runner, etc.). How it works:- Daemon tries atomic
createon a KV key with its daemon ID — succeeds only if key doesn’t exist - Success: this daemon is leader, starts runners, begins renewing the lease every 10 seconds
- Failure: another daemon is leader. This daemon polls every 10 seconds for vacancy
- Leader lease TTL is 30 seconds. If the leader dies without renewing, the key expires
- On vacancy detection: the polling daemon attempts to acquire leadership
- On graceful shutdown: leader explicitly deletes the KV key for faster failover
ravi-leader with 30-second TTL and history depth of 1.