Examples
Canonical recipes for common agent workflows. Each example shows a complete pattern you can adapt.
Agentic Chatbot
A customer support chatbot that uses @nolag/chat for the user interface and @nolag/agents for AI-powered responses. User messages are dispatched as tasks to support agents, and responses are sent back to the chat room.
import { NoLagChat } from "@nolag/chat";
import { NoLagAgents } from "@nolag/agents";
const chat = new NoLagChat(CHAT_TOKEN);
const agents = new NoLagAgents(AGENT_TOKEN);
await Promise.all([chat.connect(), agents.connect()]);
const chatRoom = chat.joinRoom("support");
const agentRoom = agents.joinRoom("support-workflow");
chatRoom.on("message", async (msg) => {
await agentRoom.handoff.dispatch({
type: "respond",
payload: { text: msg.text, history: chatRoom.getMessages() },
capabilities: ["customer-support"],
});
});
agentRoom.handoff.onResult(async (result) => {
await chatRoom.sendMessage(result.data.response);
});Patterns used: Handoff (task dispatch), composition (cross-app actor access)
Autonomous Workflow
A multi-step pipeline where agents autonomously advance through a plan. Each step's output feeds into the next step. The orchestrator tracks progress on the Blackboard and dispatches the next step when results arrive.
const room = agents.joinRoom("research-pipeline");
// Set the workflow plan
await room.blackboard.set("plan", {
steps: ["research", "analyze", "draft", "review"],
current: 0,
});
// Orchestrator advances through steps
room.handoff.onResult(async (result) => {
const plan = room.blackboard.get("plan");
const nextStep = plan.current + 1;
await room.blackboard.update("plan", (p) => ({
...p,
current: nextStep,
results: { ...p.results, [plan.steps[plan.current]]: result.data },
}));
if (nextStep < plan.steps.length) {
await room.handoff.dispatch({
type: plan.steps[nextStep],
payload: result.data,
capabilities: [plan.steps[nextStep]],
});
}
});
// Kick off the pipeline
await room.handoff.dispatch({
type: "research",
payload: { topic: "AI agent coordination" },
capabilities: ["research"],
});Patterns used: Handoff (task dispatch), Blackboard (workflow state)
Copilot Pattern
An AI copilot that watches editor state via the Blackboard and provides real-time suggestions. The editor publishes its state, an agent processes it, and suggestions flow back to the UI.
const room = agents.joinRoom("copilot");
// User types in the editor
editor.on("change", async (content) => {
await room.blackboard.set("editor-state", {
content,
cursor: editor.getCursorPosition(),
language: editor.getLanguage(),
});
});
// AI agent watches editor state and provides suggestions
room.blackboard.on("change", async (key, value) => {
if (key === "editor-state") {
await room.handoff.dispatch({
type: "suggest",
payload: value,
capabilities: ["code"],
});
}
});
// Display suggestions in the editor
room.handoff.onResult((result) => {
editor.showSuggestion(result.data.suggestion);
});Patterns used: Blackboard (shared state), Handoff (suggestion generation)
Compliance / Approval Workflow
An agent generates a report and requests human approval before publishing. The human can approve, reject, or modify the content. The entire flow is auditable via the Observe pattern.
const room = agents.joinRoom("compliance");
// Agent generates a document and requests approval
const draft = await generateReport(data);
const approval = await room.approve.request({
action: "publish-report",
description: "Q3 earnings report for external publication",
payload: { content: draft, metadata: { author: "finance-agent" } },
timeout: 3600000, // 1 hour
});
if (approval.approved) {
if (approval.modifications) {
// Human may have edited the content
await publishReport(approval.modifications.content);
} else {
await publishReport(draft);
}
} else {
await room.observe.emit("report:rejected", {
reason: approval.reason,
});
}Patterns used: Approve (human-in-the-loop), Observe (audit trail)
Tool-Augmented Agent
Workers use the Tools pattern to call web search, database queries, and other capabilities during task execution. Tools are registered by specialized agents and invoked by workers that need external data.
const room = agents.joinRoom("tool-agent");
// Register available tools
room.tools.register("web-search", {
description: "Search the web",
handler: async (params) => {
return await searchWeb(params.query);
},
});
room.tools.register("database-query", {
description: "Query the analytics database",
handler: async (params) => {
return await db.query(params.sql);
},
});
// Worker uses tools during task execution
room.handoff.onTask(async (task) => {
const searchResults = await room.tools.invoke("web-search", {
query: task.payload.question,
});
const dbResults = await room.tools.invoke("database-query", {
sql: `SELECT * FROM metrics WHERE topic = '${task.payload.topic}'`,
});
const answer = await llm.complete({
prompt: task.payload.question,
context: { searchResults, dbResults },
});
await task.complete({ answer });
});Patterns used: Tools (remote invocation), Handoff (task dispatch)
Monitoring Dashboard
A monitoring agent that collects metrics from the Observe event stream, tracks agent presence, and alerts operations when tasks fail or agents disconnect.
const room = agents.joinRoom("production-workflow");
// Collect metrics
const metrics = { tasks: 0, completed: 0, failed: 0, avgDuration: 0 };
const durations: number[] = [];
room.observe.on("task:dispatched", () => metrics.tasks++);
room.observe.on("task:completed", (event) => {
metrics.completed++;
durations.push(event.data.duration);
metrics.avgDuration = durations.reduce((a, b) => a + b, 0) / durations.length;
});
room.observe.on("task:failed", (event) => {
metrics.failed++;
alertOps("Task failed", event.data);
});
// Track agent health via presence
const agents = room.getPresence();
setInterval(() => {
const current = room.getPresence();
const disconnected = agents.filter(
(a) => !current.find((c) => c.id === a.id)
);
if (disconnected.length) {
alertOps("Agents disconnected", disconnected);
}
}, 10000);Patterns used: Observe (event stream), Presence (agent health)