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

@toolpack-sdk/agents

v2.3.0

Published

Production AI agents for Toolpack SDK — 8 channel integrations (Slack, Discord, Telegram, SMS, Email, Webhook, Scheduled, MCP), AgentMind persistent cognitive layer (goals, beliefs, reflections), interceptors, evals, and multi-agent coordination

Downloads

807

Readme

@toolpack-sdk/agents

Build production-ready AI agents with channels, workflows, and event-driven architecture.

npm version License: Apache 2.0

Features

  • 4 Built-in Agents — Research, Coding, Data, Browser
  • 8 Channel Types — Slack, Telegram, Discord, Email, SMS, Webhook, Scheduled, MCP
  • Event-Driven — Full lifecycle hooks and events
  • Human-in-the-Loopask() support for two-way channels
  • Knowledge Integration — Built-in RAG support with knowledge bases
  • Agent Mind — Persistent cognitive layer: goals, beliefs, reflections, cross-run recall
  • EvalsEvalDataset, EvalRunner, 4 scorer types, regression reports
  • OTel Tracing — OpenTelemetry interceptor for distributed traces
  • Type-Safe — Full TypeScript support

Installation

npm install @toolpack-sdk/agents

Stable API (Phase 4)

The following APIs are stable and follow semantic versioning. Breaking changes will require a major version bump:

  • BaseAgent — Abstract base class for all agents
  • BaseChannel — Abstract base class for all channels
  • AgentRegistry — Registry for agents and channels
  • AgentInput, AgentResult, AgentOutput — Core data structures
  • AgentTransport, LocalTransport, JsonRpcTransport — Transport layer
  • AgentJsonRpcServer — JSON-RPC server for hosting agents
  • AgentError — Error class for agent failures

Version Policy

  • Major (X.y.z) — Breaking API changes
  • Minor (x.Y.z) — New features, backward compatible
  • Patch (x.y.Z) — Bug fixes, backward compatible

Quick Start

import { BaseAgent, AgentRegistry, SlackChannel } from '@toolpack-sdk/agents';

// 1. Create a channel
const slack = new SlackChannel({
  name: 'slack',
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET,
  channel: '#support',
});

// 2. Create an agent (channels live on the agent)
class SupportAgent extends BaseAgent {
  name = 'support-agent';
  description = 'Customer support agent';
  mode = 'chat';
  channels = [slack];

  async invokeAgent(input) {
    const result = await this.run(input.message);
    return result;
  }
}

// 3. Single-agent: start directly
const agent = new SupportAgent({ apiKey: process.env.ANTHROPIC_API_KEY });
await agent.start();

// OR multi-agent: use AgentRegistry
// const registry = new AgentRegistry([agent]);
// await registry.start();

Built-in Agents

ResearchAgent

Web research for summarization, fact-finding, and trend monitoring.

import { ResearchAgent } from '@toolpack-sdk/agents';

const agent = new ResearchAgent({ apiKey: process.env.ANTHROPIC_API_KEY });
const result = await agent.invokeAgent({
  message: 'Summarize recent AI developments',
});

Mode: agent | Tools: web.search, web.fetch, web.scrape

CodingAgent

Code generation, refactoring, debugging, and test writing.

import { CodingAgent } from '@toolpack-sdk/agents';

const agent = new CodingAgent({ apiKey: process.env.ANTHROPIC_API_KEY });
const result = await agent.invokeAgent({
  message: 'Refactor the auth module',
});

Mode: coding | Tools: fs.*, coding.*, git.*, exec.*

DataAgent

Database queries, reporting, data analysis, and CSV generation.

import { DataAgent } from '@toolpack-sdk/agents';

const agent = new DataAgent({ apiKey: process.env.ANTHROPIC_API_KEY });
const result = await agent.invokeAgent({
  message: 'Generate weekly signups report',
});

Mode: agent | Tools: db.*, fs.*, http.*

BrowserAgent

Web browsing, form interaction, and content extraction.

import { BrowserAgent } from '@toolpack-sdk/agents';

const agent = new BrowserAgent({ apiKey: process.env.ANTHROPIC_API_KEY });
const result = await agent.invokeAgent({
  message: 'Extract prices from acme.com/products',
});

Mode: chat | Tools: web.fetch, web.screenshot, web.extract_links

Channels

Channels connect agents to external services. They can be two-way (receive messages, support ask()) or trigger-only (send only, no ask() support).

