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

@furioussoul/openagent

v0.1.10

Published

A lightweight, extensible AI Agent framework with tool calling, MCP integration, and streaming support

Readme

@furioussoul/openagent

A lightweight, extensible AI Agent framework with tool calling, streaming, multi-provider support, and a powerful event system.

npm version License: MIT

✨ Features

  • 🚀 Simple API - One-line initialization, easy chat(), stream(), and run() methods
  • 🛠️ Built-in Tools - File operations, bash execution, web fetching, code search out of the box
  • 🔌 Multi-Provider - Works with Anthropic, OpenAI, Google, Zhipu, Kimi, and OpenAI-compatible APIs
  • 📡 Event System - Full lifecycle events for monitoring and debugging
  • ⏹️ Abort Control - Cancel long-running operations at any time
  • 🔧 Extensible - Custom tools, custom agents, pluggable storage, plugin system
  • 📊 Streaming - Real-time streaming of responses and tool execution
  • 🔄 Agent Loop - Automatic multi-step tool execution with progress tracking

📦 Installation

npm install @furioussoul/openagent
# or
yarn add @furioussoul/openagent
# or
pnpm add @furioussoul/openagent

🚀 Quick Start

30 Seconds to Your First Agent

import { anthropic } from '@furioussoul/openagent'

const agent = anthropic('your-api-key')
const result = await agent.chat('List files in current directory')
console.log(result.text)

That's it! The agent can read/write files, execute commands, search code, and more.

Streaming Responses

for await (const event of agent.stream('Create a hello.txt file')) {
  if (event.type === 'text') process.stdout.write(event.text)
  if (event.type === 'tool-start') console.log(`\nUsing: ${event.name}`)
  if (event.type === 'done') console.log(`\nCost: $${event.result.cost}`)
}

With Event Monitoring

const agent = anthropic('your-api-key')

// Listen to events
agent.on('session:start', ({ sessionId }) => console.log('Session:', sessionId))
agent.on('tool:call', ({ name }) => console.log('Calling:', name))
agent.on('stream:text', ({ text }) => process.stdout.write(text))

// Run with progress tracking
const result = await agent.run('Refactor this code', {
  onProgress: ({ step, total, message }) => {
    console.log(`[${step}/${total}] ${message}`)
  },
})

// Abort if needed
setTimeout(() => agent.abort(), 30000)

📖 API Reference

Factory Functions

Create agents for different providers with a single function call:

import { anthropic, openai, google, zhipu, kimi } from '@furioussoul/openagent'

// Anthropic Claude
const agent1 = anthropic('sk-ant-xxx')
const agent2 = anthropic('sk-ant-xxx', 'claude-sonnet-4-20250514')

// OpenAI
const agent3 = openai('sk-xxx')
const agent4 = openai('sk-xxx', 'gpt-4o')

// Google Gemini
const agent5 = google('your-key', 'gemini-1.5-pro')

// Zhipu GLM
const agent6 = zhipu('your-key', 'glm-4.7')

// Kimi
const agent7 = kimi('your-key', 'kimi-k2.5-free')

OpenAgent Class

For advanced configuration:

import { OpenAgent } from '@furioussoul/openagent'

const agent = new OpenAgent({
  // Provider settings
  provider: 'anthropic',           // 'anthropic' | 'openai' | 'google' | 'zhipu' | 'kimi'
  apiKey: 'your-api-key',
  baseURL: 'https://custom.api/v1', // optional proxy
  model: 'claude-sonnet-4-20250514',
  headers: { 'X-Custom': 'value' }, // optional headers
  
  // Behavior settings
  mode: 'build',                   // 'plan' (read-only) | 'build' (full access)
  maxSteps: 10,                    // max tool execution rounds
  temperature: 0.7,
  maxOutputTokens: 4096,
  workingDirectory: '/path/to/dir',
  
  // Custom tools
  tools: [myCustomTool],
  useBuiltinTools: true,           // default: true
})

Core Methods

chat(message, options?): Promise<ChatResult>

Non-streaming chat with automatic multi-step tool execution:

const result = await agent.chat('List files in current directory')
console.log(result.text)        // Final response text
console.log(result.toolCalls)   // Tools that were called
console.log(result.usage)       // Token usage { input, output }
console.log(result.cost)        // Estimated cost in USD
console.log(result.sessionId)   // For continuing conversation

stream(message, options?): AsyncGenerator<AgentStreamEvent>

Streaming chat with real-time events:

for await (const event of agent.stream('Create a React component')) {
  switch (event.type) {
    case 'text':
      process.stdout.write(event.text)
      break
    case 'reasoning':
      console.log('[Thinking]', event.text)
      break
    case 'tool-start':
      console.log(`\nUsing tool: ${event.name}`)
      break
    case 'tool-end':
      console.log(`Result: ${event.output.slice(0, 100)}...`)
      break
    case 'done':
      console.log('\nDone:', event.result.finishReason)
      break
    case 'error':
      console.error('Error:', event.error.message)
      break
  }
}

