# LoopSettings

> 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.loopsettings/
> Summary: LoopSettings: Turns the harness loop on: the agent is re-invoked until a stop criterion says the work is finished.

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

Turns the harness loop on: the agent is re-invoked until a stop criterion
says the work is finished.

```csharp
public sealed record LoopSettings : IEquatable<LoopSettings>
```

<dl class="api-relations"><dt>Inheritance</dt><dd><a href="https://learn.microsoft.com/dotnet/api/system.object">object</a> ← <a href="/api/tracon.loopsettings/">LoopSettings</a></dd><dt>Implements</dt><dd><a href="https://learn.microsoft.com/dotnet/api/system.iequatable-1">IEquatable&lt;LoopSettings&gt;</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>

## Examples

An agent that keeps working until it writes `ALL DONE`, for at most
five iterations:

```csharp
new AgentDefinition
{
    Name = "researcher",
    Model = new ModelBinding { Provider = "openai", Model = "gpt-5" },
    Harness = new HarnessSettings
    {
        Loop = new LoopSettings
        {
            Criteria = [new LoopCriterion { Kind = "completionMarker", Marker = "ALL DONE" }],
            MaxIterations = 5,
        },
    },
};
```

A reviewer loop that fails the run when its judge is still not convinced after
three drafts, and keeps the last draft when the judge itself cannot be reached:

```csharp
new LoopSettings
{
    Criteria = [new LoopCriterion { Kind = "aiJudge", JudgeCriteria = ["The draft is ready to publish"] }],
    MaxIterations = 3,
    OnExhausted = LoopOutcome.FailRun,
    OnCriterionError = LoopOutcome.ReturnLastResponse,
};
```

## Remarks

The loop is **off** while `HarnessSettings.Loop` is
[null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null), and the harness behaves exactly as it did before this
setting existed. Nothing turns it on implicitly.

**This is not `HarnessSettings.MaximumIterationsPerRequest`.**
The two numbers count different things and are deliberately kept in separate
types so they cannot be read as one:

<table><thead><tr><th class="term">Setting</th><th class="description">What it bounds</th></tr></thead><tbody><tr><td class="term">`HarnessSettings.MaximumIterationsPerRequest`</td><td class="description">
    The harness's *inner* tool-calling loop inside ONE agent
    invocation. It is a runaway-safety ceiling, not a stop criterion.
    </td></tr><tr><td class="term">`MaxIterations`</td><td class="description">
    The *outer* loop that re-invokes the whole agent after a criterion
    says the work is not finished. It bounds how many times that may happen.
    </td></tr></tbody></table>

Criteria are evaluated **in order, and the first one that asks for
another iteration wins** — the remaining criteria are not evaluated
that iteration. The loop therefore stops only when every criterion is
satisfied. Put the cheapest criterion first: an `aiJudge` placed after a
`completionMarker` costs nothing on the iterations the marker already
keeps going.

Every completed iteration writes a
`RunEventType.LoopIterationCompleted` event to the run, so the
loop is visible in the run record instead of hidden inside a caller's own
code.

