npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ladybugs/bud

v0.4.0

Published

TypeScript AI agent orchestration framework

Readme

Bud

A TypeScript framework for building AI agent orchestration systems with type-safe tools, agents, and state-machine workflows.

Features

  • Tools - Validated, reusable functions with Zod schemas and automatic retry logic
  • Agents - AI-powered entities with tool-calling loops, memory, output mapping, and nested agent support
  • Workflows - State machine orchestration with suspend/resume capability
  • Panel - Web UI for monitoring, debugging, and executing components in real-time
  • Type Safety - Full TypeScript support with Zod validation throughout
  • Observable - Event-driven architecture with execution tracing
  • Flexible Storage - In-memory (development) or MongoDB (production)
  • Pluggable Model Clients - Built-in support for OpenAI-compatible APIs, or bring your own (AWS Bedrock, etc.)

Installation

npm install @ladybugs/bud

Quick Start

import { createBud, MemoryStorage, ChatCompletionsModelClient } from '@ladybugs/bud';
import { z } from 'zod';

// Create a Bud instance
const bud = createBud({
  name: 'my-app',
  storage: new MemoryStorage(),
  model: new ChatCompletionsModelClient({
    baseURL: 'https://openrouter.ai/api/v1',
    apiKey: process.env.OPENROUTER_API_KEY!,
    model: 'openai/gpt-4o-mini',
  }),
  dependencies: {},
  panel: { port: 3001 }, // Optional: enables web UI
});

// Define a tool
const calculator = bud.defineTool({
  name: 'calculator',
  description: 'Performs arithmetic operations',
  input: z.object({
    operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
    a: z.number(),
    b: z.number(),
  }),
  output: z.object({ result: z.number() }),
  execute: async ({ input }) => {
    const ops = {
      add: (a: number, b: number) => a + b,
      subtract: (a: number, b: number) => a - b,
      multiply: (a: number, b: number) => a * b,
      divide: (a: number, b: number) => a / b,
    };
    return { result: ops[input.operation](input.a, input.b) };
  },
});

// Define an agent that uses the tool
const mathAgent = bud.defineAgent({
  name: 'math-assistant',
  description: 'Helps with math problems',
  variables: z.object({ question: z.string() }),
  systemPrompt: async () => 'You are a helpful math assistant. Use the calculator tool to solve problems.',
  conversation: async ({ variables }) => [
    { role: 'user', content: variables.question },
  ],
  tools: [calculator],
  output: z.object({
    answer: z.number(),
    explanation: z.string(),
  }),
  maxSteps: 5,
});

// Run the agent
const result = await mathAgent.run({
  variables: { question: 'What is 15 * 7 + 23?' },
  source: 'code',
});

console.log(result.output);
// { answer: 128, explanation: "15 * 7 = 105, then 105 + 23 = 128" }

// Start the panel (if configured)
await bud.getPanel()?.start();

Core Concepts

Tools

Tools are atomic units of work with validated inputs and outputs.

const fetchUser = bud.defineTool({
  name: 'fetch-user',
  description: 'Fetches user data by ID',
  input: z.object({ userId: z.string() }),
  output: z.object({
    id: z.string(),
    name: z.string(),
    email: z.string(),
  }),
  // Optional: tool-specific context
  context: z.object({
    cache: z.record(z.any()).default({}),
  }),
  execute: async ({ input, dependencies, context, log }) => {
    log.info('Fetching user', { userId: input.userId });
    const user = await dependencies.db.findUser(input.userId);
    return user;
  },
  // Optional: retry configuration
  retry: {
    maxAttempts: 3,
    delayMs: 1000,
    backoffMultiplier: 2,
  },
});

// Execute standalone
const user = await fetchUser.execute({ userId: 'user-123' });

Agents

Agents are AI-powered entities that execute a tool-calling loop until they produce structured output.

const researchAgent = bud.defineAgent({
  name: 'researcher',
  description: 'Researches topics using available tools',
  variables: z.object({
    topic: z.string(),
    depth: z.enum(['brief', 'detailed']).default('brief'),
  }),
  systemPrompt: async ({ dependencies }) => `
    You are a research assistant.
    Current date: ${new Date().toISOString()}
  `,
  conversation: async ({ variables }) => [
    { role: 'user', content: `Research: ${variables.topic} (${variables.depth})` },
  ],
  tools: [searchTool, fetchTool, summarizeTool],
  output: z.object({
    summary: z.string(),
    sources: z.array(z.string()),
    confidence: z.number().min(0).max(1),
  }),
  maxSteps: 10,
  // Optional: persistent memory
  memory: {
    key: 'researcher-memory',
    maxEntries: 100,
  },
});

