tomo

module
v0.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 9, 2026 License: MIT

README

tomo (友)

ci Release Go Reference Go Report Card License

tomo (友, "companion") is a personal AI agent that lives on your own machine, one static Go binary, no server you don't run yourself. It sits between your chat apps and a language model: you text it on Telegram, Discord, Slack, or iMessage, or open the local web chat, and it remembers you across conversations and acts on real tools, running commands, reading and writing files, fetching pages, saving its own memory, scheduling work for later.

InstallQuick startModelsWhat it doesThe Hi! baselineDocsSafety

tomo showing its command surface, then writing a starter config with onboard and starting the daemon with the local web chat

Most agent tools ask you to hand over your history, your files, and your provider key to somebody else's server. tomo doesn't. It runs where you run it, keeps its state in a sqlite file and a folder of plain markdown next to it, and the only thing that leaves your machine is the model call you would have made anyway. Every action it takes passes a fail-closed policy gate first, and the moment a turn pulls in content from outside (a fetched page, a tool's output), the session is tainted, so an injected instruction in that content can't quietly reach your shell.

Install

go install github.com/tamnd/tomo/cmd/tomo@latest

Prefer a prebuilt binary? Grab an archive, a .deb/.rpm/.apk, or a checksum from releases. A container image ships too:

docker run --rm -it -v ~/.tomo:/root/.tomo ghcr.io/tamnd/tomo chat

Check what you got with tomo version, which prints the build's commit and date alongside the version, not just a bare number.

Quick start

tomo onboard   # writes ~/.tomo/config.yaml and walks you through a provider
tomo chat      # talk from the terminal
tomo serve     # web chat on localhost, plus every channel you configured

That's the whole loop: pick a provider once, then talk to tomo from wherever you already are. tomo -p "summarize CHANGES.md" runs one prompt non-interactively and exits, for scripts and pipelines.

Models

tomo is model-agnostic. The Anthropic API is native, and anything speaking the OpenAI chat completions dialect works through base_url: a local llama.cpp or ollama, a LAN inference box, or any hosted gateway.

default_model: anthropic/claude-fable-5
providers:
  anthropic:
    type: anthropic
    api_key: ${ANTHROPIC_API_KEY}
  local:
    type: openai
    base_url: http://gamingpc:8000/v1
    api_key: ${LOCAL_API_KEY}

A model is named provider/model, so the local provider above serves models like local/qwen2.5-coder. Set default_model to any of them, or override it per worker.

What it does

  • Speaks through the chat apps you already use. The local web chat is always on; Telegram, Discord, Slack, and iMessage start when you configure them, each with its own allow-list.
  • Acts with real tools, behind a gate. Reads and network calls run on their own; writes and code execution ask first, and anything you haven't allowed is declined outright. Once a turn pulls in untrusted content, writes and execution escalate to ask even if they were previously allowed. An optional OS-level sandbox can bound an approved command to its working tree and off the network entirely.
  • Remembers you across conversations. A markdown memory tomo reads and writes itself, with a curator that reflects after substantial turns and stamps each note with where it came from, so you can read your own agent's notes about you.
  • Gets better at your workflows. It follows skills you write and drafts new ones from workflows it sees you repeat. Installing a drafted skill is always your call, never automatic.
  • Works on its own when you want. Scheduled prompts and a heartbeat pick up standing work on a cadence and report back only when there's something worth saying.
  • Runs many agents when one isn't enough. Named workers with their own persona, model, policy, and memory, routed by @name or by channel, and one worker can hand a task off to another.
  • Speaks and listens. Optional local voice: whisper transcribes voice notes in, piper speaks replies back, all on your machine.
  • Talks to other tools over MCP. Attach MCP servers to extend its toolset, or serve tomo's own tools to Claude Code and other MCP clients.

The Hi! baseline

tomo-labs runs tomo and six other coding agents through the same tasks on the same free model, through a proxy that forces deterministic decoding and records every request verbatim, so the only thing that differs between rows is the tool. Its simplest scenario, 00-hello, is just the prompt Hi!, no task beyond completing the round trip, which isolates the fixed cost every tool pays before it does any real work:

tool tokens cached ttfb rss wall requests
tomo 1,153 1,024 761ms 12MB 2s 2
gemini-cli 8,001 2,048 1090ms 363MB 5s 3
codex 7,593 7,424 852ms 92MB 2s 2
opencode 7,260 7,168 779ms 643MB 3s 3
openclaw 16,770 7,552 1129ms 362MB 32s 2
claude-code 19,183 19,072 1105ms 287MB 2s 3
hermes 13,611 981ms 122MB 19s 26

tomo's floor is roughly a tenth of the next tool's, because its system prompt and tool schema are smaller and it doesn't re-read its own context to say hello. hermes's 26 requests on a one-line greeting is an outlier worth flagging rather than hiding: its scenario runner retries in a loop that a plain acknowledgment doesn't satisfy on the first try, which also explains openclaw's 32-second wall time against everyone else's low single digits.

See the full scenario suite, install footprints, and pass rates for all seven tools at tamnd/tomo-labs.

How it's built

chat message ─▶ channel adapter ─▶ gateway daemon ─▶ policy gate ─▶ tool ─▶ reply
                (Telegram, etc)     (owns the session)  (allow/ask/deny)

A gateway daemon owns sessions in a sqlite ledger. Channel adapters are thin: they turn a platform's messages into events and render replies back, nothing more. Tools are typed and classified (read, net, write, exec), and a policy engine decides allow, ask, or deny on every call, with approvals answered right in the channel you're talking on. Memory is plain markdown you can read and edit yourself, no vector store to inspect.

Building from source

git clone https://github.com/tamnd/tomo
cd tomo
go build -o bin/tomo ./cmd/tomo
go test ./...

The repo is split by concern:

cmd/tomo/    thin main: wires signal handling, then hands off to cli.Execute
cli/         the cobra command tree, flags, and version reporting
pkg/agent/   the turn loop: model call, tool dispatch, policy check
pkg/policy/  the allow/ask/deny gate and the taint tracking
pkg/channel/ Telegram, Discord, Slack, iMessage, and the local web chat
pkg/tool/    the built-in tool surface: shell, files, fetch, memory
pkg/memory/  the markdown memory store and its curator
pkg/schedule/ cron-style scheduled prompts and the heartbeat
pkg/mcp/     MCP client (attach servers) and MCP server (serve tomo's tools)
pkg/provider/ model providers: Anthropic native, OpenAI-compatible via base_url
pkg/wire/    stdlib-only translators between chat-completions and other LLM
             wires (Anthropic Messages, OpenAI Responses, Gemini)
pkg/sandbox/ the optional OS-level exec sandbox
pkg/store/   the sqlite session ledger
docs/        the tago documentation site, published at tomo.tamnd.com

Releasing

Push a version tag and GitHub Actions runs GoReleaser, which builds the archives, the .deb/.rpm/.apk packages, a multi-arch GHCR image, checksums, and a cosign signature:

git tag v0.3.0
git push --tags

Full documentation, including the policy and safety model in full, lives at tomo.tamnd.com.

License

MIT

Directories

Path Synopsis
Package cli wires tomo's command surface: the cobra tree, the global flags, and the fang-rendered help and errors.
Package cli wires tomo's command surface: the cobra tree, the global flags, and the fang-rendered help and errors.
cmd
tomo command
Command tomo is a personal AI agent in one binary: chat channels in front, any model behind, and a policy gate on every action it takes.
Command tomo is a personal AI agent in one binary: chat channels in front, any model behind, and a policy gate on every action it takes.
pkg
agent
Package agent runs the conversation loop: send the history, stream the reply, execute whatever tools the model called, feed the results back, and repeat until the model ends its turn.
Package agent runs the conversation loop: send the history, stream the reply, execute whatever tools the model called, feed the results back, and repeat until the model ends its turn.
anttool
Package anttool exposes ant's URI front door as tomo tools.
Package anttool exposes ant's URI front door as tomo tools.
builtin
Package builtin holds the tools tomo ships with: run a command, read and write files, fetch a URL as Markdown, tell the time.
Package builtin holds the tools tomo ships with: run a command, read and write files, fetch a URL as Markdown, tell the time.
channel
Package channel is the front-door abstraction: every place a user can talk to tomo (the web UI, Telegram, and later Discord, Slack, iMessage) is a Channel.
Package channel is the front-door abstraction: every place a user can talk to tomo (the web UI, Telegram, and later Discord, Slack, iMessage) is a Channel.
channel/discord
Package discord is tomo's Discord channel.
Package discord is tomo's Discord channel.
channel/imessage
Package imessage is a no-op everywhere but macOS.
Package imessage is a no-op everywhere but macOS.
channel/slack
Package slack is tomo's Slack channel.
Package slack is tomo's Slack channel.
channel/telegram
Package telegram is tomo's Telegram channel.
Package telegram is tomo's Telegram channel.
channel/webchat
Package webchat is tomo's built-in channel: a small single-page UI served on localhost and a streaming endpoint behind it.
Package webchat is tomo's built-in channel: a small single-page UI served on localhost and a streaming endpoint behind it.
config
Package config loads ~/.tomo/config.yaml: providers, the default model, and agent knobs.
Package config loads ~/.tomo/config.yaml: providers, the default model, and agent knobs.
curator
Package curator runs a reflection pass after a substantial exchange.
Package curator runs a reflection pass after a substantial exchange.
doctor
Package doctor runs tomo's startup preconditions as a set of named checks: the config resolves a usable provider, the data dir is writable, and every configured channel has a driver.
Package doctor runs tomo's startup preconditions as a set of named checks: the config resolves a usable provider, the data dir is writable, and every configured channel has a driver.
kitbridge
Package kitbridge turns a tamnd/*-cli built on the kit framework into tomo tools.
Package kitbridge turns a tamnd/*-cli built on the kit framework into tomo tools.
mcp
Package mcp is a small Model Context Protocol client.
Package mcp is a small Model Context Protocol client.
memory
Package memory is tomo's long-term store: MEMORY.md is a one-line-per-fact index that rides in the system prompt, and each fact's detail lives in its own markdown topic file.
Package memory is tomo's long-term store: MEMORY.md is a one-line-per-fact index that rides in the system prompt, and each fact's detail lives in its own markdown topic file.
netguard
Package netguard classifies a listen address as loopback-only or not, so the web and control surfaces can stay private by default and warn loudly when a deployment binds them where the world can reach.
Package netguard classifies a listen address as loopback-only or not, so the web and control surfaces can stay private by default and warn loudly when a deployment binds them where the world can reach.
policy
Package policy is the fail-closed gate every tool call passes through.
Package policy is the fail-closed gate every tool call passes through.
provider
Package provider defines the model-neutral message types and the streaming interface every model backend implements.
Package provider defines the model-neutral message types and the streaming interface every model backend implements.
readable
Package readable turns an HTML page into clean Markdown.
Package readable turns an HTML page into clean Markdown.
sandbox
Package sandbox confines an exec-class command to a policy the kernel enforces.
Package sandbox confines an exec-class command to a policy the kernel enforces.
schedule
Package schedule parses cron expressions and runs the ledger's jobs when they come due.
Package schedule parses cron expressions and runs the ledger's jobs when they come due.
skill
Package skill loads markdown skills that teach tomo a workflow.
Package skill loads markdown skills that teach tomo a workflow.
store
Package store persists sessions and their messages in one sqlite file, the ledger everything else replays from.
Package store persists sessions and their messages in one sqlite file, the ledger everything else replays from.
tool
Package tool holds the registry of actions the agent may take.
Package tool holds the registry of actions the agent may take.
voice
Package voice carries speech both ways.
Package voice carries speech both ways.
wire
Google Gemini generateContent API translation.
Google Gemini generateContent API translation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL