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

simullm

v0.3.0

Published

Event-driven Agent-Based Modeling framework for TypeScript

Readme

simullm

Event-driven Agent-Based Modeling framework for TypeScript.

Features

  • Event-driven architecture: Agents respond to actions/messages in a reactive system
  • Type-safe: Full TypeScript support with generic types for state and actions
  • Flexible state management: Both global state and agent internal state
  • Exit conditions: Required termination logic prevents infinite loops
  • Async support: Handles both synchronous and asynchronous agent actions
  • Action cascading: Agents can dispatch new actions during processing
  • Memory management: Agents can maintain internal state across turns

Installation

npm install simullm
# or
bun add simullm

Quick Start

import { createSimulation, createAgent } from 'simullm';

// Define your state and action types
interface CounterState {
  value: number;
}

type CounterAction = 
  | { type: "INCREMENT"; amount: number }
  | { type: "RESET" };

// Create agents
const counterAgent = createAgent<CounterState, CounterAction>(
  "counter",
  (action, context) => {
    if (action.type === "INCREMENT") {
      context.updateGlobalState(state => ({
        value: state.value + action.amount
      }));
    } else if (action.type === "RESET") {
      context.updateGlobalState(state => ({ value: 0 }));
    }
  }
);

// Create simulation with required exit condition
const simulation = createSimulation<CounterState, CounterAction>({
  initialGlobalState: { value: 0 },
  agents: [counterAgent],
  shouldExit: ({ actionCount, globalState }) => 
    actionCount >= 10 || globalState.value >= 100
});

// Run simulation
await simulation.dispatch({ type: "INCREMENT", amount: 5 });
await simulation.dispatch({ type: "INCREMENT", amount: 15 });

console.log(simulation.getGlobalState()); // { value: 20 }
console.log(simulation.getActionCount()); // 2

Core Concepts

Agents

Agents are reactive entities that respond to actions. They can:

  • Update global state
  • Update their own internal state
  • Dispatch new actions
  • Maintain memory across turns
  • Coordinate with other agents via context.allAgents

Actions

Actions are messages passed through the system. All agents receive every action and can choose to respond.

Exit Conditions (Required)

Every simulation must define when to stop via the shouldExit function. You have access to:

  • globalState: Current global state
  • agentStates: All agent internal states
  • lastAction: The action that was just processed
  • actionCount: Total number of processed actions

Examples

Basic Counter

const simulation = createSimulation({
  initialGlobalState: { value: 0 },
  agents: [counterAgent],
  shouldExit: ({ actionCount }) => actionCount >= 5
});

State-based Exit

const simulation = createSimulation({
  initialGlobalState: { temperature: 20 },
  agents: [heatingAgent, coolingAgent],
  shouldExit: ({ globalState }) => 
    globalState.temperature >= 100 || globalState.temperature <= 0
});

Agent Memory Exit

const simulation = createSimulation({
  initialGlobalState: { score: 0 },
  agents: [learningAgent],
  shouldExit: ({ agentStates }) => 
    agentStates["learner"]?.experience >= 1000
});

API Reference

createSimulation<TGlobalState, TAction>(config)

Creates a new simulation instance.

Config:

  • initialGlobalState: TGlobalState - Starting global state
  • agents: Agent[] - Array of agents to participate
  • shouldExit: (context: ExitContext) => boolean - Required exit condition

Returns: EventSimulation<TGlobalState, TAction>

createAgent<TGlobalState, TAction, TInternalState>(id, onAction, initialInternalState?)

Creates a new agent.

Parameters:

  • id: string - Unique agent identifier
  • onAction: (action, context) => void | Promise<void> - Action handler
  • initialInternalState?: TInternalState - Optional internal state

EventSimulation Methods

  • dispatch(action) - Dispatch an action to all agents
  • exit() - Returns a promise that resolves when simulation exits
  • getGlobalState() - Get current global state
  • getAgentInternalState(agentId) - Get agent's internal state
  • getAllAgentStates() - Get all agent internal states
  • getActionCount() - Get total processed actions
  • hasSimulationExited() - Check if simulation has terminated

Advanced Usage

Agent Context Properties

Each agent receives a context object with:

  • globalState - Current global state (read-only)
  • internalState - Agent's private state (read-only)
  • allAgents - Array of all agents with their IDs and internal states
  • dispatch(action) - Dispatch new actions
  • updateGlobalState(updater) - Modify global state
  • updateInternalState(updater) - Modify agent's internal state

Agent Coordination

Use context.allAgents to coordinate between agents:

const coordinator = createAgent("coordinator", (action, context) => {
  // Find available workers
  const availableWorkers = context.allAgents
    .filter(agent => agent.id !== "coordinator" && !agent.internalState?.busy)
    .map(agent => agent.id);
  
  // Assign tasks to available workers
  availableWorkers.forEach(workerId => {
    context.dispatch({ type: "ASSIGN_TASK", agentId: workerId });
  });
});

Waiting for Simulation Completion

Use the exit() method to wait for simulations to complete:

// Start simulation
simulation.dispatch({ type: "START" });

// Wait for completion
await simulation.exit();

console.log("Simulation completed!");
console.log("Final state:", simulation.getGlobalState());

Examples

See the /experiments directory for complete examples:

  • Counter simulation with turn-based agents
  • Predator-prey ecosystem simulation
  • Market trading simulation with LLM agents

Migration from v0.1.x

Breaking Change: The shouldExit property is now required in SimulationConfig.

Before (v0.1.x):

const simulation = createSimulation({
  initialGlobalState: { value: 0 },
  agents: [myAgent]
  // Could run infinitely
});

After (v0.2.x):

const simulation = createSimulation({
  initialGlobalState: { value: 0 },
  agents: [myAgent],
  shouldExit: ({ actionCount }) => actionCount >= 10 // Required
});

Development

Release Process

Use the automated release script:

bun run release

This handles:

  • Version bumping (patch/minor/major)
  • Changelog updates
  • Git commit and tagging
  • Pushing to remote
  • npm publishing

See scripts/README.md for details.

License

MIT