Foundation library for the AgentX framework — a TypeScript toolkit for building AI agent applications with tool use, streaming, and multi-turn conversation.
Foundation library for the AgentX framework — a TypeScript toolkit for building AI agent applications with tool use, streaming, and multi-turn conversation.
@agentxjs/core defines the types, interfaces, and agent engine that every other AgentX package depends on. You typically don't install it directly — it's pulled in as a dependency of agentxjs, @agentxjs/server, or driver packages.
AgentX is built around five concepts that work together:
```
Container ──contains──► Image ──has──► Session
│
created as──► Agent ──uses──► Driver
│
runs on──► Platform
A Container groups related agent configurations. Think of it as a namespace — one per app, workspace, or tenant. All Images inside a Container share the same isolation boundary.
`typescript`
// Create a container for your app
const container = await runtime.getOrCreateContainer("my-app");
An Image is a persistent agent configuration: system prompt, MCP server definitions, and settings. One Image can spawn many Agents over time. When you update the Image, the next Agent created from it picks up the changes.
`typescript`
// Create an image (configuration) inside the container
const image = await createImage(
{
containerId: "my-app",
name: "CodeReviewer",
systemPrompt: "You are a code review assistant.",
mcpServers: {
/ optional tool servers /
},
},
ctx
);
A Session holds the conversation message history for an Image. Each Image has one active Session. Messages accumulate across multiple Agent runs, providing continuity.
`typescript`
// Session is created automatically with the Image
// Messages are persisted and survive agent restarts
const messages = session.getMessages(); // full conversation history
A Driver handles communication with an LLM provider. It takes a user message, sends it to the API, and returns a stream of events (text chunks, tool calls, etc.). To support a new LLM provider, you implement the Driver interface:
`typescript
interface Driver {
receive(message: UserMessage): AsyncIterable
initialize(): Promise
dispose(): Promise
interrupt(): void;
readonly state: DriverState; // "idle" | "active" | "disposed"
}
// Factory function that the runtime calls to create a Driver per agent
type CreateDriver = (config: DriverConfig) => Driver;
`
To add a new LLM provider: implement Driver and export a CreateDriver factory. Your factory receives DriverConfig with apiKey, model, systemPrompt, session (for message history), and mcpServers. See @agentxjs/mono-driver for a reference implementation.
A Platform provides the infrastructure: repositories for data storage, an event bus for streaming, and optional bash execution. To change where data is stored (e.g., from SQLite to PostgreSQL), you implement the repository interfaces and wire them into a Platform:
`typescript`
interface AgentXPlatform {
containerRepository: ContainerRepository; // CRUD for containers
imageRepository: ImageRepository; // CRUD for images + metadata
sessionRepository: SessionRepository; // CRUD for sessions + messages
eventBus: EventBus; // pub/sub for stream events
bashProvider?: BashProvider; // optional shell execution
}
To change storage backend: implement ContainerRepository, ImageRepository, and SessionRepository. Each repository is a simple CRUD interface. See @agentxjs/node-platform for a SQLite reference implementation.
The Runtime doesn't just relay raw driver events — it processes them through a MealyMachine that derives higher-level events:
``
Driver Stream Events (raw)
│
├── message_start, text_delta, tool_use_start, message_stop ...
│
▼
MealyMachine ── pure (state, event) → [newState, outputs]
│
├── MessageAssembler ── stream → message events
│ └── assistant_message, tool_call_message, tool_result_message, error_message
│
├── StateEventProcessor ── stream → state events
│ └── conversation_start, conversation_responding, conversation_end
│
└── TurnTracker ── stream → turn events
└── turn_request (from message_start), turn_response (from message_stop)
Key design: only raw StreamEvents enter the MealyMachine. All message, state, and turn events are derived — never injected from outside. Processors can chain: outputs from one processor feed into others (e.g., TurnTracker reads message_start / message_stop from the stream layer).
The Runtime's Presenter handles the outputs:
- Stream events: emitted directly to EventBus (for real-time UI streaming)
- Message events: emitted to EventBus + persisted to SessionRepository
- State/Turn events: emitted to EventBus
`typescript
// Subscribe to different event layers
runtime.subscribe(agentId, (event) => {
switch (event.type) {
// Stream layer — real-time chunks
case "text_delta":
process.stdout.write(event.data.text);
break;
// Message layer — complete, persisted messages
case "assistant_message":
console.log("Full reply:", event.data.content);
break;
// State layer — conversation lifecycle
case "conversation_start":
case "conversation_end":
break;
// Turn layer — request-response tracking
case "turn_response":
console.log(Turn took ${event.data.duration}ms);`
break;
}
});
Most developers use agentxjs (the SDK) instead of @agentxjs/core directly. Core is for building custom drivers, platforms, or extending the framework.
`typescript
import { createAgentXRuntime } from "@agentxjs/core/runtime";
// Platform and driver come from other packages
const runtime = createAgentXRuntime(platform, createDriver);
const agent = await runtime.createAgent({ imageId: "img_xxx" });
const sub = runtime.subscribe(agent.agentId, (event) => {
if (event.type === "text_delta") {
process.stdout.write(event.data.text);
}
});
await runtime.receive(agent.agentId, "Hello!");
sub.unsubscribe();
await runtime.destroyAgent(agent.agentId);
await runtime.shutdown();
`
`typescript`
import { ... } from "@agentxjs/core"; // common + agent
import { ... } from "@agentxjs/core/agent"; // agent engine
import { ... } from "@agentxjs/core/container"; // container management
import { ... } from "@agentxjs/core/image"; // image management
import { ... } from "@agentxjs/core/session"; // session/message management
import { ... } from "@agentxjs/core/driver"; // LLM driver interface
import { ... } from "@agentxjs/core/platform"; // platform interface
import { ... } from "@agentxjs/core/runtime"; // runtime orchestration
import { ... } from "@agentxjs/core/event"; // event bus system
import { ... } from "@agentxjs/core/bash"; // command execution
import { ... } from "@agentxjs/core/network"; // client-server protocol
import { ... } from "@agentxjs/core/persistence"; // repository interfaces
import { ... } from "@agentxjs/core/mq"; // message queue interface
Orchestrates agent lifecycle. Takes platform and driver as separate parameters.
`typescript
function createAgentXRuntime(platform: AgentXPlatform, createDriver: CreateDriver): AgentXRuntime;
interface AgentXRuntime {
createAgent(options: CreateAgentOptions): Promise
destroyAgent(agentId: string): Promise
receive(agentId: string, content: string | unknown[]): Promise
subscribe(agentId: string, handler: AgentEventHandler): Subscription;
shutdown(): Promise
}
`
`typescript`
interface DriverConfig
apiKey: string;
baseUrl?: string;
model?: string;
timeout?: number; // default: 600000 (10 min)
agentId: string;
systemPrompt?: string;
cwd?: string;
mcpServers?: Record
tools?: ToolDefinition[]; // inline tools
session?: Session; // conversation history
resumeSessionId?: string;
onSessionIdCaptured?: (sessionId: string) => void;
options?: TOptions; // driver-specific options
}
DriverStreamEvent — the events your driver yields:
| Event | Data | When |
| ------------------ | ---------------------------------- | ------------------------ |
| message_start | { messageId, model } | LLM starts responding |text_delta
| | { text } | Incremental text chunk |tool_use_start
| | { toolCallId, toolName } | LLM wants to call a tool |input_json_delta
| | { partialJson } | Incremental tool input |tool_use_stop
| | { toolCallId, toolName, input } | Tool call complete |tool_result
| | { toolCallId, result, isError? } | Tool execution result |message_stop
| | { stopReason } | LLM finished responding |error
| | { message, errorCode? } | Error occurred |
McpServerConfig — two transports for connecting external tool servers:
`typescript
// Local subprocess (stdio)
{ command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"] }
// Remote server (HTTP Streamable)
{ type: "http", url: "https://mcp.example.com/sse" }
`
`typescript`
function createContainer(config: ContainerCreateConfig, ctx: ContainerContext): Promise
function loadContainer(containerId: string, ctx: ContainerContext): Promise
function getOrCreateContainer(containerId: string, ctx: ContainerContext): Promise
ContainerContext provides the repository: { containerRepository: ContainerRepository }.
`typescript`
function createImage(config: ImageCreateConfig, ctx: ImageContext): Promise
function loadImage(imageId: string, ctx: ImageContext): Promise
ImageCreateConfig: { containerId, name?, description?, systemPrompt?, mcpServers? }.ImageContext provides repositories: { imageRepository, sessionRepository }.
`typescript
function createSession(config: SessionConfig): Session;
interface Session {
initialize(): Promise
addMessage(msg: Message): Promise
getMessages(): Message[];
clear(): Promise
}
`
Typed pub/sub event bus.
`typescript`
interface EventBus {
emit(event: BusEvent): void;
on
onAny(handler: BusEventHandler): Unsubscribe;
asProducer(): EventProducer; // write-only view
asConsumer(): EventConsumer; // read-only view
}
Repository interfaces for data storage. Each is a simple CRUD interface:
`typescript
interface ContainerRepository {
create(record: ContainerRecord): Promise
findById(containerId: string): Promise
findAll(): Promise
delete(containerId: string): Promise
}
// ImageRepository and SessionRepository follow the same CRUD pattern
`
`typescript
interface BashProvider {
execute(command: string, options?: BashOptions): Promise
}
interface BashResult {
stdout: string;
stderr: string;
exitCode: number;
}
function createBashTool(provider: BashProvider): ToolDefinition;
`
This package has no runtime configuration. It provides interfaces configured through implementation packages:
| Concern | Configured via |
| ----------- | ------------------------------------------------------------------------------------ |
| Persistence | @agentxjs/node-platform (dataPath) |@agentxjs/mono-driver
| Driver | or @agentxjs/claude-driver (apiKey, provider, model) |new EventBusImpl()
| Event Bus | Created by platform () |NodeBashProvider
| Bash | Created by platform () |
Dependencies: commonxjs, rxjs, jsonrpc-lite`