Architecture

TAI is a pnpm monorepo with a small runtime core, a host CLI, a web surface, and first-party plugins that use the same extension contracts available to third parties.

The defining ideas

  1. One AgentRuntime. The runtime owns the config, the tool registry, the LLM provider, and the SQLite handle. Everything else borrows from it. The CLI, the HTTP server, the Discord channel, cron, and the workflow engine all read and write through the same instance.
  2. Durable state lives in SQLite. Sessions are scoped to their channel; tasks, projects, rooms, schedules, and memory are shared deliberately through their own records instead of leaking one chat history everywhere.
  3. The agent loop is one function. chat → tool calls → chat → stop. No state machine. No DAG. Compaction, retries, and hooks wrap around it.
  4. Hot reload by default. Config, tools, and provider are references the runtime re-resolves every iteration. Edit config.yaml, save, and the next message uses the new config.
  5. Channels are registered transports. Discord is built in and Slack is a first-party plugin. Each turns an external message into an agent run and routes output back through a transport-neutral outbound registry.

Primary packages

PackagePathWhat's in it
@tailored-ai/corepackages/core/Runtime, config, tools, providers, channels, db, cron, hooks, workflows, memory.
@tailored-ai/serverpackages/server/HTTP API (Hono), SSE streaming, webhook intake, optional static-UI mount.
@tailored-ai/clipackages/cli/The tai command. REPL, one-shot, project mode, bundled web UI.
@tailored-ai/browser-mediatorpackages/browser-mediator/Bounded browser tool. Framework-agnostic; OpenAI / Anthropic / TAI adapters.
@tailored-ai/trusted-actionspackages/trusted-actions/HITL approval gateway and executor.
@tailored-ai/ui (private)packages/ui/React + Vite SPA. Bundled inside @tailored-ai/cli.
@tailored-ai/site (private)packages/site/This documentation site.

See Packages overview for the dependency graph.

The runtime

┌──────────────────────────────────────────────────────┐
│                    AgentRuntime                      │
│                                                      │
│   Config       Tools        DB         Provider      │
│   (YAML)       (Tool[])    (SQLite)   (LLM API)      │
│                                                      │
└──────────────────────────────────────────────────────┘
       ▲             ▲             ▲
       │             │             │
  ┌────┴───┐   ┌─────┴────┐   ┌────┴─────┐
  │  CLI   │   │ Discord  │   │  HTTP    │
  │  REPL  │   │ channel  │   │  + SSE   │
  └────────┘   └──────────┘   └──────────┘

The runtime is constructed once. Every entry point calls runtime.buildLoopOptions({ session, agentName, project }) and hands the result to runAgentLoop(content, opts). The same five fields above (config, tools, db, provider, plus a mutable cache) are read each iteration.

The agent loop

loop:
  send (system, history, tools)  →  provider
  receive (text | tool_calls)
  if tool_calls:
      validate args
      execute each tool with retry on transient errors
      append tool_results to history
      continue
  else:
      return text

The loop stops when the model returns plain text or hits maxToolRounds. Around the loop sit history compaction (drops older messages when the context window fills), prompt expansion ({{next_task}}, {{last_run_iso}}), beforeRun and afterRun hooks, and per-tool retry with backoff.

Source: packages/core/src/agent/loop.ts.

Persistence

One SQLite file. Its configured path is relative to the instance home, which is ~/.tailored-ai/ by default or the directory selected by $TAI_HOME / -c.

TableWhat's in it
sessionsOne row per chat. User key, model, provider, project_id, timestamps.
messagesEvery turn in every session. Tool calls and results inlined.
project_tasksThe native task backend (status, assignee, tags).
task_commentsComments per task.
projectsRegistered projects, for per-project mode.
notesThe recall memory tier: short observations, tags, embeddings.
chunksPromoted longer-form content with embeddings.
email_seenDedup ledger for the gmail tool.
cron_runsHistory of cron job executions.
agent_schedulesPersistent one-shot and recurring wakes agents booked for themselves.
rooms, room_subscriptions, room_messagesShared-room definitions, membership, cursors, and transcripts.
workflow_runs, workflow_stepsWorkflow telemetry.
digest_runsAutopilot morning-digest archive.
exploratory_runsOnline tick history (one row per tick).
autopilot_settingsDigest time, memory-sweep TTL.
actions, subscriptions, audit_logTrusted-actions executor (if installed).

