joshbot is built around a goroutine-based message bus that decouples chat channels from a ReAct agent loop. The agent is backed by a multi-provider LLM layer via OpenRouter-compatible APIs. Everything — memory, skills, tools — is file-based, making the system portable and auditable without a database.
Every message — from CLI or Telegram — follows the same path through the system.
InboundMessage.InboundMessage into the agent goroutine via a channel. The bus handles multiplexing across channels.OutboundMessage on bus → routed to correct channel → displayed to user.The project follows Go conventions with cmd/ for the entry point and internal/ for all implementation packages.
These design decisions shape how every component works.
Channels decoupled from agent via chan InboundMessage and chan OutboundMessage. Adding a new channel requires zero agent changes.
LLM → tools → reflect → repeat (max 20 iterations). Each iteration appends tool results as observations for the next LLM call.
No databases. MEMORY.md, HISTORY.md, SKILL.md files. Portable, auditable, grep-able, syncs via Dropbox/git.
Skills summarized in context; full content loaded on demand. Keeps token overhead minimal until a skill is actually invoked.
Static system prompt segments cached with mtime-based invalidation. Reduces file I/O on every message — critical for memory/skill files read each turn.
Provider auto-detected from model prefix (claude → Anthropic, gpt → OpenAI). Fallback chains for resilience. ExtraBody support for provider-specific fields.
Conversation history summarized when approaching token limits. Budget manager tracks and enforces per-iteration costs.
Parallel fan-out or sequential chain execution. Each subagent gets a focused one-turn LLM call with its own system prompt. Results merged back into the parent agent context.
internal/agent/)The core ReAct loop. Each message triggers up to 20 think-act-observe iterations. System prompt is assembled from identity files + memory + skills summaries (with mtime-based caching). The agent serializes the tool schema from the Registry into the LLM's function calling format.
internal/tools/)20 tools implement the Tool interface. Each declares Name, Description, Parameters, and Execute. The Registry handles discovery and execution. Tools include: filesystem, shell (with safety deny-list), web fetch (SSRF-protected), memory search, skill management, parallel subagent, chain execution, message sending, and more.
internal/memory/)Two files: MEMORY.md (always in context — key facts, preferences, decisions) and HISTORY.md (grep-searchable event log for recall). Facts are extracted after each conversation turn by the learning module.
internal/skills/)Markdown files with YAML frontmatter in workspace/skills/{name}/SKILL.md. Auto-discovered on startup. Progressive loading: summary only in system prompt, full content loaded when the skill is invoked. The detection module observes tool usage patterns and auto-creates skills.
internal/providers/)Multi-provider LLM support via a unified Provider interface. Registered providers: OpenRouter, OpenAI, NVIDIA, Groq, Ollama, Anthropic, Poolside, Azure, Custom, LiteLLM. Provider auto-selection by model prefix. ExtraBody support for provider-specific fields (e.g., poolside's chat_template_kwargs).
internal/bus/)Goroutine-safe channel multiplexer. Inbound messages from any channel are routed to the agent goroutine. Outbound messages from the agent are routed back to the originating channel. This decoupling means channels and agent can be developed independently.
Providers are auto-detected from model names. The config supports both legacy and model-centric formats.
Environment variable overrides: JOSHBOT_PROVIDERS__OPENROUTER__API_KEY or JOSHBOT_MODELS_CONFIG__AGENT__MODEL. Config is validated JSON, stored at ~/.joshbot/config.json.