Agents

An agent wraps a model, optional tools, instructions, handoffs, and an event sink. The tool loop state machine lives in Rust. Prefer stream() when you need progressive UI updates.

Create and run

support-agent.ts
import { agent, createOtelListener } from "@monorch/ai";
import { openai } from "@monorch/ai/openai";

const otel = createOtelListener({ serviceName: "support" });

const support = agent({
  name: "support",
  model: openai("gpt-4.1-mini", { baseUrl: process.env.LITELLM_URL }),
  instructions: "Be concise. Prefer tools for facts.",
  tools: [lookupOrder],
  maxSteps: 8,
  onEvent: otel,
});

const { text, runId, events } = await support.run(userMessage);

for await (const ev of support.stream(userMessage)) {
  // run_start | text | tool_call | tool_result | handoff | run_end | error
}

Per-run options

Both run and stream accept AgentRunOptions: load/append thread memory, and abort in-flight model calls.

run-options.ts
import { agent, inMemoryThreads } from "@monorch/ai";

const threads = inMemoryThreads();
const bot = agent({ name: "mem", model, instructions: "Remember prior turns." });

await bot.run("hello", { threadId: "t1", memory: threads });
await bot.run("again", { threadId: "t1", memory: threads });

const ctrl = new AbortController();
const pending = bot.stream("long task", { signal: ctrl.signal });
ctrl.abort(); // stops the loop / aborts provider fetch

Options

name (default agent), model (provider or model handle), instructions, tools (registered tool defs), handoffs (other agents), maxSteps (default 8), onEvent.

Registry

Creating an agent registers it by name (default agent). Use getAgent(name) from graphs (agentNode) or your own wiring. Later agent({ name }) with the same name replaces the registry entry.

Handoffs

Pass handoffs: [billing]. Monorch exposes handoff_to_<name> tools to the model. You can also force a transfer with agent.handoff(target, input). Target must be listed in handoffs.

triage.ts
const billing = agent({
  name: "billing",
  model: mock([{ text: "Refund initiated." }]),
  instructions: "Handle billing.",
});

const triage = agent({
  name: "triage",
  model,
  instructions: "Route billing issues to billing.",
  handoffs: [billing],
});

// model-driven: handoff_to_billing tool
await triage.run("I need a refund");

// programmatic
await triage.handoff(billing, "Customer wants a refund");

What Rust owns

Run id, message history, step counts, pending tool calls, handoff targets, terminal states. JavaScript owns model generate/stream and tool execute.

Result shape

result.ts
type AgentResult = {
  text: string;
  runId: string;
  events: AiEvent[];
};

FAQ