Skip to main content

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:
  1. A per-agent interval timer fires at the configured frequency
  2. The runner checks active hours, then reads ~/ravi/{agent}/HEARTBEAT.md
  3. If the file exists and is non-empty, a heartbeat prompt is published to the agent’s main session
  4. If the agent responds with only HEARTBEAT_OK, the message is suppressed (no channel delivery)
  5. Config changes are picked up automatically via NATS refresh signals (ravi.heartbeat.refresh)
Key behaviors:
  • 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 single setTimeout for the next due job, then re-arms after execution. Schedule types:
  • --cron "0 9 * * *" — Standard cron expression (with optional --tz for timezone)
  • --every 30m — Interval-based (supports 30s, 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
Session targets:
  • main (default) — Runs in the agent’s shared session. If replySession is set, the prompt is delivered there instead
  • isolated — Dedicated session per job (agent:{agentId}:cron:{jobId})
Prompt format: Each prompt is prefixed with [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:
  1. The runner subscribes to all enabled trigger topics, grouping by topic to share subscriptions
  2. When an event fires, the runner checks cooldown, evaluates the filter expression, and resolves message templates
  3. The prompt is published to the target session with full event data included
Anti-loop protection:
  • 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: true are 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. No eval or new Function() — uses a safe parser. Syntax: data.<path> <operator> "<value>", optionally composed with &&, ||, unary !, and parentheses. Operators: ==, !=, startsWith, endsWith, includes
Precedence is ! 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}})
String values longer than 300 characters are automatically truncated. Unresolved variables are left as-is in the output.

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_v2 model (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
Environment:
Requires: 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
Key capabilities:
  • 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
Environment:

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)
Output structure:
  • 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
Environment:

Audio Transcription

Transcribes audio using Groq (preferred) or OpenAI Whisper. Automatically chunks long audio files for reliable transcription. Provider selection: Groq is preferred when GROQ_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
Supported formats: OGG/Opus, MP3, M4A, WAV, WebM Environment:
Requires: 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 (from SPEC_INSTRUCTIONS.md in 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 timestamps
  • exit_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
State tracking: Per-session state stored in a Map, including active flag, progress percentage, summary, and full history of updates. Custom instructions: Place a 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:
  1. A session is marked ephemeral with a TTL via ravi sessions set-ttl
  2. 10 minutes before expiry: a warning prompt is sent to the session with options to extend, keep, or delete
  3. On expiry: the SDK subprocess is aborted and the session is deleted from the database
  4. 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 --channel and --to flags
  • 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 the ravi.media.send NATS topic. Supported media types: image, video, audio, document Media send payload:
  • filePath — Local path to the file
  • mimetype — MIME type
  • type — Media category (image/video/audio/document)
  • filename — Display filename
  • caption — Optional caption text
  • voiceNote — Boolean flag for push-to-talk audio (OGG/Opus)
The gateway also handles typing indicators: it subscribes to 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:
  1. Daemon tries atomic create on a KV key with its daemon ID — succeeds only if key doesn’t exist
  2. Success: this daemon is leader, starts runners, begins renewing the lease every 10 seconds
  3. Failure: another daemon is leader. This daemon polls every 10 seconds for vacancy
  4. Leader lease TTL is 30 seconds. If the leader dies without renewing, the key expires
  5. On vacancy detection: the polling daemon attempts to acquire leadership
  6. On graceful shutdown: leader explicitly deletes the KV key for faster failover
KV bucket: ravi-leader with 30-second TTL and history depth of 1.