**How the loop ends when no criterion is satisfied.** By default
the run returns the last response as a success, whether the ceiling was reached
or a criterion threw. [LoopSettings.OnExhausted](/api/tracon.loopsettings/#Tracon_LoopSettings_OnExhausted) and [LoopSettings.OnCriterionError](/api/tracon.loopsettings/#Tracon_LoopSettings_OnCriterionError)
can make either case fail the run instead, so a workflow does not hand a response
no criterion accepted to its next node.

## Constructors

### <a id="Tracon_LoopSettings__ctor"></a> LoopSettings\(\)

```csharp
public LoopSettings()
```

## Fields

### <a id="Tracon_LoopSettings_DefaultMaxIterations"></a> DefaultMaxIterations

The iteration ceiling applied when `MaxIterations` is
[null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null).

```csharp
public const int DefaultMaxIterations = 10
```

#### Field Value

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

The ceiling is never left open. A criterion that can never be satisfied —
a marker the model does not write, a judge that is never convinced — turns
an open loop into a bill the consumer only learns about from an invoice,
so Tracon applies its own ceiling instead of leaving the number to the
framework's default.

## Properties

### <a id="Tracon_LoopSettings_Criteria"></a> Criteria

Gets the stop criteria, evaluated in order. At least one is required.

```csharp
public required IReadOnlyList<LoopCriterion> Criteria { get; init; }
```

#### Property Value

 [IReadOnlyList](https://learn.microsoft.com/dotnet/api/system.collections.generic.ireadonlylist\-1)<[LoopCriterion](/api/tracon.loopcriterion/)\>

### <a id="Tracon_LoopSettings_FreshContextPerIteration"></a> FreshContextPerIteration

Gets a value that starts every iteration from a fresh context instead of
carrying the previous iterations' messages forward.

```csharp
public bool FreshContextPerIteration { get; init; }
```

#### Property Value

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

Off by default. Turning it on changes what the agent remembers between
iterations: the agent no longer sees its own earlier answers, only the
original request and the criterion's feedback. Use it when repeated
context is what makes the agent repeat itself; leave it off when the work
builds on the previous iteration.

### <a id="Tracon_LoopSettings_MaxIterations"></a> MaxIterations

Gets the upper number of loop iterations. [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null) takes
`DefaultMaxIterations`. Must be greater than zero.

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

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

Gets what happens when a criterion throws while it is evaluated.
[LoopOutcome.ReturnLastResponse](/api/tracon.loopoutcome/) by default.

```csharp
public LoopOutcome OnCriterionError { get; init; }
```

#### Property Value

 [LoopOutcome](/api/tracon.loopoutcome/)

#### Remarks

By default the loop stops and the last response is returned; the
`LoopIterationCompleted` event names the criterion in
`failedCriterion`. With [LoopOutcome.FailRun](/api/tracon.loopoutcome/) the run fails
with the criterion's own error - an unreachable judge provider stays a provider
error, not a rejected response - and the event also carries `failsRun`.

The run's own cancellation is never a criterion error: a cancelled run ends as
cancelled in both modes.

### <a id="Tracon_LoopSettings_OnExhausted"></a> OnExhausted

Gets what happens when the iteration ceiling is reached while a criterion still
asks for another iteration. [LoopOutcome.ReturnLastResponse](/api/tracon.loopoutcome/) by
default.

```csharp
public LoopOutcome OnExhausted { get; init; }
```

#### Property Value

 [LoopOutcome](/api/tracon.loopoutcome/)

#### Remarks

With [LoopOutcome.FailRun](/api/tracon.loopoutcome/) the run fails with error type
`loop_unsatisfied` ([TraconLoopUnsatisfiedException](/api/tracon.traconloopunsatisfiedexception/),
[RunErrorClass.LoopUnsatisfied](/api/tracon.runerrorclass/)), and the last
`LoopIterationCompleted` event carries `failsRun`.

**The last iteration is evaluated in this mode.** By default the
turn that reaches the ceiling is not evaluated, because nothing is left to
decide. Failing the run without evaluating it would refuse a last response that
meets every criterion, so with [LoopOutcome.FailRun](/api/tracon.loopoutcome/) the criteria
run once more after that turn - one more evaluation, which for an
`aiJudge` criterion is one more model call. The number of agent turns stays
[LoopSettings.MaxIterations](/api/tracon.loopsettings/#Tracon_LoopSettings_MaxIterations). With [LoopSettings.MaxIterations](/api/tracon.loopsettings/#Tracon_LoopSettings_MaxIterations) set to 1 the
single turn is evaluated too; by default it is not.

A streaming caller receives the last turn's text before the failure: the
decision is made after that turn has streamed.

## Methods

### <a id="Tracon_LoopSettings_Equals_System_Object_"></a> Equals\(object?\)

```csharp
public override bool Equals(object? obj)
```

#### Parameters

`obj` [object](https://learn.microsoft.com/dotnet/api/system.object)?

#### Returns

 [bool](https://learn.microsoft.com/dotnet/api/system.boolean)

### <a id="Tracon_LoopSettings_Equals_Tracon_LoopSettings_"></a> Equals\(LoopSettings?\)

```csharp
public bool Equals(LoopSettings? other)
```

#### Parameters

`other` [LoopSettings](/api/tracon.loopsettings/)?

#### Returns

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

```csharp
public override int GetHashCode()
```

#### Returns

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

```csharp
public override string ToString()
```

#### Returns

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

### <a id="Tracon_LoopSettings_op_Equality_Tracon_LoopSettings_Tracon_LoopSettings_"></a> operator ==\(LoopSettings?, LoopSettings?\)

```csharp
public static bool operator ==(LoopSettings? left, LoopSettings? right)
```

#### Parameters

`left` [LoopSettings](/api/tracon.loopsettings/)?

`right` [LoopSettings](/api/tracon.loopsettings/)?

#### Returns

 [bool](https://learn.microsoft.com/dotnet/api/system.boolean)

### <a id="Tracon_LoopSettings_op_Inequality_Tracon_LoopSettings_Tracon_LoopSettings_"></a> operator \!=\(LoopSettings?, LoopSettings?\)

```csharp
public static bool operator !=(LoopSettings? left, LoopSettings? right)
```

#### Parameters

`left` [LoopSettings](/api/tracon.loopsettings/)?

`right` [LoopSettings](/api/tracon.loopsettings/)?

#### Returns

 [bool](https://learn.microsoft.com/dotnet/api/system.boolean)
