npm package: @furioussoul/openagent
Description: A lightweight, extensible AI Agent framework with tool calling, MCP integration, and streaming support
Author: furioussoul
Latest version: 0.1.10

# @furioussoul/openagent

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

[![npm version](https://badge.fury.io/js/@furioussoul%2Fopenagent.svg)](https://www.npmjs.com/package/@furioussoul/openagent)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/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

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

## 🚀 Quick Start

### 30 Seconds to Your First Agent

```typescript
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

```typescript
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

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
// 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:

```typescript
// 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:

```typescript
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:

```typescript
// 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:

```typescript
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:

```typescript
// 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:

```typescript
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:

```typescript
// 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:

```typescript
// 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:

```typescript
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

```typescript
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

```typescript
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

```typescript
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](./ARCHITECTURE.md) - System design and internals
- [Roadmap](./ROADMAP.md) - Future plans
- [Contributing](./CONTRIBUTING.md) - How to contribute

## 📄 License

MIT © [furioussoul](https://github.com/furioussoul)

## 🔗 Links

- [GitHub](https://github.com/furioussoul/OpenAgent)
- [npm](https://www.npmjs.com/package/@furioussoul/openagent)
- [Issues](https://github.com/furioussoul/OpenAgent/issues)