# Defining agents

> Declare an agent with defineAgent: its model and fallbacks, instructions, tools, delegates, memory settings, and step and tool-call limits.

`defineAgent` declares an agent: which model answers, what it is told, which tools it may call, how long its memory is, and how many steps it may take. One file per agent under `functions/_agents/`, default-exporting the definition. The dev server and the deploy build load every file in that directory, so you never import an agent yourself; functions reach it by name through `ctx.agent(name)`.

:::note{title="Version"}
Needs `@trayai/helix-sdk` and `@trayai/helix-cli` newer than 1.6.0.
:::

## A complete definition

```typescript
// functions/_agents/support-agent.ts
import { defineAgent } from '@trayai/helix-sdk';
import lookupOrder from '../_shared/tools/lookup-order';
import issueRefund from '../_shared/tools/issue-refund';

export default defineAgent({
  name: 'support-agent',
  description: 'Resolves order and refund questions for a signed-in customer.',

  model: 'claude-sonnet-4-5',
  auth: 'anthropicKey',
  fallbacks: [{ model: 'gpt-4o', auth: 'openaiKey' }],

  instructionsFile: './support-agent.md',

  tools: [lookupOrder, issueRefund],
  agents: ['shipping-agent'],

  memory: {
    scope: 'user',
    maxSessionMessages: 100,
    ttl: 60 * 60 * 24 * 7,
  },
  limits: {
    maxIterations: 15,
    maxToolCalls: 30,
  },
});
```

| Field | Required | Default | What it does |
|---|---|---|---|
| `name` | Yes | | Kebab case, starting with a letter, up to 64 characters. What `ctx.agent(name)` and `helix agent chat <name>` use |
| `description` | Yes | | One or two sentences. Shown by `helix agent list`, and read by other agents that delegate to this one |
| `model` | Yes | | A model id for the provider behind `auth`, the same string you would pass to `ctx.ai(auth)` |
| `auth` | Yes | | An authentication alias with an `aiProviders` entry in `helix.config.ts` |
| `fallbacks` | No | none | Models to try, in order, when the primary throws |
| `instructions` | One of the two | | The system prompt, inline |
| `instructionsFile` | One of the two | | Path to a text or Markdown file, relative to the agent file |
| `tools` | No | none | `defineTool` definitions the model may call |
| `agents` | No | none | Names of other agents this one may delegate to |
| `memory.scope` | No | `'project'` | `'project'` shares sessions across the project; `'user'` keys them to the signed-in user |
| `memory.maxSessionMessages` | No | 200 | Most recent messages the model is sent each turn. Does not limit what is stored |
| `memory.ttl` | No | none | Seconds of inactivity before a session expires. Omit it and sessions are kept |
| `limits.maxIterations` | No | 25 | Model steps per call |
| `limits.maxToolCalls` | No | 50 | Tool executions per call, across all steps |

The definition is validated when the file loads. A missing field, a badly formed name, two tools with the same name, or both `instructions` and `instructionsFile` set at once throws an error that names the field, so a broken agent fails at `helix dev` startup rather than on first use.

## Model, auth, and fallbacks

`model` and `auth` together are one `ctx.ai(auth)` model: the alias names the provider, the id names the model. Declare the alias in `helix.config.ts` under both `authentications` and `aiProviders` exactly as you would for a direct `ctx.ai` call. See [calling AI models](/documentation/guides/ai/).

`fallbacks` is an ordered list of further model and alias pairs, as in the complete definition above. When the primary model throws, the runtime retries the same step on the next entry, then the next. A fallback is never used after the caller aborts the request. Because any step of a run may be answered by a fallback, `result.helix.model` names the model that produced the final answer and `result.helix.cost` prices each step at the rate of the model that answered it. The cost covers this agent's own model calls. A run it delegates to another agent is priced on that agent's own result, not added here.

## Instructions

Short instructions fit inline:

```typescript
instructions: 'You are a billing assistant. Answer only billing questions. Use lookup_invoice before quoting any amount.',
```

Longer prompts go in a file next to the agent so you can edit them without touching code. The path is relative to the agent file:

```typescript
instructionsFile: './support-agent.md',
```