run(message, options?): Promise<ChatResult>

Chat with progress tracking:

const result = await agent.run('Refactor the auth module', {
  sessionId: 'existing-session-id', // optional
  onProgress: ({ step, total, message }) => {
    console.log(`[${step}/${total}] ${message}`)
  },
  signal: AbortSignal.timeout(60000), // 60s timeout
})

Event System

Listen to agent lifecycle events:

// Available events
type AgentEventType =
  | 'session:start'    // { sessionId, mode }
  | 'session:end'      // { sessionId }
  | 'tool:call'        // { name, input, toolCallId }
  | 'tool:result'      // { name, output, status }
  | 'stream:text'      // { text }
  | 'stream:reasoning' // { text }
  | 'stream:done'      // { result }
  | 'error'            // { error }

// Subscribe to events
const off = agent.on('tool:call', ({ name, input }) => {
  console.log(`Calling ${name}:`, input)
})

// One-time listener
agent.once('session:end', ({ sessionId }) => {
  console.log('Session ended:', sessionId)
})

// Unsubscribe
off()

Abort Control

Cancel long-running operations:

// Using abort() method
setTimeout(() => agent.abort(), 10000)

// Using AbortSignal
const result = await agent.chat('Long task', {
  signal: AbortSignal.timeout(30000), // 30s timeout
})

// Using AbortController
const controller = new AbortController()
setTimeout(() => controller.abort(), 10000)
const result = await agent.chat('Task', { signal: controller.signal })

// Check running status
console.log(agent.running) // true or false

Chain Configuration

Fluent API for configuration:

const agent = anthropic('your-key')
  .setMode('build')
  .setMaxSteps(15)
  .setWorkingDirectory('/path/to/project')
  .setTemperature(0.7)
  .withTools([myCustomTool1, myCustomTool2])

const result = await agent.chat('Help me')

// Get current configuration
console.log(agent.getConfig())
// { provider: 'anthropic', model: '...', mode: 'build', maxSteps: 15, ... }

Session Management

Continue conversations across multiple messages:

// First message - get session ID
const result1 = await agent.chat('Read package.json')
console.log(result1.sessionId)

// Continue same conversation
const result2 = await agent.chat('What version?', { 
  sessionId: result1.sessionId 
})

// Or use fluent API
const result3 = await agent
  .continueSession(result1.sessionId)
  .chat('Summarize it')

// Start fresh session
const result4 = await agent.newSession().chat('New topic')

🛠️ Built-in Tools

| Tool | Description | |------|-------------| | read | Read file contents with line numbers | | write | Create or overwrite files | | edit | Edit files with smart text matching | | bash | Execute shell commands | | glob | Find files by pattern | | grep | Search file contents with regex | | list | List directory contents | | webfetch | Fetch web content | | websearch | Search the web (requires provider config) | | codesearch | Search code examples (requires provider config) | | batch | Execute multiple tools concurrently | | question | Ask user for input | | task | Launch sub-agents for complex tasks | | todowrite | Manage task lists and track progress | | skill | Load specialized skills (git-expert, code-review, etc.) | | process_manage | Manage background processes |

🔧 Custom Tools

Define your own tools with Zod schema:

import { OpenAgent, defineTool } from '@furioussoul/openagent'
import { z } from 'zod'

const calculator = defineTool({
  id: 'calculator',
  description: 'Perform math calculations',
  parameters: z.object({
    expression: z.string().describe('Math expression like "2 + 2"'),
  }),
  execute: async (args) => ({
    title: 'Calculation',
    output: `Result: ${eval(args.expression)}`,
  }),
})

const agent = new OpenAgent({
  provider: 'anthropic',
  apiKey: 'your-key',
  tools: [calculator],
})

🔌 OpenAI-Compatible APIs

Works with any OpenAI-compatible API:

// Together AI
const agent = new OpenAgent({
  provider: 'openai',
  apiKey: process.env.TOGETHER_API_KEY,
  baseURL: 'https://api.together.xyz/v1',
  model: 'meta-llama/Llama-3-70b-chat-hf',
})

// Groq
const agent = new OpenAgent({
  provider: 'openai',
  apiKey: process.env.GROQ_API_KEY,
  baseURL: 'https://api.groq.com/openai/v1',
  model: 'llama-3.1-70b-versatile',
})

// DeepSeek
const agent = new OpenAgent({
  provider: 'openai',
  apiKey: process.env.DEEPSEEK_API_KEY,
  baseURL: 'https://api.deepseek.com/v1',
  model: 'deepseek-chat',
})

// Local Ollama
const agent = new OpenAgent({
  provider: 'openai',
  apiKey: 'not-needed',
  baseURL: 'http://localhost:11434/v1',
  model: 'llama3',
})

📊 Quick Functions

One-liner functions for simple use cases:

import { chat, run, stream } from '@furioussoul/openagent'

