Skip to content

Budgets & Timeouts ​

Every agent has a budget. defineAgent() fills in the defaults, and your values are merged over them.

FieldDefaultKindEffect
maxSteps20stop conditionModel calls per run, across all executions
maxTokensnonestop conditionInput plus output tokens per run, across all executions
timeoutMs900_000 (15 min)abort signalWall-clock limit per execution
ts
const reporter = defineAgent({
    name: 'weekly-report',
    model,
    instructions: 'Summarize the open work items in the trigger payload.',
    triggers: [
        manual(),
        cron('0 8 * * 1', { timezone: 'Europe/Berlin' }),
    ],
    budget: {
        maxSteps: 10,
        maxTokens: 50_000,
        timeoutMs: 5 * 60 * 1000,
    },
    tools: ({ run, state }) => ({
        lastReport: tool({
            description: 'Returns the date of the previous report.',
            inputSchema: z.object({}),
            execute: async () => (await state.get<string>('lastReport')) ?? 'never',
        }),
        saveReport: tool({
            description: 'Remembers that a report was written now.',
            inputSchema: z.object({}),
            execute: async () => {
                await state.set('lastReport', run.trigger.receivedAt.toISOString());
                return { saved: true };
            },
        }),
    }),
});

How a budget settles a run ​

SituationStatuserror.code
The step or token limit is reached and the last step asked for tool callsfailedBudgetExceeded
The step or token limit is reached and the last step was a final answercompleted
The execution ran longer than timeoutMsfailedTimeout

The step budget counts checkpointed steps, so a run resumed after a crash or an approval continues with what is left. The token budget adds the usage of earlier executions (run.usage) to the current one.

The timeout restarts with every execution. A run that waits two days for an approval is not timed out by that wait; only the time spent executing counts, per execution. The timeout aborts the run.signal your tools receive, so pass it on to long-running work.

One step beyond the limit after an approval ​

A resumed approval gets exactly one step beyond maxSteps. An approval asked for on the last allowed step is therefore still answered: the approved call runs (or the denial is recorded) and the model gets one more call to react.

Token usage ​

run.usage holds { inputTokens, outputTokens } summed over every step of the run. Each llm step stores its own usage in output.usage. A provider that reports no usage counts as zero.