Core Concepts
Understanding the building blocks of a Melony agent.
Events: The Universal Currency
In Melony, everything is an Event. Whether it's a piece of text, a tool result, or custom data, it's represented as a standard event object.
interface Event { type: string; data: any; meta?: EventMeta; // id, runId, timestamp, role, state, etc. }
The Runtime (created via the melony() builder) manages the flow of these events from the server to the client.
Event Handlers: Reactive Logic
Event handlers are the "brain" of your agent. They listen for specific event types and can yield new events. This event-driven approach allows for highly decoupled and reactive logic.
Example: Routing with Event Handlers
const agent = melony() .on("user:text", async function* (event, { runtime }) { // Route text messages based on content if (event.data.content.includes("weather")) { yield { type: "assistant:text", data: { content: "Weather in NYC is 72°F" } }; } });
Handler Context
Handlers receive a context object which provides:
state: The current run state.runtime: Access to the runtime instance (to trigger other events).runId: Unique ID for the current execution.suspend: A function to immediately halt execution (useful for Human-in-the-Loop).
Plugin System: Modular Agent Logic
Melony features a lightweight plugin system that allows you to modularize and reuse handlers across different agents. A plugin is simply a function that receives the MelonyBuilder.
import { melony, MelonyPlugin } from "melony"; const loggingPlugin: MelonyPlugin = (builder) => { builder.on("*", async function* (event) { console.log(`[Plugin] Event: ${event.type}`); }); }; const agent = melony() .use(loggingPlugin) .on("user:text", async function* () { yield { type: "assistant:text", data: { content: "Hello!" } }; });
Chaining & Recursion
When a handler yields an event, that event is automatically passed back through the runtime's handlers. This allows for powerful, recursive behaviors.
const agent = melony() .on("greet", async function* () { yield { type: "assistant:text", data: { content: "Hello!" } }; }) .on("user:text", async function* (event) { if (event.data.content === "hi") { yield { type: "greet", data: {} }; // This will trigger the 'greet' handler } });