Skip to content

Agents

defineAgent is Kuralle’s single agent primitive. Behavior is derived from which fields you populate — there’s no separate FlowAgent or TriageAgent type.

A minimal agent needs only id, instructions, and model:

import { openai } from '@ai-sdk/openai';
import { defineAgent } from '@kuralle-agents/core';
const agent = defineAgent({
id: 'support',
instructions: 'You are a helpful support agent.',
model: openai('gpt-4o-mini'),
});

From there, the fields you add determine the behavior:

| Add these fields | The agent becomes | |---|---| | tools + tools | A tool-calling agent | | flows | A structured flow agent | | routes + routing | A triage router | | agents + handoffs | A composition wrapper |

These aren’t mutually exclusive. An agent can have tools, a flow, and route to specialists at the same time.

defineAgent({
id: string,
name?: string,
description?: string,
instructions?: Instructions, // string | AgentPrompt | (ctx) => Instructions
model?: LanguageModel,
tools?: ToolSet, // AI SDK ToolSet — what the model sees and can call
tools?: Record<string, EffectTool>, // durable executors (from defineTool)
flows?: Flow[], // structured flow graphs
routes?: Route[], // route entries for triage
routing?: RoutingPolicy, // routing config — see Routing guide
agents?: AgentConfig[], // composed sub-agents
handoffs?: string[], // agent IDs this agent can hand off to
knowledge?: AgentKnowledge, // grounding sources
memory?: AgentMemory, // long-term memory
guardrails?: Guardrails, // input / output guardrails
limits?: Limits, // step / turn limits
})

A string, an AgentPrompt, or a function of session state, describing the agent’s role and rules. Keep it focused on persona and constraints — if you’re writing more than ~20 lines of procedure here, move it to a flow.

tools is the AI SDK ToolSet — what the model sees when deciding what to call. tools is the durable executor map. Pass the same tools to both using buildToolSet:

import { defineTool, buildToolSet } from '@kuralle-agents/core';
const tools = { echo, lookup };
defineAgent({
id: 'support',
instructions: '...',
model: openai('gpt-4o-mini'),
tools: buildToolSet(tools), // model-visible
tools: tools, // durable executor
});

Attach one or more Flow objects (from defineFlow) to make the agent procedure-driven. The runtime enters the flow on the first turn and tracks node state in the session.

See the Flows guide for the full node model.

Add routes to make the agent route between specialists. Pure dispatchers (routes only, no answering surface) classify silently; answering agents use host-control tools plus a lazy guard (it classifies only on a turn that produces no answer and no control tool) so dispatch never leaks as prose.

See Routing & Handoffs.

agents registers sub-agents the runtime can activate during a handoff. handoffs declares which agent IDs this agent is allowed to transfer to. The combination enables tool-based handoffs where the agent explicitly decides when to transfer.

When an agent declares knowledge, the runtime wires retrieval from HarnessConfig.knowledge (the shared KnowledgeProvider). The knowledge.autoRetrieve boolean declares who invokes that retrieval — the runtime, or the model:

| knowledge.autoRetrieve | Behavior | When to use | |---|---|---| | true (default) | Guaranteed — pre-injects retrieved snippets into the system prompt before every answering turn (## Retrieved Knowledge). Always grounded; routing turns on fused host agents pay the retrieval cost. | Regulated, factual, or policy-heavy agents where every answer must be grounded. | | false | On-demand — skips pre-injection and wires a knowledge_search tool the model calls when it needs facts. Routing/dispatch turns pay zero retrieval tax; grounding is model-discretion. | Agents that route often and need fast dispatch, or where retrieval is only needed for some answers. |

The pre-injection provider and the knowledge_search tool are mutually exclusive — the boolean picks the invoker, there is no separate mode to configure. To disable retrieval entirely, omit knowledge.

// Guaranteed (default) — pre-inject every answering turn
defineAgent({
id: 'policy-bot',
knowledge: { autoRetrieve: true }, // or omit — true is the default
// ...
});
// On-demand — retrieve only when the model answers; no routing-turn tax
defineAgent({
id: 'triage-support',
knowledge: { autoRetrieve: false },
// ...
});
define-agent.ts
import { openai } from '@ai-sdk/openai';
import { defineAgent, defineTool, buildToolSet, defineFlow, reply } from '@kuralle-agents/core';
import { z } from 'zod';
// Minimal: chat agent with no flows or routing
const chatAgent = defineAgent({
id: 'chat',
instructions: 'You are a helpful assistant.',
model: openai('gpt-4o-mini'),
});
// Tool agent: model-visible tools + durable executors
const lookup = defineTool({
name: 'lookup',
description: 'Look up a product by ID',
input: z.object({ id: z.string() }),
execute: async ({ id }) => ({ name: `Product ${id}`, price: 49.99 }),
});
const toolAgent = defineAgent({
id: 'catalog',
instructions: 'Answer product questions using the lookup tool.',
model: openai('gpt-4o-mini'),
tools: { lookup },
});
// Flow agent: behavior driven by the flow graph, not the instructions alone
const done = reply({
id: 'done',
instructions: 'Confirm and end the conversation.',
next: () => ({ end: 'complete' }),
});
const flowAgent = defineAgent({
id: 'booking',
instructions: 'You guide users through a booking.',
model: openai('gpt-4o-mini'),
flows: [
defineFlow({
name: 'booking',
description: 'Guide the user through the booking process',
start: done,
nodes: [done],
}),
],
});