SlackChannel (Two-way)

const slack = new SlackChannel({
  name: 'slack-support',
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET,
  channel: '#support',
  port: 3000,
});

TelegramChannel (Two-way)

const telegram = new TelegramChannel({
  name: 'telegram-bot',
  token: process.env.TELEGRAM_BOT_TOKEN,
});

WebhookChannel (Two-way)

const webhook = new WebhookChannel({
  name: 'github-webhook',
  path: '/webhook/github',
  port: 3000,
});

ScheduledChannel (Trigger-only)

Runs agents on cron schedules. Three modes: static cron, dynamic store (agent-driven), or hybrid.

// Static — fixed cron schedule
const scheduler = new ScheduledChannel({
  name: 'daily-report',
  cron: '0 9 * * 1-5', // 9am weekdays
  message: 'Generate daily report',
});

// Dynamic — agent schedules its own jobs via scheduler.* tools
import { SchedulerStore, createSchedulerTools } from '@toolpack-sdk/agents';
const store = new SchedulerStore({ dbPath: './scheduler.db' });
const dynamic = new ScheduledChannel({ name: 'dynamic', store });

// For Slack delivery, attach a named SlackChannel to the same agent and
// call `this.sendTo('<slackChannelName>', output)` from within `invokeAgent()`.

DiscordChannel (Two-way)

const discord = new DiscordChannel({
  name: 'discord-bot',
  token: process.env.DISCORD_BOT_TOKEN,
  guildId: 'your-guild-id',
  channelId: 'your-channel-id',
});

EmailChannel (Outbound-only)

const email = new EmailChannel({
  name: 'email-alerts',
  from: '[email protected]',
  to: '[email protected]',
  smtp: {
    host: 'smtp.gmail.com',
    port: 587,
    auth: { user: '[email protected]', pass: process.env.SMTP_PASSWORD },
  },
});

SMSChannel (Configurable)

Two-way when webhookPath is set, outbound-only otherwise.

// Two-way
const sms = new SMSChannel({
  name: 'sms-alerts',
  accountSid: process.env.TWILIO_ACCOUNT_SID,
  authToken: process.env.TWILIO_AUTH_TOKEN,
  from: '+1234567890',
  webhookPath: '/sms/webhook',
  port: 3000,
});

// Outbound-only
const smsOutbound = new SMSChannel({
  name: 'sms-notifications',
  accountSid: process.env.TWILIO_ACCOUNT_SID,
  authToken: process.env.TWILIO_AUTH_TOKEN,
  from: '+1234567890',
  to: '+0987654321',
});

McpChannel (Two-way)

Exposes a Toolpack agent as a tool in an MCP server. The agent appears in tools/list as agent.<name> and is callable by any MCP client.

import { McpChannel } from '@toolpack-sdk/agents';
import { Toolpack } from 'toolpack-sdk';

const ch = new McpChannel({ name: 'mcp' });
const agent = new PrReviewerAgent({ channels: [ch] });
await agent.start();

const sdk = await Toolpack.init({ provider: 'anthropic', tools: true });
await sdk.startMcpServer({
  transport: 'stdio',   // or 'http' with port
  agents: [ch.asAgentDefinition(agent)],
});

ch.asAgentDefinition(agent) produces the entry that startMcpServer registers in tools/list. Each MCP tools/call for agent.<name> is routed through the channel to agent.invokeAgent() and the output is returned as the tool result.

Creating Custom Agents

Extend BaseAgent to create custom agents:

import { BaseAgent } from '@toolpack-sdk/agents';

class MyAgent extends BaseAgent {
  name = 'my-agent';
  description = 'My custom agent';
  mode = 'agent';

  async invokeAgent(input) {
    // Process the message
    const result = await this.run(input.message);
    
    // Send to a channel
    await this.sendTo('slack', result.output);
    
    return result;
  }
}

Human-in-the-Loop

Use ask() to pause execution and request human input (two-way channels only). ask() sends the question and returns immediately — the user's answer arrives on the next invocation, where you check getPendingAsk().

class ApprovalAgent extends BaseAgent {
  name = 'approval-agent';
  mode = 'agent';

