JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 14
  • Score
    100M100P100Q47740F
  • License MIT

A versatile JavaScript library for designing and managing intelligent agents capable of interacting with AI-driven chat models, leveraging OpenAI's GPT for dynamic conversations and task automation.

Package Exports

  • agents-js
  • agents-js/tools

Readme

agents-js

A powerful and flexible JavaScript library for building intelligent AI agents with OpenAI's GPT models. Features include automatic multi-agent orchestration, pre-built tools, lifecycle callbacks, session management, token tracking, and human-in-the-loop confirmations.

✨ Features

  • 🤖 Automatic Multi-Agent Orchestration - Seamless agent transfers with built-in transferToAgent
  • 🛠️ Pre-built Tools - Web search, scraping, calculator, datetime, and more
  • 🔄 Lifecycle Callbacks - Hook into agent execution flow
  • 💾 Session Management - Persist conversation state
  • 💰 Token Tracking - Monitor usage and costs
  • Human-in-the-Loop - Confirm dangerous operations
  • 📝 TypeScript Support - Full type definitions included
  • 🌊 Streaming - Real-time response streaming
  • 🔑 Centralized API Key Management - Automatic fallback between services

Installation

npm install agents-js

Quick Start

require("dotenv").config();
const OpenAI = require("openai");
const { Agent, AgentController } = require("agents-js");
const { serperSearch } = require("agents-js/tools");

const client = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
  baseURL: process.env.OPENAI_BASE_URL, // Optional: for local LLM endpoints
});

const controller = new AgentController(client, {
  apiKeys: {
    serper: process.env.SERPER_API_KEY,
    firecrawl: process.env.FIRECRAWL_API_KEY,
  },
});

const agent = new Agent({
  name: "SearchAssistant",
  model: "gpt-4o-mini",
  instructions: "You are a helpful assistant with web search capabilities.",
  functions: [serperSearch],
});

const messages = [{ role: "user", content: "What's new in AI?" }];
const response = await controller.run(agent, messages);

console.log(response.messages[response.messages.length - 1].content);
console.log(`Cost: $${response.cost.toFixed(4)}`);

Pre-built Tools

Web Tools

const {
  serperSearch,
  firecrawlScrape,
  firecrawlCrawl,
} = require("agents-js/tools");

// serperSearch automatically falls back to Firecrawl if Serper key is missing
serperSearch({ query: "AI agents 2025" });
firecrawlSearch({ query: "latest news" });
firecrawlScrape({ url: "https://example.com" });

Math Tools

const {
  calculate,
  add,
  subtract,
  multiply,
  divide,
} = require("agents-js/tools");

calculate({ expression: "25 * 48 + 100" });
add({ a: 5, b: 10 });

DateTime Tools

const { getCurrentTime, getTimestamp, formatDate } = require("agents-js/tools");

getCurrentTime({ timezone: "America/New_York" });
getTimestamp();
formatDate({ timestamp: 1234567890000, format: "iso" });

Web Tools

  • serperSearch - Web search via Serper.dev (with Firecrawl fallback)
  • firecrawlSearch - Web search via Firecrawl.dev search API
  • firecrawlScrape - Web scraping via Firecrawl.dev
  • firecrawlCrawl - Website crawling

Advanced Features

API Key Management

Configure API keys for external tools in one place:

const controller = new AgentController(client, {
  apiKeys: {
    serper: "your-serper-key",
    firecrawl: "your-firecrawl-key",
  },
});

controller.setApiKey("serper", "new-key");
const key = controller.getApiKey("serper");

API Key Management:

  1. AgentController configured keys: controller.setApiKey("serper", "key")
  2. Environment variables: SERPER_API_KEY, FIRECRAWL_API_KEY

Automatic Fallback: serperSearch automatically falls back to Firecrawl if the Serper API key is missing or if the Serper API call fails. This ensures reliable web search functionality even if one service is unavailable.

Tool Usage: Tools automatically use configured API keys - no need to pass apiKey parameters.