better-sqlite3 is synchronous. The hot path uses prepared statements.

Factories

packages/core/src/factories.ts is the composition layer. Three pure builders:

  • createTools(config, contextDir, configPath, opts) builds enabled tools from the registered factories and config.tools.* blocks.
  • createProvider(config, providerId?) resolves a registered provider id and returns its provider plus default model.
  • createMetaTools(opts) returns tools that depend on a runtime reference: delegate, task_status, admin, claude_code, discord_dm.

The CLI loads registry plugins, builds the AgentRuntime, then starts channels, HTTP, cron, schedules, rooms, workflows, MCP, and background workers against that shared runtime.

Online mode

An agent with online: { enabled: true } runs a background tick on a cadence when the global exploratory.enabled switch is also on. Each tick reads the agent's goals.md, runs the loop with a narrowed tool allowlist and budgets, and either acts or backs off. The default cadence starts at 30 minutes and doubles after no-op ticks up to four hours.

The runtime calls this online mode externally. The internal module is packages/core/src/exploratory/; the rows in exploratory_runs are the audit trail. See Online agents for configuration and limits.

There's a separate autopilot worker (packages/core/src/autopilot/) for cron-like jobs that aren't tied to a user message: the morning digest (8am by default), the daily memory sweep (3:14am), the stuck-task scan (every 15 minutes). Autopilot doesn't run an agent loop; it executes SQL.

Sandboxes and worktrees

Tool execution that touches the host (exec, write) can be sandboxed. Three backends:

BackendWhen to use
hostNo sandbox. Tools run in the same process. The default.
dockercreateSandbox({ kind: "docker", image: "node:20" }). Commands run inside a container.
podmanSame shape as docker, rootless.

Used by the coder workflow: writes code in an isolated git worktree, runs typecheck and tests in a Docker sandbox, then offers the branch for review. Worktrees are managed via createWorktree({ strategy }).

Deep dive: docs/sandboxes-and-worktrees.md.

Layout

packages/
├── core/src/
│   ├── index.ts                 # barrel exports
│   ├── runtime.ts               # AgentRuntime
│   ├── factories.ts             # createTools / createProvider / createMetaTools
│   ├── config.ts                # YAML loader, validation, types
│   ├── context.ts               # context / memory file loader
│   ├── agent/                   # loop, agents, session, compact, hooks, prompt
│   ├── providers/               # openai_compatible, embedding, registry
│   ├── tools/                   # built-in tools + interface
│   ├── channels/                # discord, interface
│   ├── triggers/                # file-drop, email-poll, rss-poll, calendar-poll, etc.
│   ├── memory/                  # recall tier, embeddings, sweep
│   ├── workflows/               # loader, engine, executors
│   ├── tasks/                   # backends: native, github, beans, beads
│   ├── projects/                # per-project resolution
│   ├── sandboxes/               # host, docker, podman
│   ├── cron/                    # scheduler + cron_runs
│   ├── autopilot/               # digest, memory sweep, stuck-task scan
│   └── exploratory/             # online tick worker
├── server/src/index.ts          # Hono routes
├── cli/src/                     # arg parsing, REPL, service orchestration
├── browser-mediator/src/        # mediator, egress, sanitizer, hitl, adapters
└── trusted-actions/src/         # gateway, executor, adapters

Where to read next

  • Agents for the agent definition shape.
  • Tools for the built-in tool catalog.
  • Memory for the recall / chunks / core_memory tiers.
  • Channels for Discord, HTTP, and how to write your own.