Architecture
Understand OrcBot's modular design and how components interact.
📦 System Overview
┌─────────────────────────────────────────────────────────┐
│ Channels │
│ (Telegram, WhatsApp, Discord, CLI, Web Gateway) │
└─────────────────────────┬───────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ Agent Core │
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │
│ │ Decision │ │ Memory │ │ Scheduler │ │
│ │ Engine │ │ Manager │ │ (Heartbeat) │ │
│ └─────────────┘ └──────────────┘ └───────────────┘ │
└─────────────────────────┬───────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ Skills & Tools │
│ (Browser, File System, Search, Messaging, Plugins) │
└─────────────────────────┬───────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ Providers │
│ (OpenAI, Google, Bedrock, NVIDIA, OpenRouter) │
└─────────────────────────────────────────────────────────┘
📁 Data Paths
OrcBot stores all data in ~/.orcbot/ by default:
| Path | Purpose |
|---|---|
orcbot.config.yaml | Main configuration file |
memory.json | Short-term and episodic memories |
action-queue.json | Pending tasks queue |
profiles/ | Contact profiles (per-JID) |
plugins/ | Custom skill plugins |
browser-profiles/ | Persistent browser sessions |
downloads/ | Downloaded media files |
🧠 Decision Engine
The decision engine builds context from memories, skills, and channel metadata, then calls the LLM to determine the next action. It supports multi-tool responses.
💾 Memory Manager
Manages short-term memories with automatic consolidation into episodic summaries after 30 entries. Retrieval is capped for context window efficiency.
⏰ Scheduler
Emits scheduler:tick events for background jobs. Heartbeat tasks, polling jobs, and autonomy checks all run through the scheduler.
🔌 Event Bus
Central pub/sub system for decoupled communication. Components subscribe to events like config:changed, action:push, etc.
🔄 Request Flow
- Inbound: Message arrives via channel (Telegram, WhatsApp, etc.)
- Memory: Message stored in short-term memory with context
- Decision: Decision engine builds prompt with memories, skills, channel context
- LLM Call: MultiLLM routes to appropriate provider based on model name
- Parse: Parser layer extracts tools array from JSON response
- Execute: Skills manager executes each tool in sequence
- Respond: Results sent back through the originating channel
🔐 Trust Boundaries
| Boundary | Trust Level | Notes |
|---|---|---|
| Inbound channels | Untrusted input | Telegram, WhatsApp, Discord, Slack, Email, and Gateway Chat payloads are treated as user-controlled input and must pass through session scoping, tool policy, and prompt guardrails. |
| Gateway API | Operator surface | The gateway is an admin/control-plane interface. Remote exposure requires gatewayApiKey, explicit CORS, and a stable sourceId for correct session routing. |
| Primary agent | Trusted runtime | The primary agent owns channel delivery, final user replies, and all cross-channel policy enforcement. Worker results are returned through this agent rather than sent directly. |
| Worker processes | Constrained trusted code | Workers inherit execution capabilities needed for delegated tasks, but channel tokens are stripped from their environment by default so they cannot silently impersonate the primary channel endpoints. |
| Plugins and custom skills | Fully trusted code | Plugins execute with local code privileges. Treat them as code deployment, not prompt configuration. Review and allow-list them before use on shared systems. |
| LLM providers and external APIs | External systems | Model providers, search providers, browsers, and third-party APIs are outside OrcBot's trust boundary. Keep secrets in config/env, not in memory or prompts. |
🧭 Deterministic Inbound Routing
OrcBot routes inbound user messages by thread before they become executable actions. The order is explicit and stable:
- Resolve session scope: Every inbound message is mapped to a
sessionScopeIdusing the configured session policy (main,per-peer, orper-channel-peer). - Resume waiting work first: If the same thread already has an action in
waitingstate, the new inbound message resumes that action instead of spawning a parallel replacement. - Supersede stale pending work: Older pending user-lane actions for the same thread are failed before the new message is queued, preventing stacked stale requests from replaying out of order.
- Serialize behind active work: If the thread already has an in-progress action, the new request is queued behind it and tagged with routing metadata rather than being ambiguously merged.
- Return delegated work through primary: Worker and clone results are reported back through the primary agent using the original reply context so final user delivery stays on the correct channel thread.
These routing decisions are recorded as inbound route metadata on memories and actions, making them inspectable from logs, memory, and gateway/API surfaces.