const result = await researchAgent.run({
  variables: { topic: 'quantum computing', depth: 'detailed' },
  source: 'code',
});

console.log(`Answer: ${result.output.summary}`);
console.log(`Steps: ${result.steps}`);
console.log(`Tokens: ${result.tokenUsage.totalTokens}`);

// Resume the conversation
const followUp = await result.resume({
  message: 'Can you elaborate on quantum entanglement?',
});

Output Mapping: Transform agent output to a different structure:

const dataAgent = bud.defineAgent({
  name: 'data-fetcher',
  description: 'Fetches and processes data',
  variables: z.object({ query: z.string() }),
  systemPrompt: async () => 'You fetch data and return it as JSON.',
  conversation: async ({ variables }) => [
    { role: 'user', content: variables.query },
  ],
  tools: [fetchTool],
  // Schema for what the AI model returns
  output: z.object({
    rawData: z.string(),
    metadata: z.object({ source: z.string() }),
  }),
  // Transform the output after validation
  map: async (output, { variables, context, dependencies }) => {
    // Access to variables, context, and dependencies
    const processed = await dependencies.processor.transform(output.rawData);
    return {
      data: processed,
      query: variables.query,
      fetchedAt: new Date().toISOString(),
    };
  },
  maxSteps: 5,
});

const result = await dataAgent.run({ variables: { query: 'latest news' } });
// result.result has the mapped type: { data: ..., query: ..., fetchedAt: ... }

The map function:

  • Receives the validated output and { variables, context, dependencies }
  • Returns a Promise with the transformed output
  • Is applied on both run() and resume() calls
  • Returns the mapped output when the agent is used as a tool via toTool()

Nested Agents: Agents can use other agents as tools:

const orchestrator = bud.defineAgent({
  name: 'orchestrator',
  tools: [researchAgent, writerAgent, reviewerAgent], // Agents as tools
  // ...
});

Workflows

Workflows are state machines that orchestrate tools and agents with suspend/resume capability.

const approvalWorkflow = bud.defineWorkflow({
  name: 'approval-process',
  description: 'Document approval workflow',
  input: z.object({
    documentId: z.string(),
    requesterId: z.string(),
  }),
  context: z.object({
    status: z.string().default('pending'),
    approvals: z.array(z.string()).default([]),
    rejectionReason: z.string().optional(),
  }),
  suspendReasons: {
    awaitingApproval: {
      data: z.object({ documentId: z.string(), approver: z.string() }),
      resume: z.object({ approved: z.boolean(), comment: z.string().optional() }),
    },
  },
  output: z.object({
    approved: z.boolean(),
    finalStatus: z.string(),
  }),
  states: {
    start: {
      description: 'Initialize workflow',
      handler: async ({ input, context, workflow }) => {
        context.update((ctx) => {
          ctx.status = 'in-review';
        });
        return workflow.step('review');
      },
    },
    review: {
      description: 'Wait for approval',
      handler: async ({ input, workflow }) => {
        return workflow.suspend('awaitingApproval', {
          resumeAt: 'processDecision',
          data: { documentId: input.documentId, approver: '[email protected]' },
        });
      },
    },
    processDecision: {
      description: 'Process approval decision',
      handler: async ({ context, workflow, resume }) => {
        const { approved, comment } = resume!.data;
        if (approved) {
          context.update((ctx) => {
            ctx.status = 'approved';
            ctx.approvals.push('manager');
          });
          return workflow.finish({ approved: true, finalStatus: 'approved' });
        } else {
          context.update((ctx) => {
            ctx.status = 'rejected';
            ctx.rejectionReason = comment;
          });
          return workflow.finish({ approved: false, finalStatus: 'rejected' });
        }
      },
    },
  },
});

// Start the workflow
const result = await approvalWorkflow.start({
  input: { documentId: 'doc-123', requesterId: 'user-456' },
});

if (result.type === 'suspended') {
  console.log(`Workflow suspended: ${result.suspendReason}`);
  console.log(`Waiting for: ${result.data.approver}`);

  // Later, resume with approval decision
  const finalResult = await result.resume({
    reason: 'awaitingApproval',
    data: { approved: true, comment: 'Looks good!' },
  });

  if (finalResult.type === 'completed') {
    console.log(`Final status: ${finalResult.result.finalStatus}`);
  }
}

Running agents within workflows:

states: {
  analyze: {
    handler: async ({ input, workflow }) => {
      const analysis = await workflow.run(analyzerAgent, {
        variables: { documentId: input.documentId },
      });
      return workflow.step('nextState');
    },
  },
},

Panel UI