Lifecycle Callbacks

Hook into the agent execution flow:

const agent = new Agent({
  name: "Assistant",
  instructions: "You are helpful.",
  functions: [myTool],

  beforeModel: async ({ messages, contextVariables }) => {
    console.log("About to call model");
  },

  afterModel: async ({ response }) => {
    console.log("Model responded");
  },

  beforeTool: async (toolName, args, context) => {
    console.log(`Executing ${toolName}`);
    return true;
  },

  afterTool: async (toolName, args, result, context) => {
    console.log(`${toolName} completed`);
  },
});

Session Management

Persist conversation state across turns:

const { Session } = require("agents-js");

const session = new Session();

await session.set("user_name", "Alice");
await session.set("conversation_history", messages);

const name = await session.get("user_name");
const history = await session.get("conversation_history");

Tool Confirmation (Human-in-the-Loop)

Require user confirmation for dangerous operations:

const { ToolConfirmation } = require("agents-js");

function deleteFile({ filename }) {
  return new Result({ value: `Deleted ${filename}` });
}

const confirmationHandler = async (toolName, args, message) => {
  console.log(message);
  return true;
};

const controller = new AgentController(client, { confirmationHandler });

const agent = new Agent({
  functions: [
    new ToolConfirmation({
      tool: deleteFile,
      message: "⚠️ Confirm file deletion?",
    }),
  ],
});

Token Usage Tracking

Monitor costs and token usage:

const response = await controller.run(agent, messages);

console.log(`Input tokens: ${response.tokenUsage.totalInputTokens}`);
console.log(`Output tokens: ${response.tokenUsage.totalOutputTokens}`);
console.log(`Total cost: $${response.cost.toFixed(4)}`);

controller.resetTokenUsage();

Automatic Multi-Agent Systems

Create specialized agents that automatically transfer to each other:

const searchAgent = new Agent({
  name: "SearchSpecialist",
  instructions: "You specialize in web search.",
  functions: [serperSearch],
  description: "Expert at finding online information",
});

const mathAgent = new Agent({
  name: "MathSpecialist",
  instructions: "You specialize in calculations.",
  functions: [calculate],
  description: "Expert at mathematical operations",
});

const coordinator = new Agent({
  name: "Coordinator",
  instructions: `Route requests to the appropriate specialist:
  - For search questions → transfer to "SearchSpecialist"
  - For math questions → transfer to "MathSpecialist"`,
  functions: [], // transferToAgent is automatically added
  subAgents: [searchAgent, mathAgent],
});

const response = await controller.run(coordinator, messages);
console.log("Active agent:", response.agent.name);

Key Benefits:

  • No need to manually create transferToAgent functions
  • Automatic agent discovery and registration
  • Built-in error handling for invalid agent names
  • Seamless context transfer between agents

Streaming Responses

Stream responses in real-time:

const stream = await controller.run(agent, messages, {}, null, true);

for await (const chunk of stream) {
  if (chunk.delim === "start") {
    console.log("Starting stream...");
  } else if (chunk.delim === "end") {
    console.log("\nStream ended.");
  } else if (chunk.content) {
    process.stdout.write(chunk.content);
  } else if (chunk.response) {
    console.log("\nFinal response:", chunk.response);
  }
}

Context Variables

Pass dynamic context to your agents:

const contextVariables = {
  userId: "123",
  userName: "Alice",
  preferences: { theme: "dark" },
};

const response = await controller.run(agent, messages, contextVariables);

Functions can access context variables:

function personalizedGreeting({ context_variables }) {
  /**
   * @description Greet user with their name
   */
  const name = context_variables.userName || "there";
  return new Result({ value: `Hello, ${name}!` });
}

Custom Tools

Create custom tools with JSDoc annotations:

const { Result } = require("agents-js");

function fetchWeather({ city }) {
  /**
   * @param {string} city - City name to get weather for
   * @description Get current weather for a city
   */

  const temp = Math.floor(Math.random() * 30) + 10;
  return new Result({
    value: `The temperature in ${city} is ${temp}°C`,
  });
}