  async invokeAgent(input) {
    // Turn 2: check if we are waiting for an answer
    const pending = this.getPendingAsk(input.conversationId);
    if (pending && input.message) {
      return this.handlePendingAsk(
        pending,
        input.message,
        async (answer) => {
          if (answer.toLowerCase() === 'yes') {
            await this.sendTo('slack', 'Draft approved!');
            return { output: 'Draft approved and sent.' };
          }
          return { output: 'Draft discarded.' };
        },
      );
    }

    // Turn 1: do some work, then ask for approval
    const draft = await this.run(`Draft a response to: ${input.message}`);
    return this.ask(`Here is my draft:\n\n${draft.output}\n\nApprove? (yes/no)`);
  }
}

Note: ask() throws if called from trigger-only channels (ScheduledChannel, EmailChannel). It requires a registry — use AgentRegistry, not standalone agent.start().

Conversation History

Store conversation history separately from domain knowledge:

import { InMemoryConversationStore } from '@toolpack-sdk/agents';

class SupportAgent extends BaseAgent {
  // In-memory store (development/single-process)
  conversationHistory = new InMemoryConversationStore();

  async invokeAgent(input) {
    // History is automatically loaded before AI call
    // and stored after response
    const result = await this.run(input.message);
    return result;
  }
}

Features:

  • Auto-assembles conversation history before each AI call (up to 3 000-token budget by default)
  • Auto-stores user and assistant messages via the capture interceptor
  • Auto-trims to maxMessagesPerConversation limit (default: 500)
  • Zero-config in-memory mode for development
  • conversation_search tool is automatically provided as a request-scoped tool whenever a conversationId is active

Memory model: Agent memory is per-conversation by default. The conversation_search tool is bound at invocation time to the current conversation — the LLM cannot override this scope, and turns from other conversations are structurally unreachable. Use knowledge_add to promote durable facts that should persist across conversations; knowledge is the only cross-conversation bridge.

Knowledge Integration

Integrate knowledge bases for RAG (domain knowledge, not conversation history). Knowledge is configured at the SDK level and automatically available to all agents:

import { Toolpack } from 'toolpack-sdk';
import { Knowledge, MemoryProvider } from '@toolpack-sdk/knowledge';

// Configure knowledge at SDK level
const knowledge = await Knowledge.create({
  provider: new MemoryProvider(),
});

const toolpack = await Toolpack.init({
  provider: 'openai',
  knowledge, // Available to all agents using this toolpack
});

class SmartAgent extends BaseAgent {
  async invokeAgent(input) {
    // Both `knowledge_search` and `knowledge_add` tools are
    // automatically available as request-scoped tools.
    // The AI can use them to retrieve or store information.
    const result = await this.run(input.message);
    return result;
  }
}

Available Tools:

  • knowledge_search — Search the knowledge base for relevant information
  • knowledge_add — Add new information to the knowledge base at runtime

The SDK automatically injects usage guidance into the system prompt when these tools are available.

Knowledge as the cross-conversation bridge:

knowledge_add is the only path by which information crosses conversation boundaries. Conversation history is scoped to the current conversation and inaccessible elsewhere; anything promoted via knowledge_add becomes available in all future conversations for that agent.

Promote when:

  • A task surfaces a fact useful beyond the current conversation
  • A user states a durable preference
  • A decision is made that future conversations should respect

Do not promote:

  • Routine task outputs (e.g., "answered a weather question")
  • Context that is specific to this conversation only
  • Confidential information whose visibility should remain inside the current conversation

Because every promotion is an explicit agent action visible in traces, the knowledge base stays auditable and intentional. If you need per-entry visibility controls (e.g., scoping a knowledge entry to a subset of channels), that is a future extension — for now, apply developer discipline: only promote what every future conversation is permitted to see.

Multi-Channel Routing

Send output to multiple channels:

class MultiChannelAgent extends BaseAgent {
  async invokeAgent(input) {
    const result = await this.run(input.message);
    
    await this.sendTo('slack', result.output);
    await this.sendTo('email-team', result.output);
    await this.sendTo('sms-alerts', 'Task done!');
    
    return result;
  }
}

Agent Events

Listen to agent lifecycle events:

const agent = new MyAgent(sdk);

agent.on('agent:start', (input) => {
  console.log('Agent started:', input.message);
});

agent.on('agent:complete', (result) => {
  console.log('Agent completed:', result.output);
});

agent.on('agent:error', (error) => {
  console.error('Agent error:', error);
});

Extending Built-in Agents

Customize built-in agents with your own prompts and logic:

import { ResearchAgent } from '@toolpack-sdk/agents';
import { AGENT_MODE } from 'toolpack-sdk';

class FintechResearchAgent extends ResearchAgent {
  mode = {
    ...AGENT_MODE,
    systemPrompt: 'You are a fintech research specialist. Always cite sources and flag regulatory implications.',
  };

  async onComplete(result) {
    // Notify team
    await this.sendTo('slack-research', result.output);
  }
}

// Knowledge is configured at SDK level, not on the agent.
// The AI can use `knowledge_add` to store information during execution.
const toolpack = await Toolpack.init({
  provider: 'openai',
  knowledge: await Knowledge.create({ provider: new MemoryProvider() }),
});

Peer Dependencies

The following are optional peer dependencies. Install only what you need:

# For DiscordChannel
npm install discord.js

# For EmailChannel  
npm install nodemailer

# For SMSChannel
npm install twilio

API Reference

BaseAgent

abstract class BaseAgent {
  abstract name: string;
  abstract description: string;
  abstract mode: ModeConfig | string;
  
  // Core method to implement
  abstract invokeAgent(input: AgentInput): Promise<AgentResult>;
  
  // Built-in methods
  protected run(message: string, options?: AgentRunOptions, context?: { conversationId?: string }): Promise<AgentResult>;
  protected sendTo(channelName: string, message: string): Promise<void>;
  protected ask(question: string, options?: { context?: Record<string, unknown>; maxRetries?: number; expiresIn?: number }): Promise<AgentResult>;
  protected getPendingAsk(conversationId?: string): PendingAsk | null;
}

AgentRegistry

class AgentRegistry {
  constructor(agents: BaseAgent[]);
  start(): Promise<void>;
  stop(): Promise<void>;
  sendTo(channelName: string, output: AgentOutput): Promise<void>;
  getAgent(name: string): AgentInstance | undefined;
  getChannel(name: string): ChannelInterface | undefined;
  invoke(agentName: string, input: AgentInput): Promise<AgentResult>;
}

Channels

All channels extend BaseChannel:

abstract class BaseChannel {
  abstract readonly isTriggerChannel: boolean;
  name?: string;
  
  abstract listen(): void;
  abstract send(output: AgentOutput): Promise<void>;
  abstract normalize(incoming: unknown): AgentInput;
  onMessage(handler: (input: AgentInput) => Promise<void>): void;
}

Agent-to-Agent Messaging

Agents can delegate tasks to other agents without tight coupling.

Local Delegation (Same Process)

import { AgentRegistry, BaseAgent } from '@toolpack-sdk/agents';
import type { AgentInput, AgentResult } from '@toolpack-sdk/agents';

class EmailAgent extends BaseAgent {
  name = 'email-agent';
  description = 'Sends email reports';
  mode = 'chat';
  channels = [slack]; // channels are class properties, not constructor args

  async invokeAgent(input: AgentInput): Promise<AgentResult> {
    // Delegate to DataAgent and wait for result
    const report = await this.delegateAndWait('data-agent', {
      message: 'Generate weekly leads report',
      intent: 'generate_report',
    });
    
    return {
      output: `Email sent with report: ${report.output}`,
    };
  }
}

const emailAgent = new EmailAgent({ apiKey: process.env.ANTHROPIC_API_KEY! });
const dataAgent = new DataAgent({ apiKey: process.env.ANTHROPIC_API_KEY! });
const registry = new AgentRegistry([emailAgent, dataAgent]);
await registry.start();

Cross-Process Delegation (JSON-RPC)

Server (Host Agents):

import { AgentJsonRpcServer } from '@toolpack-sdk/agents';

const server = new AgentJsonRpcServer({ port: 3000 });
server.registerAgent('data-agent', new DataAgent({ apiKey: process.env.ANTHROPIC_API_KEY! }));
server.registerAgent('research-agent', new ResearchAgent({ apiKey: process.env.ANTHROPIC_API_KEY! }));
server.listen();

Client (Call Remote Agents):

import { AgentRegistry, JsonRpcTransport, BaseAgent } from '@toolpack-sdk/agents';
import type { AgentInput, AgentResult } from '@toolpack-sdk/agents';

