@agentxjs/runtime
v1.9.0
Published
Runtime for AI Agents - Agent execution, SystemBus, Environment, and Repository
Downloads
228
Readme
@agentxjs/runtime
Complete Runtime implementation for AI Agents with Claude SDK integration
Overview
@agentxjs/runtime provides the complete runtime infrastructure for AI agents, including:
- SystemBus - Event-driven communication backbone
- Container - Agent lifecycle management
- Environment (Receptor + Effector) - External world interface (Claude SDK)
- Session - Message persistence
- Persistence - Storage layer (SQLite, Memory)
Key Features:
- Event-Driven Architecture - All operations via command events
- Claude SDK Integration - Production-ready ClaudeEnvironment
- Pluggable Storage - SQLite, Memory, or custom backends
- Type-Safe Commands - Request/response pattern with full typing
- Cross-Platform - Works in Node.js (Browser support via agentxjs)
Installation
pnpm add @agentxjs/runtimeDependencies:
@anthropic-ai/claude-agent-sdk- Claude API clientbetter-sqlite3- SQLite database (optional, for persistence)unstorage- Unified storage layer
Quick Start
Minimal Setup
import { createRuntime } from "@agentxjs/runtime";
// Minimal - uses environment variable ANTHROPIC_API_KEY
const runtime = createRuntime();
// Subscribe to events
runtime.on("text_delta", (e) => {
console.log(e.data.text);
});
// Create container
const containerRes = await runtime.request("container_create_request", {
containerId: "my-container",
});
// Run agent
const agentRes = await runtime.request("agent_run_request", {
containerId: "my-container",
config: {
name: "Assistant",
systemPrompt: "You are a helpful assistant",
},
});
// Send message
await runtime.request("agent_receive_request", {
agentId: agentRes.data.agentId,
content: "Hello!",
});
// Cleanup
await runtime.dispose();With Configuration
import { createRuntime, createPersistence } from "@agentxjs/runtime";
import { sqliteDriver } from "@agentxjs/persistence/sqlite";
const runtime = createRuntime({
// LLM configuration
llm: {
apiKey: process.env.ANTHROPIC_API_KEY,
baseUrl: "https://api.anthropic.com",
},
// Persistence (using sqlite driver)
persistence: await createPersistence(sqliteDriver({ path: "./data.db" })),
// Logger
logger: {
level: "info",
enableTimestamp: true,
},
});Architecture
System Overview
┌────────────────────────────────────────────────────────────────┐
│ Runtime │
├────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ SystemBus │ │
│ │ (Central event bus - all communication flows here) │ │
│ └──────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌──────────┐ ┌────────────┐ │
│ │Environment │ │Container │ │Persistence │ │
│ │ │ │ │ │ │ │
│ │ Receptor │ │ Agent1 │ │ SQLite │ │
│ │ Effector │ │ Agent2 │ │ Images │ │
│ │ (Claude SDK)│ │ Agent3 │ │ Sessions │ │
│ └─────────────┘ └──────────┘ └────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘Component Responsibilities
| Component | Role | Examples | | --------------- | ------------------------ | --------------------------------- | | SystemBus | Event distribution | Emit, subscribe, request/response | | Environment | External world interface | Claude API calls, streaming | | Container | Agent lifecycle | Create, destroy, manage agents | | Session | Message persistence | Save/load conversation history | | Persistence | Storage backend | SQLite, Memory, custom |
SystemBus - Event Backbone
All Runtime operations go through the SystemBus using command events.
Request/Response Pattern
// Type-safe request
const response = await runtime.request("container_create_request", {
containerId: "my-container",
});
// Response is fully typed
console.log(response.data.containerId); // ✓ TypeScript knows this existsEvent Subscription
// Subscribe to specific event type
runtime.on("text_delta", (event) => {
console.log(event.data.text);
});
// Subscribe to multiple types
runtime.on(["message_start", "message_stop"], (event) => {
console.log(event.type);
});
// Subscribe to all events
runtime.onAny((event) => {
console.log(event.type, event.data);
});
// Unsubscribe
const unsubscribe = runtime.on("text_delta", handler);
unsubscribe();Available Commands
Container Commands
// Create container
const res = await runtime.request("container_create_request", {
containerId: "my-container",
});
// Destroy container
await runtime.request("container_destroy_request", {
containerId: "my-container",
});Agent Commands
// Run agent
const res = await runtime.request("agent_run_request", {
containerId: "my-container",
config: {
name: "Assistant",
systemPrompt: "You are helpful",
},
});
// Send message
await runtime.request("agent_receive_request", {
agentId: res.data.agentId,
content: "Hello!",
});
// Interrupt agent
await runtime.request("agent_interrupt_request", {
agentId: res.data.agentId,
});
// Destroy agent
await runtime.request("agent_destroy_request", {
agentId: res.data.agentId,
});Session Commands
// Create session
await runtime.request("session_create_request", {
sessionId: "session_123",
containerId: "my-container",
agentId: "agent_123",
});
// Get session
const res = await runtime.request("session_get_request", {
sessionId: "session_123",
});
// Delete session
await runtime.request("session_delete_request", {
sessionId: "session_123",
});Direct Command Emission
For advanced use cases:
// Emit command directly (fire and forget)
runtime.emitCommand("agent_receive_request", {
requestId: "req_123",
agentId: "agent_123",
content: "Hello!",
});
// Listen for response
runtime.onCommand("agent_receive_response", (event) => {
if (event.data.requestId === "req_123") {
console.log("Response:", event.data);
},
});Environment - External World Interface
Environment connects Runtime to the external world (Claude API).
ClaudeEnvironment
Built-in implementation using @anthropic-ai/claude-agent-sdk:
import { createRuntime } from "@agentxjs/runtime";
const runtime = createRuntime({
llm: {
apiKey: process.env.ANTHROPIC_API_KEY,
baseUrl: "https://api.anthropic.com",
},
});Architecture
┌────────────────────────────────────────────────────────────┐
│ Environment │
├────────────────────────────────────────────────────────────┤
│ │
│ Receptor (Perceives external world → emits to SystemBus) │
│ │ │
│ │ Claude API Streaming Response │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Transforms Claude SDK events → DriveableEvent │ │
│ │ (message_delta, tool_use, etc.) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ │ emit to SystemBus │
│ ▼ │
│ SystemBus │
│ │ │
│ │ subscribe │
│ ▼ │
│ Effector (Subscribes to SystemBus → acts on external world)│
│ │ │
│ │ Executes tool calls │
│ ▼ │
│ External Tools (MCP, filesystem, etc.) │
│ │
└────────────────────────────────────────────────────────────┘Container - Agent Lifecycle
Container manages multiple agents:
// Via command events (recommended)
const res = await runtime.request("container_create_request", {
containerId: "my-container",
});
// Run agents in container
const agent1 = await runtime.request("agent_run_request", {
containerId: "my-container",
config: { name: "Agent1" },
});
const agent2 = await runtime.request("agent_run_request", {
containerId: "my-container",
config: { name: "Agent2" },
});
// Destroy container (destroys all agents)
await runtime.request("container_destroy_request", {
containerId: "my-container",
});Persistence - Storage Layer
Persistence is now provided by the separate @agentxjs/persistence package with subpath exports for each driver.
Built-in Storage Backends
SQLite (Production)
import { createRuntime, createPersistence } from "@agentxjs/runtime";
import { sqliteDriver } from "@agentxjs/persistence/sqlite";
const runtime = createRuntime({
persistence: await createPersistence(sqliteDriver({ path: "./data.db" })),
});Memory (Development)
import { createRuntime, createPersistence, memoryDriver } from "@agentxjs/runtime";
const runtime = createRuntime({
persistence: await createPersistence(memoryDriver()),
});Other Drivers
// Redis
import { redisDriver } from "@agentxjs/persistence/redis";
const persistence = await createPersistence(redisDriver({ url: "redis://localhost:6379" }));
// MongoDB
import { mongodbDriver } from "@agentxjs/persistence/mongodb";
const persistence = await createPersistence(mongodbDriver({ connectionString: "mongodb://..." }));
// MySQL
import { mysqlDriver } from "@agentxjs/persistence/mysql";
const persistence = await createPersistence(mysqlDriver({ host: "localhost", database: "agentx" }));
// PostgreSQL
import { postgresqlDriver } from "@agentxjs/persistence/postgresql";
const persistence = await createPersistence(
postgresqlDriver({ host: "localhost", database: "agentx" })
);Storage Schema
Persistence stores:
- Images - Agent snapshots with conversation history
- Containers - Container metadata
- Sessions - Message history per agent
// Save agent image
const image = await runtime.request("image_snapshot_request", {
agentId: "agent_123",
});
// Resume from image
const agent = await runtime.request("image_resume_request", {
imageId: image.data.imageId,
});Advanced Usage
Custom Persistence Backend
import { type StorageDriver } from "@agentxjs/runtime";
const customDriver: StorageDriver = {
async getItem(key) {
// Fetch from your backend
},
async setItem(key, value) {
// Save to your backend
},
async removeItem(key) {
// Delete from your backend
},
async getKeys(base) {
// List keys with prefix
},
async clear() {
// Clear all data
},
};
const runtime = createRuntime({
persistence: createPersistence({
driver: customDriver,
}),
});Event Filtering
// Filter events by agent
runtime.on(
"text_delta",
(event) => {
console.log(event.data.text);
},
{
filter: (event) => event.context?.agentId === "agent_123",
}
);
// Priority execution
runtime.on("message_stop", handler, {
priority: 10, // Higher priority runs first
});
// One-time subscription
runtime.once("agent_created", (event) => {
console.log("Agent created:", event.data.agentId);
});Testing
Runtime is designed for easy testing:
import { createRuntime, createPersistence, memoryDriver } from "@agentxjs/runtime";
import { describe, it, expect } from "vitest";
describe("Runtime", () => {
it("creates and runs agent", async () => {
const runtime = createRuntime({
llm: {
apiKey: "test-key",
},
persistence: await createPersistence(memoryDriver()),
});
// Create container
const containerRes = await runtime.request("container_create_request", {
containerId: "test-container",
});
expect(containerRes.data.containerId).toBe("test-container");
// Run agent
const agentRes = await runtime.request("agent_run_request", {
containerId: "test-container",
config: { name: "TestAgent" },
});
expect(agentRes.data.name).toBe("TestAgent");
await runtime.dispose();
});
});Design Decisions
Why Event-Driven?
Event-driven architecture enables:
- Decoupling - Components communicate via events, not direct calls
- Extensibility - Add new components without modifying existing ones
- Testability - Mock events instead of entire components
- Observability - All operations are visible as events
Why SystemBus?
SystemBus provides:
- Single Source of Truth - All communication flows through one point
- Type Safety - Commands are fully typed
- Request/Response - Async operations with correlation IDs
- Priority/Filtering - Advanced subscription options
Why Separate Environment?
Environment abstraction allows:
- Multiple Backends - Claude, OpenAI, local models
- Testing - Mock Environment in tests
- Cross-Platform - Different implementations for Node.js, Browser
- Clear Boundary - External world vs. internal logic
Configuration Reference
interface RuntimeConfig {
// LLM configuration
llm?: {
apiKey?: string; // Default: process.env.ANTHROPIC_API_KEY
baseUrl?: string; // Default: "https://api.anthropic.com"
};
// Persistence
persistence?: Persistence;
// Logger
logger?: {
level?: LogLevel;
enableTimestamp?: boolean;
enableColor?: boolean;
};
}Environment Variables
# Required
ANTHROPIC_API_KEY=sk-ant-xxxxx
# Optional
ANTHROPIC_BASE_URL=https://api.anthropic.com # Custom endpoint
LOG_LEVEL=info # debug, info, warn, errorPackage Dependencies
@agentxjs/types Type definitions
↑
@agentxjs/common Logger facade
↑
@agentxjs/persistence Storage layer (drivers as subpath exports)
↑
@agentxjs/agent AgentEngine
↑
@agentxjs/runtime This package (Runtime + Environment)
↑
agentxjs High-level unified APIRelated Packages
- @agentxjs/types - Type definitions
- @agentxjs/common - Logger facade
- @agentxjs/persistence - Storage layer with driver subpath exports
- @agentxjs/agent - AgentEngine
- agentxjs - High-level unified API
License
MIT