// Quick chat
const result = await chat('Hello!', {
  provider: 'anthropic',
  apiKey: process.env.ANTHROPIC_API_KEY,
})

// Quick run with progress
const result2 = await run('List files', {
  provider: 'anthropic',
  apiKey: process.env.ANTHROPIC_API_KEY,
  onProgress: (p) => console.log(p),
})

// Quick stream
for await (const event of stream('Create component', {
  provider: 'anthropic',
  apiKey: process.env.ANTHROPIC_API_KEY,
})) {
  if (event.type === 'text') process.stdout.write(event.text)
}

🔄 Agent Modes

Control tool access with modes:

// Build mode (default) - full access
const agent = new OpenAgent({ mode: 'build' })

// Plan mode - read-only
const agent = new OpenAgent({ mode: 'plan' })

// Switch modes dynamically
agent.setMode('build')
  • build (default): Full access - can read, write, execute commands
  • plan: Read-only - can only read files and search

📦 Sub-path Imports

Import specific modules for tree-shaking:

// Main entry (most common)
import { OpenAgent, anthropic, defineTool } from '@furioussoul/openagent'

// Core module
import { createSession, llmStream, AgentLoop } from '@furioussoul/openagent/core'

// Tool module
import { defineTool, registerTools, builtinTools } from '@furioussoul/openagent/tool'

// Provider module
import { configureProvider, getModelInfo } from '@furioussoul/openagent/provider'

// Plugin module
import { PluginManager, getPluginManager } from '@furioussoul/openagent/plugins'

// Interface module (DI)
import { NoopEventBus, NoopBillingService } from '@furioussoul/openagent/interfaces'

// Utils module
import { createLogger, estimateTokens } from '@furioussoul/openagent/utils'

// Types
import type { AgentMode, Session, ToolContext } from '@furioussoul/openagent/types'

🔌 Plugin System

Extend OpenAgent with plugins:

import { 
  OpenAgent, 
  getPluginManager,
  type OpenAgentPlugin 
} from '@furioussoul/openagent'

// Create custom plugin
const myPlugin: OpenAgentPlugin = {
  name: 'my-plugin',
  version: '1.0.0',
  
  // Custom tools
  tools: [myCustomTool],
  
  // Lifecycle hooks
  onSessionStart: async (sessionId) => {
    console.log(`Session started: ${sessionId}`)
  },
  
  onToolCall: async (toolName, args) => {
    console.log(`Tool called: ${toolName}`)
  },
}

// Register plugin
const pluginManager = getPluginManager()
await pluginManager.register(myPlugin)

// Use with plugin manager
const agent = new OpenAgent({
  provider: 'anthropic',
  apiKey: 'your-key',
  usePluginManager: true,
})

📚 Type Definitions

ChatResult

interface ChatResult {
  text: string           // Final response text
  toolCalls: ToolCall[]  // Tools that were called
  usage: TokenUsage      // { input, output }
  cost: number           // Estimated cost in USD
  finishReason: string   // 'stop', 'tool_use', etc.
  sessionId: string      // For continuing conversation
}

StreamEvent

type AgentStreamEvent =
  | { type: 'text'; text: string }
  | { type: 'reasoning'; text: string }
  | { type: 'tool-start'; name: string; input: Record<string, unknown> }
  | { type: 'tool-end'; name: string; output: string }
  | { type: 'done'; result: ChatResult }
  | { type: 'error'; error: Error }

OpenAgentOptions

interface OpenAgentOptions {
  provider?: 'anthropic' | 'openai' | 'google' | 'zhipu' | 'kimi'
  model?: string
  apiKey?: string
  baseURL?: string
  headers?: Record<string, string>
  agent?: AgentDefinition
  workingDirectory?: string
  useBuiltinTools?: boolean
  tools?: ToolDefinition[]
  sessionStore?: SessionStore
  mode?: 'plan' | 'build'
  maxSteps?: number
  temperature?: number
  maxOutputTokens?: number
  usePluginManager?: boolean
}

📁 Project Structure

packages/openagent/
├── src/
│   ├── index.ts           # Main exports
│   ├── agent.ts           # OpenAgent class
│   ├── types.ts           # Type definitions
│   ├── core/              # Core engine
│   │   ├── agent/         # Agent loop & runner
│   │   ├── session/       # Session management
│   │   ├── events/        # Event system
│   │   └── llm.ts         # LLM streaming
│   ├── tool/              # Tool system
│   │   ├── builtin/       # Built-in tools
│   │   ├── local/         # Local executors
│   │   └── registry.ts    # Tool registry
│   ├── provider/          # Provider configuration
│   ├── plugins/           # Plugin system
│   ├── interfaces/        # DI interfaces
│   └── utils/             # Utility functions
├── examples/              # Usage examples
├── ARCHITECTURE.md        # Architecture docs
└── README.md              # This file

📖 Documentation

📄 License

MIT © furioussoul

🔗 Links