botinabox

The problem

Building multi-agent bots from scratch is hard.

You need task routing, priority queues, retry logic, cost tracking, multi-channel messaging, workflow orchestration, session management, and security — before you even get to the LLM call. Most teams end up building the same infrastructure over and over.

Bot in a Box gives you all of that in a single npm package. Define your agents, wire up your channels, and focus on what your bot actually does.

Without a framework

  • Manual task routing with custom if/else chains
  • No budget controls — surprise API bills
  • Per-channel glue code for Slack, Discord, webhooks
  • No retry logic — failed tasks are lost
  • No workflow support — everything is sequential
  • In-memory state — lost on restart
  • Security bolted on after the fact (or not at all)

With Bot in a Box

  • Declarative agents with automatic task assignment
  • Per-agent and global budget enforcement
  • Unified channel adapters — one interface, any platform
  • Retry with exponential backoff and followup chains
  • DAG workflows with parallel execution
  • SQLite-backed state — survives restarts
  • Input sanitization, audit logging, HMAC verification built in

The pattern

1

Define agents

Declare agents in YAML or TypeScript — model, role, adapter, budget.

2

Wire channels

Register Slack, Discord, or webhook adapters. Agents auto-connect.

3

Route messages

Inbound messages are parsed, user-resolved, and dispatched to agents.

4

Execute tasks

The run manager picks up tasks, calls the LLM, handles retries.

5

Return results

Output flows back through channels. State persists to SQLite.

In practice

Customer Support Bot

Tables: agents, channels, tasks, sessions

A team needs to handle support tickets across Slack and Discord with different agents for billing, technical issues, and general questions.

typescript
import {
  HookBus, DataStore, defineCoreTables,
  AgentRegistry, TaskQueue, RunManager,
  ChannelRegistry, MessagePipeline,
  SessionManager,
} from 'botinabox';
import { SlackAdapter } from 'botinabox/slack';
import { DiscordAdapter } from 'botinabox/discord';

const hooks = new HookBus();
const db = new DataStore({ dbPath: './support.db', hooks });
defineCoreTables(db);
await db.init();

const agents = new AgentRegistry(db, hooks);
const channels = new ChannelRegistry(hooks);
const sessions = new SessionManager(db);

// Register specialized agents
await agents.register({
  slug: 'billing',
  name: 'Billing Support',
  adapter: 'api',
});
await agents.register({
  slug: 'technical',
  name: 'Technical Support',
  adapter: 'api',
});

// Connect channels
channels.register(new SlackAdapter({
  botToken: process.env.SLACK_BOT_TOKEN!,
  appToken: process.env.SLACK_APP_TOKEN!,
}));
channels.register(new DiscordAdapter({
  token: process.env.DISCORD_TOKEN!,
}));

// Messages auto-route to the right agent
const pipeline = new MessagePipeline(
  agents, new TaskQueue(db, hooks), hooks
);

Insight

The message pipeline resolves users, classifies intent, and routes to the right agent — all without custom routing code.

DevOps Automation Bot

Tables: agents, workflows, schedules

An engineering team wants to automate their deploy pipeline: run tests, build, deploy to staging, wait for approval, then deploy to production.

typescript
import {
  HookBus, DataStore, defineCoreTables,
  AgentRegistry, WorkflowEngine, Scheduler,
} from 'botinabox';

const hooks = new HookBus();
const db = new DataStore({ dbPath: './devops.db', hooks });
defineCoreTables(db);
await db.init();

const agents = new AgentRegistry(db, hooks);
const workflows = new WorkflowEngine(db, hooks);
const scheduler = new Scheduler(db, hooks);

// Define a deploy workflow (DAG)
const workflowId = await workflows.create({
  slug: 'deploy-pipeline',
  name: 'Deploy Pipeline',
  steps: [
    {
      id: 'test',
      name: 'Run Tests',
      agentSlug: 'ci-agent',
      taskTemplate: {
        title: 'Run test suite',
        description: 'Execute all unit and integration tests',
      },
    },
    {
      id: 'build',
      name: 'Build',
      agentSlug: 'ci-agent',
      dependsOn: ['test'],
      taskTemplate: {
        title: 'Build artifacts',
        description: 'Compile and bundle for production',
      },
    },
    {
      id: 'deploy-staging',
      name: 'Deploy to Staging',
      agentSlug: 'deploy-agent',
      dependsOn: ['build'],
      taskTemplate: {
        title: 'Deploy to staging',
        description: 'Push build to staging environment',
      },
    },
  ],
});

// Schedule nightly deploys
await scheduler.create({
  slug: 'nightly-deploy',
  cron: '0 2 * * *',       // 2 AM daily
  timezone: 'America/New_York',
  action: 'workflow.run',
  actionConfig: { workflowId },
});

Insight

Steps run in parallel when dependencies allow. If "test" fails, downstream steps are skipped automatically based on the failure policy.

Research Assistant

Tables: agents, providers, budget, tasks

A research team wants multiple agents using different LLM providers — a fast model for classification, a powerful model for synthesis — with strict cost controls.

typescript
import {
  HookBus, DataStore, defineCoreTables,
  AgentRegistry, TaskQueue, RunManager,
  ProviderRegistry, ModelRouter,
  BudgetController,
} from 'botinabox';
import createAnthropicProvider
  from 'botinabox/anthropic';
import createOpenAIProvider
  from 'botinabox/openai';

const hooks = new HookBus();
const db = new DataStore({ dbPath: './research.db', hooks });
defineCoreTables(db);
await db.init();

// Register multiple providers
const providers = new ProviderRegistry();
providers.register(
  createAnthropicProvider({
    apiKey: process.env.ANTHROPIC_API_KEY!,
  })
);
providers.register(
  createOpenAIProvider({
    apiKey: process.env.OPENAI_API_KEY!,
  })
);

// Route by purpose
const router = new ModelRouter(providers, {
  default: 'claude-sonnet-4-6',
  aliases: {
    fast: 'claude-haiku-4-5',
    smart: 'claude-opus-4-6',
  },
  routing: {
    classification: 'fast',
    task_execution: 'smart',
  },
});

// Enforce budgets
const budget = new BudgetController(db, hooks, {
  globalMonthlyCents: 50000,   // $500/month
  warnPercent: 80,
});

const agents = new AgentRegistry(db, hooks);
await agents.register({
  slug: 'classifier',
  name: 'Classifier',
  adapter: 'api',
  model: 'fast',
  budgetMonthlyCents: 5000, // $50/month
});
await agents.register({
  slug: 'synthesizer',
  name: 'Synthesizer',
  adapter: 'api',
  model: 'smart',
  budgetMonthlyCents: 30000, // $300/month
});

Insight

Each agent has its own budget. When the classifier hits $50, it stops — but the synthesizer keeps running on its separate $300 budget.

Ready to build?

Install botinabox and have your first agent running in minutes.