Skip to main content

@agentlib/utils

Common utilities for AgentLIB — shared helpers and event system used across packages.

Installation

npm install @agentlib/utils

Overview

The @agentlib/utils package provides low-level utilities used throughout AgentLIB:
  • EventEmitter: Type-safe, async event emitter
  • Shared helper functions
  • Common types
Most users won’t need to install this directly — it’s included as a dependency of @agentlib/core.

EventEmitter

A fully typed, async-first event emitter used by agents, orchestrators, and other components.

Quick Start

import { EventEmitter } from '@agentlib/utils'

type MyEvents = {
  'user:login': { userId: string; timestamp: Date }
  'user:logout': { userId: string }
  'data:update': { key: string; value: any }
}

const emitter = new EventEmitter<MyEvents>()

// Subscribe to events
emitter.on('user:login', ({ userId, timestamp }) => {
  console.log(`User ${userId} logged in at ${timestamp}`)
})

// Emit events
await emitter.emit('user:login', {
  userId: 'user-123',
  timestamp: new Date()
})

Type Safety

type Events = {
  'data': { value: number }
  'error': { message: string }
}

const emitter = new EventEmitter<Events>()

// ✅ Type-safe: correct event and payload
emitter.on('data', ({ value }) => {
  console.log(value.toFixed(2))
})

// ❌ Type error: wrong event name
emitter.on('invalid', (payload) => {})

// ❌ Type error: wrong payload structure
emitter.emit('data', { wrongKey: 123 })

API Reference

on(event, handler)

Subscribe to an event:
emitter.on('data', async ({ value }) => {
  await processValue(value)
})
Returns: this (chainable)

once(event, handler)

Subscribe to an event once (auto-unsubscribes after first call):
emitter.once('ready', () => {
  console.log('Ready! (only logged once)')
})

await emitter.emit('ready')
await emitter.emit('ready') // Handler not called
Returns: this (chainable)

off(event, handler)

Unsubscribe from an event:
const handler = ({ value }) => console.log(value)

emitter.on('data', handler)
emitter.off('data', handler)
Returns: this (chainable)

emit(event, payload)

Emit an event asynchronously (waits for all handlers):
await emitter.emit('data', { value: 42 })
console.log('All handlers completed')
Returns: Promise<void>

emitSync(event, payload)

Emit an event synchronously (fire-and-forget):
emitter.emitSync('data', { value: 42 })
console.log('Handlers running in background')
Returns: void

Usage Examples

Basic Events

import { EventEmitter } from '@agentlib/utils'

type Events = {
  'start': { timestamp: Date }
  'progress': { percent: number }
  'complete': { result: string }
}

const emitter = new EventEmitter<Events>()

emitter.on('start', ({ timestamp }) => {
  console.log('Started at', timestamp)
})

emitter.on('progress', ({ percent }) => {
  console.log(`Progress: ${percent}%`)
})

emitter.on('complete', ({ result }) => {
  console.log('Done:', result)
})

await emitter.emit('start', { timestamp: new Date() })
await emitter.emit('progress', { percent: 50 })
await emitter.emit('progress', { percent: 100 })
await emitter.emit('complete', { result: 'Success!' })

Async Handlers

emitter.on('data', async ({ value }) => {
  await fetch('/api/data', {
    method: 'POST',
    body: JSON.stringify({ value })
  })
})

// Wait for all async handlers to complete
await emitter.emit('data', { value: 42 })

Error Handling

emitter.on('process', async ({ data }) => {
  try {
    await riskyOperation(data)
  } catch (error) {
    await emitter.emit('error', { message: error.message })
  }
})

emitter.on('error', ({ message }) => {
  console.error('Error:', message)
})

Method Chaining

emitter
  .on('start', () => console.log('Starting...'))
  .on('progress', ({ percent }) => console.log(`${percent}%`))
  .on('complete', () => console.log('Done!'))

Unsubscribing

const handler = ({ value }) => console.log(value)

emitter.on('data', handler)

// Later...
emitter.off('data', handler)

One-Time Events

emitter.once('initialized', () => {
  console.log('App initialized')
})

await emitter.emit('initialized') // Logged
await emitter.emit('initialized') // Not logged

Integration with AgentLIB

Agents extend EventEmitter:
import { createAgent } from '@agentlib/core'

const agent = createAgent({ name: 'assistant' })

// Agent is an EventEmitter
agent.on('run:start', ({ input }) => {
  console.log('Running:', input)
})

agent.on('run:end', ({ output }) => {
  console.log('Result:', output)
})
Orchestrators extend EventEmitter:
import { Orchestrator } from '@agentlib/orchestrator'

const orchestrator = new Orchestrator(planner, config)

orchestrator.on('agent:invoke', ({ agent, prompt }) => {
  console.log(`Starting ${agent}`)
})

orchestrator.on('agent:completed', ({ agent, output }) => {
  console.log(`${agent} done`)
})

Types

EventMap

Defines the shape of your event system:
type EventMap = Record<string, any>

type MyEvents = {
  'event1': { data: string }
  'event2': { count: number }
}

EventHandler

Function signature for event handlers:
type EventHandler<T = any> = (payload: T) => void | Promise<void>

Best Practices

  1. Always define event types:
    type Events = {
      'start': void
      'data': { value: number }
    }
    const emitter = new EventEmitter<Events>()
    
  2. Use await with emit for critical events:
    await emitter.emit('critical', { data })
    // All handlers have completed
    
  3. Use emitSync for fire-and-forget events:
    emitter.emitSync('log', { message: 'info' })
    
  4. Handle errors in async handlers:
    emitter.on('data', async (payload) => {
      try {
        await process(payload)
      } catch (err) {
        console.error('Handler error:', err)
      }
    })
    
  5. Clean up listeners:
    const handler = () => {}
    emitter.on('data', handler)
    
    // Later...
    emitter.off('data', handler)
    

Requirements

  • Node.js: >= 18.0.0
  • No dependencies

Exports

Classes

  • EventEmitter<TEvents> - Generic event emitter

Types

  • EventMap - Event definition map type
  • EventHandler<T> - Event handler function type

Build docs developers (and LLMs) love