The Panel provides a web-based interface for monitoring and interacting with your Bud components.

const bud = createBud({
  // ... other config
  panel: {
    port: 3001,
    host: 'localhost',
    title: 'My App Dashboard',
    theme: 'dark', // or 'light'
    auth: {
      username: 'admin',
      password: 'secret',
    },
  },
});

// Components are auto-registered when defined
const tool = bud.defineTool({ /* ... */ });
const agent = bud.defineAgent({ /* ... */ });
const workflow = bud.defineWorkflow({ /* ... */ });

// Start the server
await bud.getPanel()?.start();
// Access at http://localhost:3001

Panel Features:

  • View all registered tools, agents, and workflows
  • Execute tools and agents directly from the UI
  • Monitor workflow instances and their states
  • View execution traces and timelines
  • Real-time updates via Server-Sent Events
  • Agent run history with nested run tracking

Storage

Memory Storage (Development)

import { MemoryStorage } from '@ladybugs/bud';

const storage = new MemoryStorage();

// Clear for tests
storage.clear();

MongoDB Storage (Production)

import { MongoStorage } from '@ladybugs/bud';

const storage = new MongoStorage({
  uri: 'mongodb://localhost:27017',
  database: 'my-app',
  collections: {
    events: 'bud_events',
    logs: 'bud_logs',
    memory: 'bud_memory',
  },
});

await storage.connect();

// Later...
await storage.disconnect();

Configuration

BudConfig

interface BudConfig<TDependencies> {
  name: string;                    // Instance name
  storage: BudStorage;             // Storage adapter
  model: IModelClient;             // AI model client
  dependencies: TDependencies;     // Injected dependencies
  additionalLoggers?: BudLogger[]; // Extra loggers
  retry?: RetryConfig;             // Default retry config
  panel?: PanelOptions;            // Panel settings
}

Model Client

Bud uses the IModelClient interface for AI model communication. Use the built-in ChatCompletionsModelClient for OpenAI-compatible APIs, or implement your own (e.g., for AWS Bedrock).

import { ChatCompletionsModelClient } from '@ladybugs/bud';

// OpenAI-compatible APIs (OpenRouter, OpenAI, Ollama, etc.)
const model = new ChatCompletionsModelClient({
  baseURL: string;       // API base URL
  apiKey: string;        // API key
  model: string;         // Model identifier
  temperature?: number;  // 0-2, default 0.7
  maxTokens?: number;    // Maximum response tokens, default 4096
  contextWindow?: number; // Context window size (required for compact feature)
});

// Custom model client (e.g., AWS Bedrock)
const model: IModelClient = {
  chat: async (params) => { /* ... */ },
  getContextWindow: async () => 200000,
};

RetryConfig

interface RetryConfig {
  maxAttempts: number;       // Max retry attempts
  delayMs: number;           // Initial delay
  backoffMultiplier: number; // Multiplier for each retry
}

Error Handling

Bud provides specific error classes for different failure modes:

import {
  // Tool errors
  ToolInputValidationError,
  ToolOutputValidationError,
  ToolExecutionError,

  // Agent errors
  AgentMaxStepsError,
  AgentOutputValidationError,
  AgentToolNotFoundError,

  // Workflow errors
  WorkflowStateError,
  WorkflowNotFoundError,
  WorkflowCancelledError,

  // Model errors
  ModelApiError,
} from '@ladybugs/bud';

try {
  await agent.run({ variables: { query: 'test' }, source: 'code' });
} catch (error) {
  if (error instanceof AgentMaxStepsError) {
    console.log('Agent exceeded step limit');
  } else if (error instanceof ToolExecutionError) {
    console.log('Tool failed:', error.message);
  }
}

Logging

import { ConsoleLogger } from '@ladybugs/bud';

const bud = createBud({
  // ... config
  additionalLoggers: [
    new ConsoleLogger({ level: 'debug' }),
  ],
});

// In tool/agent handlers
execute: async ({ log }) => {
  log.debug('Debug message', { data: 'value' });
  log.info('Info message');
  log.warn('Warning message');
  log.error('Error message', new Error('Something failed'));
};

Development

# Install dependencies
npm install

# Build TypeScript
npm run build

# Run unit tests
npm test

# Run integration tests (requires .env.development)
npm run test:integration

# Build panel UI
npm run build:panel

# Start panel dev server
npm run panel:dev

Environment Variables

Create .env.development for integration tests:

AI_PROVIDER_API_KEY=your-api-key
AI_PROVIDER_BASE_URL=https://openrouter.ai/api/v1
AI_PROVIDER_MODEL=openai/gpt-4o-mini

License

MIT