@ellie-ai/ag-ui-adapter-plugin
v0.2.0
Published
AG-UI protocol adapter plugin for Ellie runtime
Maintainers
Readme
@ellie-ai/ag-ui-adapter-plugin
AG-UI protocol adapter plugin for Ellie runtime. Provides AG-UI wire protocol compatibility while preserving 100% of Ellie's observability features.
Features
- AG-UI Protocol Compatibility - Maps Ellie's 45+ actions to standard AG-UI events (RunStarted, TextMessageContent, ToolCallStart, etc.)
- RFC 6902 JSON Patch - Generates STATE_DELTA events with compliant JSON Patch operations for incremental state updates
- Dual Observability - Both Ellie native actions AND AG-UI events visible in Redux DevTools
- Zero Breaking Changes - Opt-in plugin that doesn't affect existing Ellie users
- SSE Streaming - Events emitted via EventEmitter for Server-Sent Events integration
Installation
bun add @ellie-ai/ag-ui-adapter-plugin fast-json-patchUsage
Basic Setup
import { createRuntime } from "@ellie-ai/runtime";
import { agentPlugin } from "@ellie-ai/agent-plugin";
import { agUIAdapterPlugin } from "@ellie-ai/ag-ui-adapter-plugin";
import { createServer, createServerPlugin } from "@ellie-ai/server";
import { anthropic } from "@ellie-ai/model-anthropic";
// 1. Create AG-UI adapter plugin
const aguiAdapter = agUIAdapterPlugin({
trackedSlices: ["agent"], // Which state slices to track for STATE_DELTA
emitStateDelta: true, // Enable STATE_DELTA events
});
// 2. Create runtime with adapter plugin
const runtime = createRuntime({
plugins: [
agentPlugin({ model: anthropic() }),
aguiAdapter,
serverPlugin.plugin,
] as const,
});
// 3. Create server with AG-UI route
const server = createServer({
runtime,
plugin: serverPlugin,
agUIAdapterBus: aguiAdapter.bus, // Pass adapter bus to enable /events/ag-ui route
});
server.listen(3000);Consuming AG-UI Events
Connect to the /events/ag-ui endpoint to receive AG-UI protocol events:
const events = new EventSource("http://localhost:3000/events/ag-ui");
events.addEventListener("RunStarted", (e) => {
const event = JSON.parse(e.data);
console.log("Execution started:", event.runId);
});
events.addEventListener("TextMessageContent", (e) => {
const event = JSON.parse(e.data);
console.log("Assistant:", event.content);
});
events.addEventListener("ToolCallStart", (e) => {
const event = JSON.parse(e.data);
console.log("Tool call:", event.toolName, event.input);
});
events.addEventListener("STATE_DELTA", (e) => {
const event = JSON.parse(e.data);
// Apply RFC 6902 JSON Patch operations
applyPatch(state, event.patches);
});AG-UI Event Mapping
Ellie → AG-UI Event Mapping
| Ellie Action | AG-UI Event | Notes |
|--------------|-------------|-------|
| RUNTIME_EXECUTION_STARTED | RunStarted | Direct mapping |
| RUNTIME_EXECUTION_COMPLETED | RunFinished | status: "completed" |
| RUNTIME_EXECUTION_FAILED | RunFinished | status: "failed", includes error |
| RUNTIME_EXECUTION_ABORTED | RunFinished | status: "cancelled" |
| AGENT_MODEL_RESPONDED | TextMessageContent | Extract text content |
| AGENT_MODEL_STREAM_CONTENT_DELTA | TextMessageContent | Chunked streaming |
| AGENT_MODEL_STREAM_REASONING_DELTA | ReasoningContent | Extended thinking |
| AGENT_MODEL_STREAM_COMPLETED | TextMessageContent | Stream complete marker |
| AGENT_TOOL_CALL_REQUESTED | ToolCallStart | Direct mapping |
| AGENT_TOOL_CALL_EXECUTED | ToolCallOutput | Direct mapping |
| AGENT_TOOL_CALL_FAILED | ToolCallError | Includes error details |
| AGENT_TOOL_MISSING | ToolCallError | code: "TOOL_NOT_FOUND" |
| State changes | STATE_DELTA | RFC 6902 JSON Patch |
Unmapped Ellie Actions
The following Ellie actions are not mapped to AG-UI events (preserved in native /events endpoint only):
RUNTIME_WORK_*- Internal work trackingAGENT_CONTEXT_PREPARATION_NEEDED- Internal coordinationAGENT_MODEL_REQUESTED- Only responses matter for AG-UIAGENT_MODEL_STREAM_STARTED- Covered by first content deltaAGENT_LOOP_INCREMENTED- Internal trackingAGENT_REGISTER_TOOL- Not in AG-UI specAGENT_DEREGISTER_TOOL- Not in AG-UI specAGENT_SYSTEM_MESSAGE_UPDATED- Internal stateAGENT_CONVERSATION_TRUNCATED- Internal state
Configuration Options
interface AGUIAdapterConfig {
/**
* EventEmitter for emitting AG-UI events
* If not provided, creates a new EventEmitter
*/
bus?: EventEmitter;
/**
* Which state slices to track for STATE_DELTA events
* Default: ['agent'] (only track agent slice)
*/
trackedSlices?: string[];
/**
* Whether to emit STATE_DELTA events on state changes
* Default: true
*/
emitStateDelta?: boolean;
/**
* Minimum delay between STATE_DELTA emissions (ms)
* Used to throttle high-frequency state updates
* Default: 0 (no throttling)
*/
stateDeltaThrottleMs?: number;
}Advanced Usage
Listening to AG-UI Events in Middleware
import { agUIAdapterPlugin } from "@ellie-ai/ag-ui-adapter-plugin";
const adapter = agUIAdapterPlugin();
// Listen to AG-UI events
adapter.bus.on("ag-ui.event", (event) => {
if (event.type === "RunStarted") {
console.log("New execution started:", event.runId);
}
});Applying JSON Patch Operations
import { applyPatch } from "fast-json-patch";
let state = { agent: { conversation: [], loop: { count: 0 } } };
events.addEventListener("STATE_DELTA", (e) => {
const event = JSON.parse(e.data);
const patchResult = applyPatch(state, event.patches, true);
state = patchResult.newDocument;
});Redux DevTools Integration
All AG-UI events are dispatched as AG_UI_EVENT_EMITTED actions, making them visible in Redux DevTools alongside native Ellie actions:
{
type: "AG_UI_EVENT_EMITTED",
payload: {
event: {
type: "RunStarted",
runId: "exec-123",
input: "Hello",
timestamp: 1704067200000
},
sourceAction: "RUNTIME_EXECUTION_STARTED"
}
}Performance Considerations
State Diff Generation
- Memory Overhead: Tracking previous state for JSON Patch adds ~2-5MB overhead
- Mitigation: Only track
agentslice by default (configurable viatrackedSlices) - Performance:
fast-json-patchgenerates diffs at ~1000 ops/sec for 10KB objects
Throttling STATE_DELTA Events
For high-frequency state updates, use stateDeltaThrottleMs:
const adapter = agUIAdapterPlugin({
stateDeltaThrottleMs: 100, // Max 10 STATE_DELTA events per second
});Architecture
Plugin Flow
┌─────────────────────────────────────┐
│ Ellie Runtime + Plugins │
│ ├─ agent-plugin (45+ actions) │
│ └─ ag-ui-adapter-plugin (NEW) │
│ • Listens to Ellie actions │
│ • Maps to AG-UI events │
│ • Generates JSON Patch deltas │
│ • Emits AG_UI_EVENT_EMITTED │
└──────────────┬──────────────────────┘
│ Both protocols in Redux DevTools
▼
┌─────────────────────────────────────┐
│ Server with Dual Endpoints │
│ ├─ GET /events (Ellie native) │
│ └─ GET /events/ag-ui (AG-UI) │
└──────────────┬──────────────────────┘
│
├──► Ellie Client (full observability)
└──► AG-UI Client (standard protocol)Key Principles
- Zero Breaking Changes - Existing Ellie users unaffected
- Dual Emission - Both Ellie actions AND AG-UI events emitted
- 100% Observability - All 45 Ellie actions preserved in Redux DevTools
- Standard Protocol - AG-UI events follow specification
- Clean Layering - Adapter is pure transformation, no business logic
Troubleshooting
No AG-UI events received
Ensure you passed agUIAdapterBus to createServer:
const server = createServer({
runtime,
plugin: serverPlugin,
agUIAdapterBus: aguiAdapter.bus, // Required for /events/ag-ui
});STATE_DELTA events not emitting
Check that emitStateDelta is enabled (default: true):
const adapter = agUIAdapterPlugin({
emitStateDelta: true, // Ensure this is true
});High memory usage
Reduce tracked state slices:
const adapter = agUIAdapterPlugin({
trackedSlices: ["agent"], // Only track agent slice (default)
});API Reference
See the full API documentation for detailed type definitions and event schemas.
Related
License
MIT
