Agent
Define an agent with instructions and a tool, then add only the capabilities it needs.
An agent is a definition: an id, instructions, and the tools the model may
call. You build it with one chained builder, export it from agents/index.ts,
and the Runtime picks the model.
import { Agent, tool } from "@nylorun/agents";
import { z } from "zod";
const lookupOrder = tool({
name: "lookup_order",
description: "Look up a sample order by ID. Try demo-123.",
input: z.object({ orderId: z.string() }),
output: z.object({ orderId: z.string(), status: z.string() }),
async run({ orderId }) {
return { orderId, status: orderId === "demo-123" ? "shipped" : "not found" };
},
});
export const assistant = Agent({ id: "assistant", name: "Order assistant" })
.instructions("Help with orders. Always use lookup_order for order questions.")
.tools(lookupOrder)
.build();Agent({ id, name?, description?, metadata? })takes identity only.idis the stable identifier sessions use;nameis the label Studio shows..instructions()tells the model what to do. See Instructions..tools()adds typed functions that run in your process. See Tools.
Do not set a model on the agent. The Tenant's model settings choose it; see Models.
The builder's rules
- One method per thing. Every capability has its own method; there is no
generic
.use(). - List methods accumulate.
.instructions(),.tools(),.subagents(),.mcp(),.skills(),.plugin()and.capability()add to what is there, and each takes several items at once:.tools(lookupOrder, refundOrder). - Single values are set once.
.output()and.sandbox()fail the build withagent.single-valuewhen called twice. - Every call returns a new builder. The original is unchanged, so a shared base agent can be extended in several ways.
The capability map — every authoring method and compose pattern — is on Build overview. Add only what this agent uses.
Hooks
Patch a turn or decide after a model call:
export const assistant = Agent({ id: "assistant", name: "Order assistant" })
.instructions("Help with orders.")
.tools(lookupOrder)
.beforeTurn(({ info }) => ({
instructions: [`Tenant ${info?.tenantId ?? "unknown"}`],
}))
.build();.beforeTurn(), .beforeModel(), .afterModel(), .afterTurn(), Patch /
Decision / TurnDecision, and retry bounds are on Hooks.
Capabilities
Bundle instructions, tools, and hooks, then attach them with .capability():
import { Agent, capability } from "@nylorun/agents";
const support = capability({ id: "support" })
.instructions("Ask for an order number before using find_order.")
.tools(findOrder);
export const assistant = Agent({ id: "assistant", name: "Order assistant" }).capability(support);A capability has the same methods as a ReAct agent. Ordering, ownership, and catalog rows are on Capabilities.
MCP
Declare MCP servers the Runtime discovers:
export const assistant = Agent({ id: "assistant", name: "Order assistant" })
.instructions("Use the available tools.")
.mcp({
github: { type: "streamable-http", url: "https://mcp.example.com/github" },
});A server's name defaults to its key. Transports, snapshots, and McpError
are on MCP.
Sandbox
Give the agent Runtime-executed computer tools:
export const analyst = Agent({ id: "analyst" })
.instructions("Analyse the data the user gives you. Use Python.")
.sandbox();Images, network presets, backends, idle, and sandbox.* events are on
Sandbox.
Skills
Load an Agent Skills catalog folder:
export const assistant = Agent({ id: "assistant", name: "Order assistant" })
.tools(lookupOrder)
.skills("./assistant-skills");Folder layout, the agentskills.io spec, load_skill /
read_skill_resource, and build-copy are on Skills.
Agent Plugins
Attach an Agent Plugin package as one capability:
export const assistant = Agent({ id: "assistant", name: "Order assistant" })
.instructions("Use the plugin tools and skills.")
.plugin("./plugins/github");loadPlugin, diagnostics, pluginRoot, and plugin MCP/skills are on
Plugins.
.skills() and .plugin() read files, so they are on the Agent from
@nylorun/agents. @nylorun/agents/define exports the same builder without
them, for code that must run anywhere.
Subagents
Add an agent with .subagents() so the model can delegate a self-contained task:
const researcher = Agent({
id: "researcher",
description: "Investigates an order's history. Returns a short summary with the ids it relied on.",
})
.instructions("Investigate one question about one order.")
.tools(searchOrders, readTicket);
export const assistant = Agent({ id: "assistant" })
.instructions(
"For anything needing more than two lookups, delegate to researcher with a complete, self-contained task.",
)
.tools(lookupOrder)
.subagents(researcher);{ task: string }, required description, flow agents as subagents,
ctx.agent, history({ agent }), delegation.* events, and limits are on
Subagents.
Flow agents
Chain stages on the same builder when your code, not the model, decides what runs next:
import { Agent, flow } from "@nylorun/agents";
export const issueDesk = Agent({ id: "issue-desk" })
.step(triage)
.switch(
{
bug: flow().loop(fixer, { verify: tester, max: 3 }),
docs: docsWriter,
default: general,
},
{ on: ({ input }) => input.kind, id: "route" },
)
.parallel({ security: securityReviewer, style: styleReviewer }, { id: "reviews" })
.step(openPr, { input: ({ results }) => ({ title: results.triage.summary }) });Every stage is .stage(whatRuns, { how }), and every stage function receives
{ input, results, flowInput }. See Flow agents.
Details
.build() returns the assembled agent. You can export the builder; generated
projects call .build() so the registry holds a built agent. Invalid tool
schemas, duplicate tool names, or other incompatible contributions raise
AgentBuildError with one diagnostic per problem.
descriptionis optional catalog metadata. It is required when this agent is used as a subagent; see Subagents..output(schema)validates the agent's completed output and types it..instructions()and.tools()on the agent compile as capability id"agent".JSON.stringify(agent)is the public manifest:manifestSchemaVersion: 4for a ReAct agent, a workflow manifest (workflowSchemaVersion: 2) for a flow agent. Bindings stay local;getBinding()is not a wire format.Agent.from(manifest, implementations)reconstructs either kind.
Keep runtime concerns outside the agent
Construct service clients in the application and close over them from tools. Do not put API keys, database handles, HTTP routing, or deployment policy in an agent manifest.
Earlier syntax
The options form and .use() still work for one minor release and print a
DeprecationWarning once per code. They compile to the same manifest.
| Before | Now | Warning code |
|---|---|---|
Agent({ id, instructions, tools, outputSchema }) | Agent({ id }).instructions(…).tools(…).output(…) | NYLORUN_DEP_AGENT_OPTIONS |
agents inside tools: [...] | .subagents(agent) | — |
.use(mcp(…)), .use(sandbox()) | .mcp(…), .sandbox() | NYLORUN_DEP_USE |
.use(skills(dir)), .use(plugin(dir)) | .skills(dir), .plugin(dir) | NYLORUN_DEP_USE |
.use(capability({ id, instructions, tools })) | .capability(capability({ id }).instructions(…).tools(…)) | NYLORUN_DEP_USE, NYLORUN_DEP_CAPABILITY_OPTIONS |
.before("turn"|"step", fn) | .beforeTurn(fn) · .beforeModel(fn) | NYLORUN_DEP_HOOKS |
.after("step"|"turn", fn) | .afterModel(fn) · .afterTurn(fn) | NYLORUN_DEP_HOOKS |
Chain, Switch, Parallel, Map, Loop | Flow agents: Agent({ id }).step(…) | — |
Full signatures are in Reference → define.