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

@compyle/unagent

v1.0.0

Published

Unified AI Agents Framework - A unified AI Agent development framework

Readme

Unagent - Unified AI Agent Development Framework

A unified development framework designed for multiple AI Clients (Claude Agent SDK, Codex, etc.), using a publish-subscribe pattern with support for asynchronous event handling, error isolation, and type safety.

Features

  • Unified Event System - All AI clients use the same event format
  • Async Event Handling - Complete async listener support
  • Error Isolation - Single listener errors don't affect other listeners
  • Type Safety - Complete TypeScript type definitions
  • Extensibility - Plugin system and unified Client interface
  • Agent SDK Integration - Based on the latest Anthropic Claude Agent SDK

Architecture Layers

Application Layer (custom listeners, business logic)
    ↓
Event Bus (EventEmitter)
    ↓
Client Abstraction Layer (Claude Agent SDK, Codex)

Installation

npm install @compyle/unagent

Dependencies

This project uses @anthropic-ai/claude-agent-sdk, which requires installing Claude Code CLI first:

# macOS/Linux/WSL
curl -fsSL https://claude.ai/install.sh | bash

# Or use Homebrew
brew install --cask claude-code

Tests

npm run test

Quick Start

Using Claude Agent SDK

import {
  ClaudeClient,
  getEventBus,
} from "@compyle/unagent";

// 1. Get the event bus and set up custom listeners
const eventBus = getEventBus();

// Listen to all events
eventBus.on("event", (event) => {
  console.log("Event:", event.type);
});

// 2. Use Claude Agent Client
const claude = new ClaudeClient({
  apiKey: process.env.ANTHROPIC_API_KEY,
  allowedTools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"],
  permissionMode: "bypassPermissions",
});

// Stream call
const { events } = await claude.runStream({
  query: "Analyze the current project structure and summarize",
});

for await (const event of events) {
  // Handle events
  if (event.type === "item.end") {
    console.log(event.message);
  }
}

Define a Custom Tool

import { defineTool } from "@compyle/unagent";
import { z } from "zod";

const repoStats = defineTool({
  name: "repo_stats",
  description: "Summarize repository size and extensions",
  inputSchema: {
    path: z.string().min(1),
  },
  handler: async ({ path }) => `Path: ${path}`,
});

Using Custom Agents

const customAgent = new ClaudeClient({
  apiKey: process.env.ANTHROPIC_API_KEY,
  allowedTools: ["Read", "Grep", "Glob", "Task"],
  permissionMode: "bypassPermissions",
  // Define custom sub agents
  agents: {
    "code-reviewer": {
      description: "Expert code review agent",
      prompt: "Analyze code quality, security, and best practices.",
    },
  },
});

const result = await customAgent.run({
  query: "Review code using code-reviewer agent",
});

Read-only Agent (Safe Mode)

const readOnlyAgent = new ClaudeClient({
  apiKey: process.env.ANTHROPIC_API_KEY,
  allowedTools: ["Read", "Glob", "Grep"], // Read-only operations
  permissionMode: "bypassPermissions",
});

const { events } = await readOnlyAgent.runStream({
  query: "Analyze project structure",
});

Using Codex Client with Permission Modes

import { CodexClient } from "@compyle/unagent";

// Safe mode: read-only access
const readOnlyCodex = new CodexClient({
  apiKey: process.env.OPENAI_API_KEY,
  permissionMode: "auto",  // Maps to "read-only" sandbox
  model: "gpt-4o",
});

// Workspace write mode: can modify files in workspace
const writeCodex = new CodexClient({
  apiKey: process.env.OPENAI_API_KEY,
  permissionMode: "acceptEdits",  // Maps to "workspace-write" sandbox
  model: "gpt-4o",
  addDirs: ["/tmp", "/var/tmp"],  // Additional writable directories
});

// Danger mode: full system access (use with caution!)
const fullAccessCodex = new CodexClient({
  apiKey: process.env.OPENAI_API_KEY,
  permissionMode: "bypassPermissions",  // Maps to "danger-full-access" sandbox
  model: "gpt-4o",
});

const { events } = await writeCodex.runStream({
  query: "Create a new file and update existing code",
});

Python Usage

This repository includes a Python bridge package that calls the library through a Node.js process.

Install with uv (Recommended)

# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh

# Or use Homebrew
brew install uv

# Install project dependencies
cd python
bash install_with_uv.sh

# Activate virtual environment
source .venv/bin/activate

# Run examples
ANTHROPIC_API_KEY=your_key python examples/python-demo/main.py

Traditional Installation Method

npm run build
python -m venv .venv
source .venv/bin/activate
pip install -e ./python
ANTHROPIC_API_KEY=your_key python examples/python-demo/main.py
from unagent import ClaudeClient

client = ClaudeClient({"enable_message_mode": True})
stream = client.run_stream({"query": "Summarize the repo"})
for event in stream["events"]:
    print(event["type"])

Examples

Node.js Demo (Orchestration + Custom Tools + Event Bus)

export ANTHROPIC_API_KEY=your_claude_key
cd examples/nodejs-demo
npm install
npm start "Analyze src/ and generate documentation with repo stats"

Python Demos

export ANTHROPIC_API_KEY=your_claude_key
python examples/python-demo/main.py "Analyze src/ and generate documentation with repo stats"

export CODEX_API_KEY=your_codex_key
python examples/python-demo/multi_agent_collaboration.py "Draft an API checklist for a new service"

Core Components

1. Event System (src/events/)

Event Types (event-types.ts)

Defines all unified event types:

  • SessionStartEvent - Session starts
  • SessionEndEvent - Session ends
  • ItemStartEvent - Message/item starts
  • ItemChunkEvent - Streaming chunk for a message/item
  • ItemEndEvent - Message/item ends with full message
  • ErrorEvent - Error event

Event Bus (event-bus.ts)

Provides an event bus using the publish-subscribe pattern:

import { getEventBus } from "@compyle/unagent";

const eventBus = getEventBus();

// Register listeners
eventBus.on("event", (event) => {
  console.log("Event:", event);
});

// Emit events
eventBus.emit({
  type: "session.start",
  timestamp: Date.now(),
  clientType: ClientType.CLAUDE,
  sessionId: "session-123",
  params: { query: "Hello" },
});

2. Client Abstraction Layer (src/client/)

BaseClient

Base class for all AI clients, defining a unified interface:

interface IBaseClient {
  run(params: ClientRunParams): Promise<ClientRunResult>;
  runStream(params: ClientRunParams): Promise<ClientStreamResult>;
  on(listener: EventListener): string;
  once(listener: EventListener): string;
  off(listenerOrId: string | EventListener): void;
  removeAllListeners(): void;
  getClientType(): ClientType;
}

ClaudeClient

Implementation based on Anthropic Agent SDK:

const claude = new ClaudeClient({
  apiKey: process.env.ANTHROPIC_API_KEY,
  allowedTools: ["Read", "Write", "Edit", "Bash"],
  permissionMode: "bypassPermissions",
  useProjectConfig: true, // Read .claude/ directory config
});

// Stream call
const { events } = await claude.runStream({ query: "Analyze code" });

// Non-stream call
const result = await claude.run({ query: "Analyze code" });

ClaudeClient Configuration Options:

| Option | Type | Description | |------|------|------| | apiKey | string | Anthropic API key | | allowedTools | string[] | List of allowed tools | | permissionMode | "auto" \| "bypassPermissions" \| "acceptEdits" | Permission mode | | agents | Record<string, AgentDefinition> | Custom sub agents | | mcpServers | Record<string, McpServerConfig> | MCP server configuration | | hooks | Record<string, any> | Hooks configuration | | useProjectConfig | boolean | Whether to use project config | | workingDirectory | string | Working directory |

Using Codex Client

import { CodexClient } from "@compyle/unagent";

const codex = new CodexClient({
  apiKey: process.env.OPENAI_API_KEY,
  permissionMode: "acceptEdits",  // Maps to "workspace-write" sandbox mode
  model: "gpt-4o",
});

// Stream call
const { events } = await codex.runStream({ query: "Analyze code" });

// Non-stream call
const result = await codex.run({ query: "Analyze code" });

CodexClient Configuration Options:

