太刀tachi-agent · the hub

Architecture.

One small orchestrator at the center. Every front-end calls orchestrator.run(task); the hub recalls memory, runs a bounded ReAct loop, logs the trace, and halts cleanly. dokoro gives tachi-agent persistent memory. TachiBot gives it better judgment — TachiBot orchestrates models; tachi-agent orchestrates work.

01 · The hub flow

Front-ends in, three seams out.

The ReAct loop lives in the client — never inside an MCP server — so there's no agent-inside-a-server recursion and no hidden control flow on the wire.

// front-ends
CLIREPLTelegramSlackClaude Code (MCP)GatewayOpenClaw
orchestrator.run(task)▼
Orchestrator — the hubstateless between runs
recall
dokoro.recall(session)
ReAct loop ↻
reason → act → observe
log
dokoro.log(trace)
HALT guard · maxIterations · timeoutMs · AbortSignal
▼
▍ seam · the brain
Driver
default: Qwen2.5 / Ollama
swap: Hermes · cloud · OpenClaw · Kimi swarm
registerDriver()
▍ seam · the tools
ToolHost
merged MCP tools, namespaced
dokoro_* + tachibot_*
config, not code · allowlist
▍ seam · the context
Memory
dokoro — session store
recall / log
swappable · optional
tool results → next reasoning step (bounded) · progress streams to stderr · final answer prints to stdout
02 · The three seams, in depth

Swap any seam. Never touch the core.

The complete public API is three interfaces — see src/types.ts. Because the orchestrator depends only on these, every integration composes the same hub.

SeamSwap it to…Default
Driver · the brainPick a registered heart via TACHI_DRIVER — ollama · hermes · openai · openrouter — or register your own via registerDriver (a cloud model, OpenClaw, a Kimi swarm). A queued task's driver field overrides per task.local Qwen2.5 / Ollama (native /api/chat)
ToolHost · the toolsAdd or remove MCP servers and tools — config, not code. An allow allowlist keeps dangerous tools out unless granted.dokoro + tachibot merged over stdio, namespaced ${server}_${tool}
Memory · the contextSwap the persistent-context backend or disable it entirely. The orchestrator is stateless between runs.dokoro session recall / log
The three layers

dokoro ↔ tachi-agent ↔ TachiBot.

Memory, runtime, and reasoning. Each is independently replaceable; none depends on the internals of another.

memory
Persistent session memory. tachi-agent calls dokoro.recall before each run and dokoro.log after — bookending the loop with durable context. Opt in to in-loop recall and per-step note writes for long multi-step tasks.
runtime
tachi-agent
The ReAct loop that orchestrates work: drives the bounded reason / act / observe cycle, owns the task queue and daemon, manages skills, and surfaces the REPL, CLI, Telegram, and Gateway front-ends.
reasoning
The multi-model council that orchestrates models. tachi-agent calls tachibot_jury, tachibot_council, tachibot_grok_search and other council tools over MCP — TachiBot handles provider routing and adjudication.

“TachiBot orchestrates models; tachi-agent orchestrates work.”

Tools auto-appear

Whatever you connect, the agent can call.

Tools come from whatever MCP servers are connected — all config, not code. Connect tachibot and dokoro and these surface automatically, namespaced and ready. The agent ships a small curated allowlist (TACHI_ALLOW); set it to tachibot_,dokoro_ to expose everything.

auto-discovered toolsshell
1# namespaced ${server}_${tool} — no registration in code
2tachibot_jury
3tachibot_council
4tachibot_grok_search
5tachibot_perplexity_ask
6tachibot_nextThought
7tachibot_execute_prompt_technique
8tachibot_workflow
9dokoro_session_recall
10# …add a server in config and its tools join the loop
03 · The ReAct loop

Recall → reason & act → log. Always bounded.

Recall
The hub asks dokoro for relevant session context before the first reasoning step.
Reason & act
The driver reasons and optionally calls allowlisted tools; results feed the next step.
Log
The trace is written back to dokoro for the next run to recall.
Bounded
A run halts at maxIterations, a wall-clock timeoutMs, or a cooperative AbortSignal — Ctrl-C yields haltedBy: "aborted".
memory bookend · recall … log
▍ memory · before first step
recall
dokoro.recall(session) → prior context
▼
▍ the heart · bounded
reason & act loop
while ( iter < maxIterations
  && elapsed < timeoutMs
  && !signal.aborted )
reason → maybe call tool → observe ↻
guard fails▼
▍ halt — exactly one reason
halt
final-answer · maxIterations · timeout · aborted
▼
▍ memory · after halt
log
dokoro.log(session, trace)
recall → loop → log · always bounded
04 · Memory in the loop (opt-in)

Bookend by default. In-loop when you need it.

By default memory is a bookend — recall once before the loop, log once after. Set memoryInLoop: true in OrchestratorOptions to go further.

// bookenddefault
recall · once
[ ReAct loop ] reason → act → observe ↻
no memory mid-loop
log · once
// in-loopopt-in · off by default
recall · once
[ ReAct loop ] reason → act → observe ↻
↻ per-iteration recall → live memory (rewritten in place)↳ each step: note → shared_note_append
log · once
Per-iteration recall
The hub refreshes context each tool-calling step, keyed on the evolving conversation. A single “live memory” block is rewritten in place — no context bloat.
Per-step notes
Each iteration writes a working-memory note via Memory.note → dokoro shared_note_append — the append-only, agent-tagged blackboard.
Off by default
The extra per-step tool calls degrade small local models; zero behavior change for existing callers. Enable it for long, multi-step tasks.
opt ints
1const result = await createOrchestrator({
2 driver,
3 host,
4 memory,
5 options: {
6 maxIterations: 20,
7 timeoutMs: 120_000,
8 memoryInLoop: true, // opt-in
9 },
10}).run("audit every ADR and flag gaps");
05 · The daemon — standalone foundation

Around the hub: a daemon that owns the unattended machinery.

The hub stays a per-run unit. The daemon wraps it — queue, worker, schedules, event log, notifiers. TACHI_DRIVER sets the default heart; each queued task can override it.

durable work
Queue + worker
A persistent, crash-safe task queue (.tachi/queue.json) and a worker that drains it. Interrupted tasks re-queue on restart; failures retry with exponential backoff. POST /tasks enqueues from outside.
recurrence
Schedules
A hand-edited .tachi/schedules.json (daily at HH:MM or every N minutes), re-read live each tick; machine state kept in a separate -state.json. Due entries feed the same queue.
pluggable hearts
Driver registry
TACHI_DRIVER picks the default brain — ollama · hermes · openai · openrouter — and a task's driver field overrides it per task. Explicit selection only: an unavailable heart fails the task loudly, never silently substitutes.
observability
Event log + notifiers
Every run appends to a durable JSONL log in .tachi/runs/, and TACHI_NOTIFY pushes each task outcome to Telegram / Slack — the record and the ping for runs nobody watched.
multi-tenant
One orchestrator per tenant
One Orchestrator, one AbortController, and one dokoro session / workspace id per tenant. The orchestrator is stateless between runs, so N tenants = N independent run() calls with no shared state. Don't share memory across tenants — scope recall / log by tenant session id — and use per-tenant tool allowlists and per-tenant maxIterations / timeoutMs as rate and cost limits.