const agent = new Agent({
  functions: [fetchWeather],
});

API Reference

Agent

new Agent({
  name: string,
  model: string,
  instructions: string | function,
  functions: Array<Function | ToolConfirmation>,
  toolChoice: string | null,
  parallelToolCalls: boolean,
  beforeModel: function,
  afterModel: function,
  beforeTool: function,
  afterTool: function,
  subAgents: Array<Agent>,
  description: string,
})

AgentController

new AgentController(client, {
  confirmationHandler: async (toolName, args, message) => boolean,
  apiKeys: {
    serper: "key",
    firecrawl: "key",
  },
});

controller.run(
  agent,
  messages,
  contextVariables,
  modelOverride,
  stream,
  debug,
  maxTurns,
  executeTools
);
controller.setApiKey(service, key);
controller.getApiKey(service);
controller.getTokenUsage();
controller.resetTokenUsage();

Result

new Result({
  value: string,
  agent: Agent | null,
  contextVariables: object,
});

Response

{
  messages: Array<Message>,
  agent: Agent,
  contextVariables: object,
  tokenUsage: object,
  cost: number,
}

Usage Examples

const agent = new Agent({
  name: "ResearchAssistant",
  model: "gpt-4o-mini",
  instructions: "You are a research assistant with web search capabilities.",
  functions: [serperSearch],
});

const response = await controller.run(agent, [
  { role: "user", content: "What are the latest developments in AI?" }
]);

Multi-Agent System with Automatic Transfers

const weatherAgent = new Agent({
  name: "WeatherSpecialist",
  instructions: "You specialize in weather information.",
  functions: [serperSearch],
});

const timeAgent = new Agent({
  name: "TimeSpecialist", 
  instructions: "You specialize in time and timezone information.",
  functions: [getCurrentTime],
});

const coordinator = new Agent({
  name: "Assistant",
  instructions: "Route weather questions to WeatherSpecialist, time questions to TimeSpecialist.",
  functions: [], // transferToAgent automatically added
  subAgents: [weatherAgent, timeAgent],
});

// Agents automatically transfer based on user queries
const response = await controller.run(coordinator, [
  { role: "user", content: "What's the weather in New York?" }
]);

Session Management

const session = new Session();
await session.set("user_preferences", { theme: "dark", language: "en" });

const response = await controller.run(agent, messages, {
  session: session,
  user_preferences: await session.get("user_preferences")
});

Environment Variables

Create a .env file in your project root:

# OpenAI Configuration (or compatible endpoint)
OPENAI_API_KEY=your-api-key
OPENAI_BASE_URL=http://localhost:3002/v1  # Optional: for local LLM endpoints

# External Tool API Keys (optional)
SERPER_API_KEY=your-serper-key
FIRECRAWL_API_KEY=your-firecrawl-key

Using Local LLM Endpoints:

agents-js works with any OpenAI-compatible API endpoint. For example, to use a local LLM:

# Example: Local LLM at localhost:3002
OPENAI_API_KEY=your-local-key
OPENAI_BASE_URL=http://localhost:3002/v1

# Example: Ollama
OPENAI_API_KEY=ollama
OPENAI_BASE_URL=http://localhost:11434/v1

# Example: LM Studio
OPENAI_API_KEY=lm-studio
OPENAI_BASE_URL=http://localhost:1234/v1

TypeScript

Full TypeScript definitions are included:

import { Agent, AgentController, Result, Response, Session } from "agents-js";
import { serperSearch, calculate } from "agents-js/tools";

const agent: Agent = new Agent({
  name: "Assistant",
  model: "gpt-4o-mini",
  instructions: "You are helpful.",
});

Contributing

Contributions are welcome! Please feel free to submit issues and pull requests.

License

MIT License - see LICENSE file for details.

Author

Vikrant Guleria

Examples

See the examples directory for practical demonstrations:


Built with ❤️ for the AI agent community