$ _

a personal AI agent
as one Go binary

A nanobot-class personal agent with none of the Python. joshbot is a single ~19MB static binary — no venv, no runtime dependencies. Self-hosted, curl to running in under a minute, and scriptable enough to test in CI.

Install Now How It Works
curl -fsSL https://raw.githubusercontent.com/bigknoxy/joshbot/main/install.sh | bash
joshbot — interactive session
$ joshbot agent
> what was that article about Go concurrency patterns?
[memory_search] Found 3 relevant facts...
You asked about this on March 12th. Key patterns: goroutines (cheap threads), channels (communicate via shared memory), and select (multiplex). Want me to fetch the full article?
>

ReAct loop,
real results

joshbot uses a ReAct (Reasoning + Acting) loop — it thinks, uses tools, learns from results, and iterates — all within a single conversation.

01

You speak

Send a message via CLI, Telegram, or script. Every channel feeds into a common message bus.

02

It reasons

The LLM core analyzes your intent with full context — past conversations, stored memories, available skills and tools.

03

It acts

Runs tools (search files, fetch web pages, execute code, delegate subagents) and observes results.

04

It learns

Key facts are extracted and saved to long-term memory. Repeated tool patterns auto-create reusable skills.

~19MB
Single static binary
0
Runtime dependencies
100%
Self-hosted

What can it do?

From daily productivity to deep research — joshbot handles the spectrum.

⚡

Daily Briefing

Summarizes calendar, tasks, and priorities into a morning plan. Flag urgent items and draft replies.

🔬

Deep Research

Gathers information across multiple sources, cross-references, and returns a structured brief with citations.

🧠

Cross-Session Memory

Retains facts, preferences, and decisions across days. Retrieves relevant context without being reminded. Optional two-stage Dream consolidation clusters past turns into durable insights using local embeddings — no embedding API.

🛠️

Code & Engineering

Generates, explains, debugs, and refactors code. Runs lint/tests on results. Investigates bugs systematically.

📡

Scheduled & Proactive

Schedule reminders in plain durations (30m, 2h, 1d), one-off or repeating — jobs and their delivery survive restarts, so a reminder set before a reboot still reaches your chat. A heartbeat scans for unchecked tasks and follows up on its own.

🧩

Auto Skill Creation

Observes repeated tool-use patterns and generates reusable skills. Common tasks become one-command operations.

🔎

Conversation Search

joshbot sessions search "that thing" greps every transcript — case-insensitive, newest first, redacted output — and the agent has the same recall via the session_search tool, so "what did we decide last week" is answered from the record, on any channel. joshbot agent --continue resumes the latest session headlessly with a recap.

🤖

Subagent Delegation

Fans out independent tasks to parallel subagents or chains steps sequentially. Each gets focused context.

⚡

Streaming Replies

The interactive CLI and Telegram print the answer as it is generated instead of after the turn ends. On by default since v1.48.0 — set streaming: false to restore whole-reply delivery. Scripted output is unaffected either way.

⌨️

Interactive Line Editor

In a real terminal, joshbot agent gives you Tab slash-command completion, Up/Down history, multiline editing (Alt+Enter) and a prompt that shows the current model.

🧭

Slash Commands

Stop a running turn from a [⏹ Stop] button on the streaming reply, switch models or personalities per-session (/model, /personality — inline button pickers on Telegram), summarize context (/compact) and inspect status (/status) — in the CLI and from the Telegram menu.

🔌

OpenAI-Compatible API

joshbot serve speaks the OpenAI chat API at /v1/chat/completions (streaming included) and /v1/models, so any client with a base-URL setting can use joshbot as a backend. The model is the agent — a request runs the full loop with tools, memory and skills, not a proxy to an upstream provider. Authentication is mandatory (it refuses to start with no key configured) and it binds loopback by default, because whoever reaches this endpoint reaches the shell tool. When stt is configured it also serves POST /v1/audio/transcriptions — the one route that is not the agent — transcribing a multipart upload with the same speech-to-text provider Telegram voice notes use; without stt.provider it answers 501 naming the key. When embeddings is configured it also serves POST /v1/embeddings, embedding text with that provider's key and endpoint — no key needed for a local ollama — and answering 501 naming embeddings.provider when it is not. Setting api.webui also serves a self-contained browser chat page at /, embedded in the binary with no CDN; it is off by default because the page is a login form accepting an API key that reaches the shell tool, and with it off those routes 404.

