# AgentRunBudget

> Tracon documentation. Tracon is a .NET package family that adds a control
> plane on Microsoft Agent Framework, published as NuGet packages that run
> inside your own ASP.NET Core application.
>
> Page: https://tracon.dev/api/tracon.agentrunbudget/
> Summary: AgentRunBudget: A run budget shared across an entire call tree.

<div class="api-provenance">Namespace Tracon · Assembly <code>Tracon.Abstractions.dll</code></div>

A run budget shared across an entire call tree.

```csharp
public sealed class AgentRunBudget
```

<dl class="api-relations"><dt>Inheritance</dt><dd><a href="https://learn.microsoft.com/dotnet/api/system.object">object</a> ← <a href="/api/tracon.agentrunbudget/">AgentRunBudget</a></dd><dt>Inherited Members</dt><dd><a href="https://learn.microsoft.com/dotnet/api/system.object.gettype">object.GetType()</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.tostring">object.ToString()</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object)">object.Equals(object?)</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object)">object.Equals(object?, object?)</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.referenceequals">object.ReferenceEquals(object?, object?)</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.gethashcode">object.GetHashCode()</a></dd></dl>

## Remarks

**This type is deliberately a `class`, not a `record`.**
The budget is shared mutable state: every run in the tree uses the
*same* instance. A `record` invites copying; a copied budget
would give each branch its own limit, and the limit would lose its meaning.

Counters increment lock-free (`Threading.Interlocked`). Child runs start
concurrently: Microsoft Agent Framework's background agents run without
blocking, so the same budget is read and written from multiple threads.

[AgentRunBudget.TryReserveRun](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_TryReserveRun) (the count and depth dimensions) only blocks a
**new** child run from starting; it never interrupts a child
run already in progress — cutting one off midway would leave the model
with an incomplete context and would also corrupt the root run.

The token, cost, and duration dimensions are enforced the same way, by a
decorator installed inside the tool-call loop (`RunBudgetChatClient`,
internal to `Tracon.Core`): once [AgentRunBudget.IsExhausted](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_IsExhausted) is
[true](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool), that decorator refuses the tree's **next**
model call, so a long-running tool loop is cut off mid-run rather than
only at its next child call. The cutoff always lands between two model
turns, never inside one — the decorator only ever refuses a call it has
not yet made. A tool that itself runs long is **not** interrupted;
the cutoff waits for that tool call to finish and only then refuses the
model call that would follow it.

A workflow function node is gated at its **start**, on two of the
three: once [AgentRunBudget.IsCostBudgetExhausted](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_IsCostBudgetExhausted) or [AgentRunBudget.IsDurationBudgetExhausted](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_IsDurationBudgetExhausted)
is [true](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool) the node does not begin and the run ends
`Failed` with [TraconRunBudgetExceededException](/api/tracon.traconrunbudgetexceededexception/). The token
dimension does not stop a node: it is on by default and a function node spends
no tokens. A node that is already running is never stopped, so the tree can
pass a ceiling by as much as its last node spends; the ceiling is not absolute.

A tool call is gated at its **start** on all three: once [AgentRunBudget.IsExhausted](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_IsExhausted)
is [true](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool) it does not begin. The model's next call is refused for the same
condition, so no model reads the result of a tool call that starts now. The result of
a refused call is a sentence that says the call did not start; a streaming run records
the call as failed, and the run ends at its next model call as it always did. A tool that
has started is never stopped, and tool calls that start together (concurrent tool calls)
all start before any of them reports.

The budget of a workflow run that continues earlier runs starts from what they spent
([AgentRunBudget.ConsumedTokens](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_ConsumedTokens), [AgentRunBudget.ConsumedCost](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_ConsumedCost), [AgentRunBudget.StartedRuns](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_StartedRuns)) and has
a [AgentRunBudget.Deadline](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_Deadline) that is as far behind a fresh one as the earlier runs were executing:
the ceiling is the logical run's, and the time a workflow waited for a person is not counted.

## Constructors

### <a id="Tracon_AgentRunBudget__ctor_System_Nullable_System_TimeSpan__System_TimeProvider_"></a> AgentRunBudget\(TimeSpan?, TimeProvider?\)

Creates a new run budget.

```csharp
public AgentRunBudget(TimeSpan? maxDuration = null, TimeProvider? timeProvider = null)
```

#### Parameters

