Advanced Usage
Go beyond the basics with human-in-the-loop workflows, persistence, and recursive orchestration using melony and @melony/react.
Human-in-the-Loop (HITL)
Melony makes it easy to pause agent execution and wait for human approval before proceeding with sensitive operations (like making a payment or sending an email).
You can implement this by intercepting an event and calling context.suspend().
const agent = melony() .on("place-order", async function* (event, context) { if (!event.meta?.approved) { // Suspend the execution and yield an approval request context.suspend({ type: "approval-required", data: { operation: "place-order", params: event.data } }); return; } // Logic for placing the order goes here... yield { type: "assistant:text", data: { content: "Order placed successfully!" } }; });
When an operation requires approval:
- The handler calls
context.suspend(). - Melony stops the execution and yields the provided event.
- The runtime waits for a new event from the client (e.g., the user clicking "Approve").
- Once approved (detected by
meta.approvedin this example), the handler can proceed.
Persistence
By default, Melony runtimes are stateless. To build long-running agents, use an event handler to persist events to your database.
const agent = melony() .on("*", async function* (event, context) { // Log all events to a database await db.events.create({ ...event, runId: context.runId }); });
Recursive Orchestration
When a handler yields an event, it is automatically passed back through the runtime's handlers. This allows for powerful, recursive behaviors.
const agent = melony() .on("research", async function* (event, { runtime }) { yield { type: "assistant:text", data: { content: "Researching..." } }; const data = await myInternalSearch(event.data.query); yield { type: "assistant:text", data: { content: "Synthesizing results..." } }; yield { type: "summarize", data: { text: data } }; }) .on("summarize", async function* (event) { const summary = await summarize(event.data.text); yield { type: "assistant:text", data: { content: summary } }; });
Client-Side Integration (React)
Using @melony/react, you can easily handle custom events and HITL workflows in your UI.
import { useMelony } from "@melony/react"; function Chat() { const { messages, send } = useMelony(); return ( <div> {messages.map(message => ( <div key={message.runId}> {message.content.map(event => { if (event.type === "approval-required") { return ( <div key={event.id}> <p>Approval required for: {event.data.operation}</p> <button onClick={() => send({ type: "place-order", data: event.data.params, meta: { approved: true } })}> Approve </button> </div> ); } // ... handle other events })} </div> ))} </div> ); }