The daemon is the standalone foundation: a persistent task queue, a worker
that drains it, recurring schedules, per-task driver selection,
outcome notifications, and a durable per-run event log.
On macOS, tachi-agent service install is the primary path — one command bootstraps a launchd LaunchAgent that starts at login and restarts on crash.
tachi-agent service install is the primary macOS daemon path. It reads env vars from your environment (or an --env-file), writes a 0600 plist under ~/Library/LaunchAgents, and bootstraps the service with launchctl.
bash — macOS service install
export GATEWAY_TOKEN="change-me"
tachi-agent service install --env-file .env # bootstrap launchd LaunchAgent# starts at login · restarts on crash# logs: ~/Library/Logs/tachi-agent/daemon.log
tachi-agent service status # check whether the agent is running
tachi-agent service uninstall # remove the plist + unload
--env-file <path> — reads GATEWAY_TOKEN + all TACHI_* vars from the file and bakes them into the plist.
--cwd <dir> — sets the daemon working directory (default ~/.tachi-agent).
The plist is written 0600 so only your user can read it — provider keys are safe at rest.
On Linux, run node dist/daemon/index.js under a systemd unit with Restart=always.
02// skills / recipes
Markdown recipes that shape what a run sees.
Drop a .md file in .tachi/skills/ (override with TACHI_SKILLS_DIR). The body becomes the system prompt; frontmatter carries the name, tool allowlist, and driver preset.
.tachi/skills/researcher.md
---
name: researcher
description: Deep-research mode — searches and synthesizes a cited report.
tools: tachibot_grok_search, tachibot_perplexity_ask, tachibot_nextThought
driver: openai
---
You are a careful research assistant. Your output must be accurate and traceable.
Activate a skill from the CLI or inside the REPL:
bash — activate a skill
tachi-agent --skill researcher "explain the CAP theorem trade-offs"# or inside the interactive REPL# /skill researcher
Ready-made recipes live in examples/skills/: repo-review, researcher, nightly-digest, fact-checker. Copy them into your project's .tachi/skills/ or point TACHI_SKILLS_DIR at the directory directly. Driver precedence: --driver flag / /driver command > skill's driver field > TACHI_DRIVER.
03┌─ interactive chat
Bare tachi-agent opens a REPL.
No arguments? You get an interactive session. The same /commands surface works in the REPL and in Telegram.
Command
What it does
/help
Show the command list
/tools
List the agent's available tools
/model
Show the current model
/status
Show session state (driver, skill, mode)
/driver <name>|off
Set or clear the session driver
/skill <name>|off
Activate or clear a skill bundle
/reset
Clear the full session (driver + skill)
/jury <question>
Run a cross-model jury verdict via tachibot_jury
/search <query>
Search with tachibot_grok_search or Perplexity
/think <question>
Reason step by step over a question
/task add <text>
Queue a task on the daemon
/task list
List queued tasks
/task show <id>
Show task detail
/schedule list
List scheduled jobs
/exit, /quit
Leave (REPL: Ctrl-D also exits)
The prompt shows the active session state: tachi [driver·skill] ›. History persists at ~/.tachi-agent/repl_history (capped at 1000 lines). Run tachi-agent doctor for a preflight check of your environment before the first session.
04┌─ the foundation
A daemon under a supervisor. A queue that survives it.
Run node dist/daemon/index.js under launchd (macOS
LaunchAgent with KeepAlive) or systemd
(Restart=always). The supervisor keeps the process alive; the queue keeps
the work alive.
The queue (.tachi/queue.json) is crash-safe: a task left
running by a dead daemon is re-queued on restart with its spent attempt
counted. Failed tasks retry with exponential backoff (default 3 attempts).
05// the queue
Queue work from outside. External cron is the scheduler.
POST /tasks enqueues durable work — unlike POST /runs, a queued task survives restarts and retries with backoff. External cron is the scheduler of record for anything calendar-driven you want outside the agent:
crontab — nightly digest at 02:30, run on the OpenAI heart
# crontab — nightly digest at 02:30, run on the OpenAI heart
30 2 * * * curl -s -X POST -H "Authorization: Bearer $TACHI_TOKEN" -H "Content-Type: application/json" \
-d '{"task":"summarize yesterdays inbox and post to slack","driver":"openai"}' http://127.0.0.1:8787/tasks
Method & path
Purpose
POST /tasks {task, driver?, maxAttempts?}
Enqueue a durable task — survives restarts, retries with exponential backoff.
GET /tasks
The live queue: status, attempts, driver, answer/error per task.
GET /tasks/:id
One task in full — including the recorded error after a loud failure.
06┌─ recurring schedules
A JSON file you edit by hand. Re-read live.
For self-contained recurrence, the daemon also evaluates .tachi/schedules.json (TACHI_SCHEDULES_FILE) every TACHI_SCHEDULES_POLL_MS (default 30s) and enqueues due entries into the same queue:
kind: "daily" + at: "HH:MM" — fires once per day, the first tick at/after that local time.
kind: "every" + everyMinutes: N — fires immediately on first sight, then every N minutes.
The file is yours. It's re-read on every tick, so hand edits apply without a restart, and the daemon never writes to it. Machine state (last-run times) lives in a separate .tachi/schedules-state.json, so a restart doesn't re-fire a schedule that already ran.
Fault-tolerant. Malformed entries are skipped with a stderr warning; a broken file never takes the daemon down.
07// multi-heart
One default brain. A different heart per task.
TACHI_DRIVER picks the daemon's default brain (ollama by default — local, private). A task's optional "driver" field overrides it for that task only: keep the local heart for interactive chat, and send a nightly heavy job to "driver": "openai" or "openrouter".
Driver
What it is
Needs
ollama
Local model via Ollama — private, offline. The default.
—
hermes
Self-hosted OpenAI-compatible endpoint.
HERMES_BASE_URL / HERMES_MODEL
openai
GPT via the OpenAI API.
OPENAI_API_KEY
openrouter
Any model behind OpenRouter.
OPENROUTER_API_KEY
bash — local heart by default, GPT for the nightly job
# the daemon's default heart stays localexport TACHI_DRIVER=ollama
# this one task runs on GPT — for that task only
curl -s -X POST -H "Authorization: Bearer $TACHI_TOKEN" -H "Content-Type: application/json" \
-d '{"task":"deep-audit the quarter logs","driver":"openai"}' http://127.0.0.1:8787/tasks
explicit by design
No silent fallback
There is no automatic routing and no silent substitution. A task
naming an unknown or unconfigured driver (e.g. openai without
OPENAI_API_KEY) fails loudly: the actionable error is
recorded on the task, normal retry/backoff applies, and the failed task stays
inspectable in the queue.
division of labor
Hearts ≠ council ≠ memory
The driver is the heart that runs the loop. Decision-making stays with the
tachibot
multi-model council tools; persistent memory stays
dokoro.
Swapping the heart changes none of that.
08┌─ notifications
The agent reaches out. You don't poll.
Set TACHI_NOTIFY and the worker pushes every task outcome —
success or failure — to those targets, using the existing
TELEGRAM_BOT_TOKEN / SLACK_BOT_TOKEN.
Live queue: status, attempts, driver, answer/error.
.tachi/queue.json
The queue on disk (survives restarts; readable JSON).
.tachi/runs/*.jsonl
Durable per-run event log (TACHI_RUN_LOG_DIR) — every step, append-only. Post-hoc debugging for runs nobody watched.
The same visibility from the CLI — point it at the daemon with
TACHI_DAEMON_URL + GATEWAY_TOKEN:
bash — queue + run-log CLI
export TACHI_DAEMON_URL="http://127.0.0.1:8787" GATEWAY_TOKEN="change-me"
tachi-agent task add "compile the weekly report" --driver openai --max-attempts 5
tachi-agent task list # id · status · attempt n/max · driver · excerpt
tachi-agent task show <id> # one task in full — answer or recorded error
tachi-agent runs log <run-id> # replay the durable JSONL event log of a run