```markdown
<!-- functions/_agents/support-agent.md -->
You are the support assistant for Acme.

Rules:
- Always call lookup_order before discussing an order.
- Never promise a refund; call issue_refund, which may require approval.
- If you cannot resolve the issue, say so and offer to open a ticket.
```

Exactly one of `instructions` and `instructionsFile` must be set. The file is read when the agent is first used in `helix dev`, and bundled into the deployment at build time. The path is relative to the agent file and must point inside the project. The file can be up to 256 KB.

## Tools and delegates

`tools` takes `defineTool` definitions, covered in [defining tools](/documentation/guides/agent-tools/). `agents` takes the names of other agents. Each one becomes a tool called `agent_<name>` (hyphens become underscores) whose description is the target agent's `description`, so write descriptions that tell a calling agent when to hand off. Delegation patterns, depth limits, and what a delegate can and cannot do are in [agent architecture and patterns](/documentation/guides/agent-patterns/).

References under `agents` are checked the first time the agent runs, not when the file loads, because two agents may legitimately refer to each other.

## Memory

`memory` configures the session every conversation gets. `scope: 'user'` makes sessions private to the signed-in user who invoked the function, and lets you call `agent.sessions()` to list that user's conversations. The default `'project'` scope shares sessions across the project, which suits a schedule or a back-office function. The message window and retention are in [agent sessions](/documentation/guides/agent-sessions/).

## Limits

A call stops when the model gives a final answer, or when either limit is reached. `maxIterations` counts model steps; `maxToolCalls` counts tool executions across the whole call. When a limit stops the run, `result.text` is whatever the model had produced by then and `result.finishReason` reports why it stopped. `result.status` is still `'completed'`: status only tells you whether the run is waiting on an approval, so check `finishReason` when you need to know the answer is finished. Override both per call with `stopWhen`:

```typescript
import { stepCountIs, totalToolCallsIs } from '@trayai/helix-sdk';

const result = await ctx.agent('support-agent').generate({
  prompt: ctx.input.question,
  stopWhen: [stepCountIs(5), totalToolCallsIs(10)],
});
```

## Running an agent

`ctx.agent(name)` returns a client with `generate` and `stream`. Both take a `prompt` or a `messages` array, an optional `sessionId`, and per-call overrides:

```typescript
const agent = ctx.agent('support-agent');

const result = await agent.generate({ prompt: 'Where is order 1042?' });
result.text;              // the final answer
result.sessionId;         // pass back to continue the conversation
result.status;            // 'completed' | 'awaiting-approval'
result.steps.length;      // model steps taken
result.helix.cost.amount; // estimated USD for the whole call
result.helix.model;       // model that produced the final answer
```

`stream` resolves to the AI SDK stream result with `sessionId` added. Prior history is loaded before the stream starts, which is why it returns a promise. The turn is saved to the session when the stream finishes. If that save fails, or the stream is aborted, the turn is not saved and nothing is thrown, because your function has already returned the stream:

```typescript
const stream = await agent.stream({ prompt: ctx.input.question, sessionId: ctx.input.sessionId });
return stream.toUIMessageStreamResponse();
```

Per-call options: `activeTools` narrows the tool list for one call, `toolChoice` forces or forbids tool use, `prepareStep` adjusts settings before each step, and `providerOptions` passes provider-specific settings through. All are the AI SDK's own options. Full shapes are in the [`ctx.agent()` reference](/documentation/reference/context/agent/).

## What you see in the logs

Every call opens a span named `agent.generate` or `agent.stream` carrying the agent name, session id, step count, and outcome. Tool calls nest under it. In `helix dev` with pretty logs that renders as:

```
AGENT support-agent
  TOOL lookup_order  success  212ms
  TOOL issue_refund  success  380ms
AGENT support-agent → completed  3 steps  4520ms
```

The closing line is yellow when the run paused for approval and red when it failed. In production, the same spans appear in the execution's trace on the project's [Logs tab](/documentation/governance/dashboard/#logs). Use the **Uses AI** filter to find them. See [observability](/documentation/guides/observability/).

Full option list with types: [agent definition reference](/documentation/reference/agents/).

---

Canonical: https://helix.tray.ai/documentation/guides/defining-agents/
Any link on this page is available as markdown by appending .md to its URL.
Full corpus: https://helix.tray.ai/documentation/llms-full.txt