@furioussoul/openagent
v0.1.10
Published
A lightweight, extensible AI Agent framework with tool calling, MCP integration, and streaming support
Maintainers
Readme
@furioussoul/openagent
A lightweight, extensible AI Agent framework with tool calling, streaming, multi-provider support, and a powerful event system.
✨ Features
- 🚀 Simple API - One-line initialization, easy
chat(),stream(), andrun()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 conversationstream(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 falseChain 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 commandsplan: 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
- Architecture - System design and internals
- Roadmap - Future plans
- Contributing - How to contribute
📄 License
MIT © furioussoul