📱

Telegram & Discord

Chat from anywhere via Telegram or Discord. Full skill and memory access from your phone. On Telegram, replies are authored as Markdown and delivered as HTML so ordinary prose is not mangled, and thread to the message that asked, a live status line shows what tool is running mid-turn, reminders survive restarts, text files you send are actually read, and voice notes are transcribed and answered when STT is configured (honestly refused when not). Telegram uses long-polling, Discord an outbound gateway websocket — no webhooks or inbound ports either way. Both fail closed — an empty allowlist rejects everyone. Both can run at once — the bus fans out replies per channel.

🖼️

Images

joshbot can also send files out: ask it for a report, a log or a chart and it arrives as a real Telegram attachment — a photo inline, anything else as a document — with the type decided by sniffing the bytes. The file must be inside the workspace, resolved through the same containment walk the filesystem tool uses, so an escape via a symlink sends nothing — and the bytes are read once through that same contained handle, so nothing re-opens the path afterwards. It goes to the conversation you asked in; there is no recipient argument for the model to choose. Channels without attachment support say so and print the workspace-relative path rather than dropping it.

Attach a picture with joshbot agent -m "what is this?" --image shot.png (repeatable), or just send a photo on Telegram. The type is decided by sniffing the bytes, never by the extension or by what the sender declared — PNG, JPEG, GIF and WebP, 5 MB per image and 20 MB per request. If no configured model is known to accept images, the run fails before any provider call with an error naming the models it tried; unknown models count as not vision-capable, so a typo reads as a typo rather than a provider 400. Sessions record what was sent — type, size, SHA-256 — not the bytes.

Send a PDF on Telegram and the file itself rides the turn, read by the model rather than guessed at from its name. The bytes must start with %PDF-; limits are 8 MiB per document and 16 MiB per request, refused from the declared size before any transfer. Document reading is a narrower capability than vision and has its own model list, checked before the first network call on both the streaming and non-streaming paths — an incapable model produces an error naming it, not a provider 400. Office formats (docx, xlsx, pptx) are still refused, now saying what does work.

🔁

Resilient Multi-Provider Fallback

A transient 429 or 5xx is retried on the same provider with backoff — honouring the upstream Retry-After — before the fallback chain moves on, so one blip never silently switches the model mid-conversation. A provider that keeps failing is deprioritized for a cooldown window instead of being re-dialled on every turn, and when a fallback does answer, the reply says so in one line. Set the chain from the CLI: joshbot configure --fallback "nvidia,poolside". Common failures end with an actionable next step, not just an upstream error dump.

🔀

Named Profiles

Define profiles for a hosted model and your local Ollama, then pick one per run with --profile — on agent, gateway and preflight. A profile stores the name of the environment variable holding its key, never the key, and a typo, a disabled profile or an unset variable stops the run at startup instead of surfacing as a 401 mid-conversation. joshbot profiles list shows what each would dial and is safe to paste into a bug report.

📜

Scriptable & Agent-Testable

Every command runs non-interactively. Pipe one message with agent -m, get machine-readable output with --output-format json or NDJSON with stream-json, and thread turns with --resume. Meaningful exit codes make it safe to gate CI on.

🛡️

Redacted Output

API keys, auth headers and your home directory are stripped from logs and joshbot status before you ever see them — so a bug report is safe to paste. Conversations on disk stay verbatim, owner-only.

Get Started

One command to install

Downloads a single binary for your platform (Linux/macOS, amd64/arm64), verifies it, and points you at joshbot onboard. Open source, MIT licensed. Your data stays on your machine.

curl -fsSL https://raw.githubusercontent.com/bigknoxy/joshbot/main/install.sh | bash