`maxDuration` [TimeSpan](https://learn.microsoft.com/dotnet/api/system.timespan)?

The wall-clock time the whole tree may take, taken at face value: unlike
the four [init](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/init) dimensions below, [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null)
is the only value that means "no limit" — [TimeSpan.Zero](https://learn.microsoft.com/dotnet/api/system.timespan) or
a negative value produces a [AgentRunBudget.Deadline](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_Deadline) that has already
passed, the same way a raw `MaxTotalTokens = 0` reads as
"exhausted from the start" rather than "unlimited". The "zero/negative
means unlimited" convention is applied one layer up, in
`TraconAgentGraphOptions.CreateBudget` (`Tracon.Core`).

`timeProvider` [TimeProvider](https://learn.microsoft.com/dotnet/api/system.timeprovider)?

The time source [AgentRunBudget.Deadline](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_Deadline) is computed from and every later
expiry check reads. Defaults to [TimeProvider.System](https://learn.microsoft.com/dotnet/api/system.timeprovider).

## Properties

### <a id="Tracon_AgentRunBudget_ConsumedCost"></a> ConsumedCost

The amount spent across the tree so far.

```csharp
public decimal ConsumedCost { get; }
```

#### Property Value

 [decimal](https://learn.microsoft.com/dotnet/api/system.decimal)

### <a id="Tracon_AgentRunBudget_ConsumedTokens"></a> ConsumedTokens

The tokens spent across the tree so far.

```csharp
public long ConsumedTokens { get; }
```

#### Property Value

 [long](https://learn.microsoft.com/dotnet/api/system.int64)

### <a id="Tracon_AgentRunBudget_Deadline"></a> Deadline

The instant the tree's time budget runs out. Computed once, when this
budget was constructed; every run in the tree shares the same value.
[null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null) when [AgentRunBudget.MaxDuration](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_MaxDuration) is [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null).
For a run that continues an earlier one it is earlier than a fresh budget's
by the time the earlier runs were executing.

```csharp
public DateTimeOffset? Deadline { get; }
```

#### Property Value

 [DateTimeOffset](https://learn.microsoft.com/dotnet/api/system.datetimeoffset)?

### <a id="Tracon_AgentRunBudget_IsCostBudgetExhausted"></a> IsCostBudgetExhausted

Whether the cost limit has been exceeded.

```csharp
public bool IsCostBudgetExhausted { get; }
```

#### Property Value

 [bool](https://learn.microsoft.com/dotnet/api/system.boolean)

### <a id="Tracon_AgentRunBudget_IsDurationBudgetExhausted"></a> IsDurationBudgetExhausted

Whether the tree's [AgentRunBudget.Deadline](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_Deadline) has passed.

```csharp
public bool IsDurationBudgetExhausted { get; }
```

#### Property Value

 [bool](https://learn.microsoft.com/dotnet/api/system.boolean)

### <a id="Tracon_AgentRunBudget_IsExhausted"></a> IsExhausted

Whether the tree has spent past its token, cost, or time limit. Does
**not** reflect [AgentRunBudget.MaxTotalRuns](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_MaxTotalRuns): the run-count
limit only blocks starting a *new* child run
([AgentRunBudget.TryReserveRun](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_TryReserveRun)), it says nothing about whether the
current run may keep calling its model.

```csharp
public bool IsExhausted { get; }
```

#### Property Value

 [bool](https://learn.microsoft.com/dotnet/api/system.boolean)

### <a id="Tracon_AgentRunBudget_IsTokenBudgetExhausted"></a> IsTokenBudgetExhausted

Whether the token limit has been exceeded.

```csharp
public bool IsTokenBudgetExhausted { get; }
```

#### Property Value

 [bool](https://learn.microsoft.com/dotnet/api/system.boolean)

### <a id="Tracon_AgentRunBudget_MaxDepth"></a> MaxDepth

The maximum allowed call depth. The root run is 0, so the default
value allows a three-layer tree.

```csharp
public int MaxDepth { get; init; }
```

#### Property Value

 [int](https://learn.microsoft.com/dotnet/api/system.int32)

### <a id="Tracon_AgentRunBudget_MaxDuration"></a> MaxDuration

The wall-clock time the whole tree may take, counted from when this
budget was constructed. If [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null), there is no time limit.

```csharp
public TimeSpan? MaxDuration { get; }
```

#### Property Value

 [TimeSpan](https://learn.microsoft.com/dotnet/api/system.timespan)?

#### Remarks

A workflow run that continues an earlier one (a person's answer, an operator's resume,
a recovery after an interruption) counts the time the earlier runs were executing too,
and not the time the workflow waited for a person: the limit is the logical run's.

### <a id="Tracon_AgentRunBudget_MaxTotalCost"></a> MaxTotalCost

The maximum amount spendable across the tree. If [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null),
there is no cost limit.

```csharp
public decimal? MaxTotalCost { get; init; }
```

#### Property Value

 [decimal](https://learn.microsoft.com/dotnet/api/system.decimal)?

#### Remarks

Cannot be enforced when a model call's pricing is undefined
([PricingSource.Unknown](/api/tracon.pricingsource/)); the token limit applies instead,
the same fallback [QuotaDefinition.MaxCost](/api/tracon.quotadefinition/#Tracon_QuotaDefinition_MaxCost) uses.

### <a id="Tracon_AgentRunBudget_MaxTotalRuns"></a> MaxTotalRuns

The maximum number of *child* runs that may start. The root run
is not counted toward this number. If [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null), there is
no count limit.

```csharp
public int? MaxTotalRuns { get; init; }
```

#### Property Value

 [int](https://learn.microsoft.com/dotnet/api/system.int32)?

### <a id="Tracon_AgentRunBudget_MaxTotalTokens"></a> MaxTotalTokens

The maximum tokens spendable across the tree. If [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null),
there is no token limit.

```csharp
public long? MaxTotalTokens { get; init; }
```

#### Property Value

 [long](https://learn.microsoft.com/dotnet/api/system.int64)?

### <a id="Tracon_AgentRunBudget_StartedRuns"></a> StartedRuns

The number of child runs started so far.

```csharp
public int StartedRuns { get; }
```

#### Property Value

 [int](https://learn.microsoft.com/dotnet/api/system.int32)

## Methods

### <a id="Tracon_AgentRunBudget_DescribeExhaustion"></a> DescribeExhaustion\(\)

Produces a user-facing text describing the limit that was exceeded.

```csharp
public string DescribeExhaustion()
```

#### Returns

 [string](https://learn.microsoft.com/dotnet/api/system.string)

Text stating which limit was exceeded.

#### Remarks

For agent delegation the text is returned to the model as a tool result;
for a workflow step it is the message of the
[TraconRunBudgetExceededException](/api/tracon.traconrunbudgetexceededexception/) that fails the run. Saying
"budget exhausted" is not enough; unless the exceeded limit is named, the
user cannot see which setting to raise.

### <a id="Tracon_AgentRunBudget_DescribeModelCallExhaustion"></a> DescribeModelCallExhaustion\(\)

Produces a user-facing text describing the token, cost, or time limit
that cut a model call short mid-run.

```csharp
public string DescribeModelCallExhaustion()
```

#### Returns

 [string](https://learn.microsoft.com/dotnet/api/system.string)

#### Remarks

Only called once [AgentRunBudget.IsExhausted](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_IsExhausted) is [true](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool);
the run-count limit does not apply here (see [AgentRunBudget.IsExhausted](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_IsExhausted)).

### <a id="Tracon_AgentRunBudget_RecordUsage_System_Int64_"></a> RecordUsage\(long\)

Records the number of tokens spent into the budget.

```csharp
public void RecordUsage(long tokens)
```

#### Parameters

`tokens` [long](https://learn.microsoft.com/dotnet/api/system.int64)

The number of tokens to add. A negative value is ignored.

### <a id="Tracon_AgentRunBudget_RecordUsage_System_Int64_System_Nullable_System_Decimal__"></a> RecordUsage\(long, decimal?\)

Records the tokens and cost spent in one model turn into the budget.

```csharp
public void RecordUsage(long tokens, decimal? cost)
```

#### Parameters

`tokens` [long](https://learn.microsoft.com/dotnet/api/system.int64)

The number of tokens to add. A negative value is ignored.

`cost` [decimal](https://learn.microsoft.com/dotnet/api/system.decimal)?

The cost to add, or [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null) when the turn's price is
undefined ([PricingSource.Unknown](/api/tracon.pricingsource/)) — nothing is added to
[AgentRunBudget.ConsumedCost](/api/tracon.agentrunbudget/#Tracon_AgentRunBudget_ConsumedCost) in that case, so an installation with an
unpriced model never trips a cost limit it cannot actually measure.

### <a id="Tracon_AgentRunBudget_TryReserveRun"></a> TryReserveRun\(\)

Reserves budget room for a new child run.

```csharp
public bool TryReserveRun()
```

#### Returns

 [bool](https://learn.microsoft.com/dotnet/api/system.boolean)

[true](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool) if room was reserved; [false](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool) if
the token, cost, time, or count limit has been exceeded.

#### Remarks

The counter increments only when room is reserved. If a failed attempt
incremented the counter, every new attempt in a tree that has hit its
limit would keep growing the count, and the UI would show runs that
never actually started.