const emailAgent = new EmailAgent({ apiKey: process.env.ANTHROPIC_API_KEY! });
const registry = new AgentRegistry([emailAgent], {
  transport: new JsonRpcTransport({
    agents: {
      'data-agent': 'http://localhost:3000',
      'research-agent': 'http://remote-server:3000',
    }
  })
});

// Inside EmailAgent
class EmailAgent extends BaseAgent {
  async invokeAgent(input: AgentInput): Promise<AgentResult> {
    // Can now delegate to remote agents
    const report = await this.delegateAndWait('data-agent', {
      message: 'Generate report'
    });
    return { output: `Email sent with: ${report.output}` };
  }
}

Delegation Methods

  • delegate(agentName, input) - Fire-and-forget, returns immediately
  • delegateAndWait(agentName, input) - Waits for result, returns AgentResult

Registry

Discover and publish community-built agents.

Finding Agents

import { searchRegistry } from '@toolpack-sdk/agents/registry';

// Search all agents
const results = await searchRegistry();

// Search by keyword
const results = await searchRegistry({ keyword: 'fintech' });

// Filter by category
const results = await searchRegistry({ category: 'research' });

// Display results
for (const agent of results.agents) {
  console.log(`${agent.name}: ${agent.toolpack?.description || agent.description}`);
  console.log(`  Install: npm install ${agent.name}`);
}

Publishing an Agent

Add the toolpack metadata to your package.json:

{
  "name": "toolpack-agent-fintech-research",
  "version": "1.0.0",
  "keywords": ["toolpack-agent"],
  "toolpack": {
    "agent": true,
    "category": "research",
    "description": "Research agent focused on fintech news and regulatory updates",
    "tags": ["fintech", "news", "research"]
  }
}

Requirements:

  • Must include "toolpack-agent" in keywords
  • Must have "toolpack": { "agent": true } in package.json
  • Agent class must extend BaseAgent

Error Handling

Error Types

| Error | Cause | Resolution | |-------|-------|------------| | AgentError | Generic agent failure | Check error message for details | | AgentError (delegate) | Agent not registered | Ensure agent is registered with AgentRegistry | | AgentError (transport) | Transport misconfiguration | Verify transport config and agent URLs | | RegistryError | NPM registry failure | Check network connection and registry URL |

Handling Errors

import { AgentError } from '@toolpack-sdk/agents';

try {
  const result = await agent.invokeAgent({ message: 'Hello' });
} catch (error) {
  if (error instanceof AgentError) {
    // Agent-specific error
    console.error('Agent failed:', error.message);
  } else {
    // Unknown error
    console.error('Unexpected error:', error);
  }
}

Common Issues

Agent not found during delegation

Agent "data-agent" not found in registry. Available agents: email-agent, browser-agent

→ Ensure the target agent is registered in AgentRegistry.

Transport configuration error

No transport configured for delegation

→ Use AgentRegistry with LocalTransport (default) or configure JsonRpcTransport for cross-process communication.

JSON-RPC connection failure

Failed to invoke agent "data-agent" at http://localhost:3000: fetch failed

→ Verify the JSON-RPC server is running and the URL/port is correct.

Interceptors

Interceptors are composable middleware that run before invokeAgent. They can filter, enrich, classify, or short-circuit incoming messages. All built-ins are opt-in — none run unless you explicitly list them.

Import from the dedicated subpath:

import {
  createNoiseFilterInterceptor,
  createRateLimitInterceptor,
  createSelfFilterInterceptor,
  // ...
} from '@toolpack-sdk/agents/interceptors';

Writing a Custom Interceptor

import type { Interceptor } from '@toolpack-sdk/agents/interceptors';

const myInterceptor: Interceptor = async (input, ctx, next) => {
  if (shouldIgnore(input)) {
    return ctx.skip(); // End the chain silently — no reply sent
  }
  const result = await next(); // Continue to next interceptor or agent
  return result;
};

class MyAgent extends BaseAgent {
  interceptors = [myInterceptor];
}

Registering Interceptors

import {
  createNoiseFilterInterceptor,
  createRateLimitInterceptor,
} from '@toolpack-sdk/agents/interceptors';

class MyAgent extends BaseAgent {
  name = 'my-agent';
  description = 'My agent';
  mode = 'chat';

  interceptors = [
    createNoiseFilterInterceptor({ denySubtypes: ['message_changed', 'message_deleted'] }),
    createRateLimitInterceptor({
      getKey: (input) => input.participant?.id ?? 'anon',
      tokensPerInterval: 5,
      interval: 60000, // 5 messages per minute per user
    }),
  ];