| Option | Type | Description | |------|------|------| | apiKey | string | OpenAI API key | | permissionMode | "auto" \| "default" \| "acceptEdits" \| "bypassPermissions" | Permission mode (maps to sandbox mode) | | model | string | Model name (e.g., "gpt-4o") | | systemPrompt | string | System prompt | | mcpServers | Record<string, McpServerConfig> | MCP server configuration | | addDirs | string[] | Additional directories for access | | workingDirectory | string | Working directory | | env | Record<string, string> | Environment variables (e.g., CODEX_HOME) |

If env.CODEX_HOME is not set, a temporary directory is created under {TMPDIR}/unagent/sessions/<id>.

Codex Permission Mode Mapping:

The permissionMode option maps to Codex sandbox modes as follows:

| permissionMode | Codex sandboxMode | Description | |-----------------|-------------------|-------------| | "auto" / "default" / "dontAsk" / "plan" | "read-only" | Read-only access (safest) | | "acceptEdits" | "workspace-write" | Can write to workspace and additional directories | | "bypassPermissions" | "danger-full-access" | No sandbox, full system access (riskiest) |

Available Tools:

  • Read - Read files
  • Write - Create new files
  • Edit - Edit existing files
  • Bash - Run commands
  • Glob - Find files
  • Grep - Search file contents
  • WebSearch - Web search
  • WebFetch - Fetch web content
  • Task - Call sub agent
  • AskUserQuestion - Ask user questions

Event Types

All events inherit from BaseEvent and include timestamp, clientType, and optional sessionId.

SessionStartEvent

{
  type: "session.start",
  timestamp: number,
  clientType: ClientType,
  sessionId: string,
  params?: { query?: string, ... }
}

SessionEndEvent

{
  type: "session.end",
  timestamp: number,
  clientType: ClientType,
  sessionId?: string,
  reason?: string
}

ItemStartEvent

{
  type: "item.start",
  timestamp: number,
  clientType: ClientType,
  message: Message
}

ItemChunkEvent

{
  type: "item.chunk",
  timestamp: number,
  clientType: ClientType,
  message: Message
}

ItemEndEvent

{
  type: "item.end",
  timestamp: number,
  clientType: ClientType,
  message: Message
}

ErrorEvent

{
  type: "error",
  timestamp: number,
  clientType: ClientType,
  error: Error,
  context?: { stage?: string, ... }
}

Advanced Usage

Adding MCP Servers

const claude = new ClaudeClient({
  apiKey: process.env.ANTHROPIC_API_KEY,
  allowedTools: ["Read", "Bash"],
  mcpServers: {
    playwright: {
      command: "npx",
      args: ["@playwright/mcp@latest"],
    },
  },
});

Using Hooks

const claude = new ClaudeClient({
  apiKey: process.env.ANTHROPIC_API_KEY,
  hooks: {
    PostToolUse: [{
      matcher: "Edit|Write",
      hooks: [async (input) => {
        // Log file modifications
        console.log("File modified:", input.tool_input?.file_path);
        return {};
      }],
    }],
  },
});

Session Resumption

let sessionId: string | undefined;

// First query
for await (const message of query(...)) {
  if (message.type === "system" && message.subtype === "init") {
    sessionId = message.session_id;
  }
}

// Resume session
const resumed = new ClaudeClient({
  apiKey: process.env.ANTHROPIC_API_KEY,
  resume: sessionId,
});

Development

Build

npm run build

Examples

See examples/basic-usage.ts for complete usage examples.

File Structure

src/
├── events/           # Event system core
│   ├── event-types.ts    # Event type definitions
│   ├── event-bus.ts      # Event bus implementation
│   └── index.ts          # Export module
├── messages/         # Message system
│   ├── message-types.ts  # Message type definitions
│   ├── message-utils.ts  # Message utility functions
│   └── index.ts          # Export module
├── client/           # Client abstraction layer
│   ├── base-client.ts    # Base client interface and abstract class
│   ├── codex/            # Codex client implementation
│   └── claude.ts         # Claude Agent SDK implementation
├── tools/            # Tool system
├── agents/           # Agent system
└── index.ts          # Main entry file

Migration Guide

If migrating from the old @anthropic-ai/sdk, the main changes are:

  1. No need to call messages.create() directly - Agent SDK automatically handles tool execution through query() function
  2. Configuration options changed - Use allowedTools and permissionMode instead of model, maxTokens, etc.
  3. Event format changed - New SDK returns different event formats, which are automatically converted to unified format

License

MIT