  async invokeAgent(input) {
    return this.run(input.message);
  }
}

Built-in Interceptors

| Interceptor | Purpose | |---|---| | createNoiseFilterInterceptor | Drop messages by subtype (edits, deletes, bot messages) | | createEventDedupInterceptor | Drop duplicate events (Slack retries, webhook redeliveries) | | createSelfFilterInterceptor | Drop the agent's own messages (infinite loop guard) | | createRateLimitInterceptor | Token-bucket rate limiting per user or conversation | | createAddressCheckInterceptor | Rule-based address detection (@mention, vocative, direct message) | | createIntentClassifierInterceptor | LLM-based intent classification for ambiguous address checks | | createParticipantResolverInterceptor | Resolve participant identity from platform user ID | | createCaptureInterceptor | Persist inbound and outbound messages to conversation history (auto-registered) | | createDepthGuardInterceptor | Reject delegation chains that exceed a configured depth | | createTracerInterceptor | Structured logging of each chain hop for debugging | | createOTelTracerInterceptor | OpenTelemetry span per invocation — compatible with any OTel-compliant backend |

Capabilities

Capability agents are headless agents with no channels. They are invoked by interceptors or other agents for specific cross-cutting concerns.

Import from the dedicated subpath:

import { IntentClassifierAgent, SummarizerAgent } from '@toolpack-sdk/agents/capabilities';

IntentClassifierAgent

Classifies whether a message is directly addressing the target agent. Used by createIntentClassifierInterceptor to resolve ambiguous cases that rules alone cannot determine.

import { IntentClassifierAgent } from '@toolpack-sdk/agents/capabilities';
import type { IntentClassifierInput } from '@toolpack-sdk/agents/capabilities';

const classifier = new IntentClassifierAgent({ apiKey: process.env.ANTHROPIC_API_KEY });
const result = await classifier.invokeAgent({
  message: 'classify',
  data: {
    message: 'Hey @assistant can you help?',
    agentName: 'assistant',
    agentId: 'U123',
    senderName: 'alice',
    channelName: 'general',
  } as IntentClassifierInput,
});
// result.output === 'direct' | 'indirect' | 'passive' | 'ignore'

SummarizerAgent

Compresses older conversation history turns into a compact summary. Used by the prompt assembler when conversation history exceeds the token budget.

import { SummarizerAgent } from '@toolpack-sdk/agents/capabilities';
import type { SummarizerInput, SummarizerOutput } from '@toolpack-sdk/agents/capabilities';

const summarizer = new SummarizerAgent({ apiKey: process.env.ANTHROPIC_API_KEY });
const result = await summarizer.invokeAgent({
  message: 'summarize',
  data: {
    turns: olderTurns,
    agentName: 'support-agent',
    agentId: 'U123',
    maxTokens: 500,
    extractDecisions: true,
  } as SummarizerInput,
});
const summary = JSON.parse(result.output) as SummarizerOutput;

Evals — LLM Quality Evaluation

Unit tests verify wiring; evals verify agent quality. Use the eval primitives to build regression suites and track answer quality over time.

import {
  EvalDataset,
  EvalRunner,
  ContainsScorer,
  LLMJudgeScorer,
  compareEvalRuns,
  formatEvalReport,
} from '@toolpack-sdk/agents';

const dataset = new EvalDataset([
  { id: 'q1', input: 'What is 2+2?', expectedOutput: '4' },
  { id: 'q2', input: 'Capital of France?', expectedOutput: 'Paris' },
]);

const runner = new EvalRunner({
  agent: myAgent,
  dataset,
  scorers: [new ContainsScorer()],
});

const run = await runner.run();
console.log(`Average score: ${(run.averageScore * 100).toFixed(1)}%`);

Four built-in scorers:

| Scorer | When to use | |---|---| | ExactMatchScorer | Deterministic outputs — exact string match | | ContainsScorer | Output must contain the expected string | | LLMJudgeScorer | Open-ended answers — ask an LLM to grade on 0–1 | | CustomScorer | Any custom scoring logic |

Regression detection:

const report = compareEvalRuns(baselineRun, currentRun);
console.log(formatEvalReport(report));
expect(report.regressions).toHaveLength(0); // CI gate

Testing

npm test

License

Apache 2.0 © Toolpack SDK