Home / Docs / Agent Harness

Agent Harness

Build agents that finish multi-step work

A normal chat agent owns one turn: the model calls tools until it decides to answer. The FabrCore Agent Harness adds a typed work list, an outer iteration loop, optional planning mode, background delegation, and durable session snapshots. Use it when an agent must keep working until a bounded plan is complete.

Choose the right shape

Use CreateChatClientAgent for ordinary one-turn chat. Use CreateFabrCoreHarnessAgent when one agent owns a durable plan across turns. Use a Surface task squad when a blueprint-defined coordinator should complete one multi-agent run inside a single turn.

Minimal Harness agent

CreateFabrCoreHarnessAgent is the agent-side entry point. It resolves the tracked chat client, Orleans-backed history, context compaction, configured tools, and the persisted Harness session.

ResearcherAgent.cs
using FabrCore.Core;
using FabrCore.Sdk;

[AgentAlias("researcher")]
[Description("Researches a goal end to end.")]
public sealed class ResearcherAgent : FabrCoreAgentProxy
{
    private FabrCoreHarnessResult harness = null!;

    public ResearcherAgent(
        AgentConfiguration config,
        IServiceProvider services,
        IFabrCoreAgentHost host) : base(config, services, host) { }

    public override async Task OnInitialize()
    {
        var tools = await ResolveConfiguredToolsAsync();
        harness = await CreateFabrCoreHarnessAgent(
            config.Models ?? "default",
            threadId: "main",
            tools);
    }

    public override async Task<AgentMessage> OnMessage(AgentMessage message)
    {
        var run = await harness.RunAsync(message);
        var response = message.Response();
        response.Message = run.Text;
        return response;
    }
}
Always call FabrCoreHarnessResult.RunAsync.

Calling the inner Agent.RunAsync bypasses the snapshot wrapper and silently disables durable Harness state.

What the Harness composes

CapabilityWhat it gives the modelWhy it matters
Todostodos_add, todos_complete, todos_get_remaining, and related toolsThe plan becomes typed state that the host can inspect.
Modesmode_get and mode_setPlanning can stop for review before execution begins.
LoopInvisible host behaviorThe agent is reinvoked when bounded work is still outstanding.
Background agentsbackground_agents_* toolsThe model can fan work out and collect monitored, ACL-governed results.
CompactionNo extra toolEvery long tool loop uses the context compaction ladder.

Plan and execute modes

Modes are enabled by default. Pass the full AgentMessage to RunAsync so the Harness can read message.Args["_plan-mode"].

ValueStarting mode
Missing, invalid, or "true"plan by default
"false"execute

MissingPlanModeBehavior can preserve the current mode or choose execution when the flag is omitted. An explicit valid value always wins.

Bounded completion loops

HarnessLoopMode is a flags enum. Evaluators run in flag order; the first evaluator that finds unfinished work continues the run.

ModeContinues whileCost
TodoIncomplete todos remain in execution modeNo extra judge call
BackgroundDelegations are still runningDelegate execution
MarkerA configured completion marker is absentNo extra judge call
JudgeA judge model decides the request is not completeOne extra LLM call per evaluation

The default iteration cap is 10. It is a budget, not a promise of completion. Read GetRemainingTodosAsync() and tell the caller what did not finish.

Background delegation

External delegates are FabrCore agents with real handles. They are health-probed, represented with registry descriptions and capabilities, called through FabrCore messaging, and governed by normal ACL rules.

{
  "args": {
    "_HarnessBackgroundAgents": "eric:crm,eric:policy-desk",
    "_HarnessBackgroundTimeoutSeconds": "180",
    "_HarnessLoop": "todo,background",
    "_HarnessLoopMaxIterations": "8"
  }
}

Private in-process specialists are a second topology. Create them with CreateInternalAgentAsync and add InternalAgentResult.AsBackgroundAgent(). They receive isolated tracked model clients, timeout and concurrency bounds, child attribution, and fail-closed tool scopes. Only explicitly classified read/compute tools are allowed in concurrent background policies.

Durability model

  • Todos, mode, delegation records, and loop position are stored under _harness_session:{threadId}.
  • Conversation history remains in Orleans MessageThreads; it is not duplicated into the Harness snapshot.
  • Snapshots are restored during activation and written after every wrapped run, including exceptional exits.
  • In-flight background work cannot be resumed after deactivation. It is marked Lost and must be reported or deliberately retried.
  • Set _HarnessSessionPersistence=false only for high-frequency agents that do not need cross-turn Harness state.

Blueprint configuration

ArgumentPurpose
_HarnessModeEnable plan/execute modes; defaults to true.
_HarnessLoopComma-separated loop modes such as todo,background.
_HarnessLoopMaxIterationsMaximum outer-loop reinvocations.
_HarnessBackgroundAgentsComma-separated qualified delegate handles.
_HarnessBackgroundTimeoutSecondsTimeout for each delegated request.
_HarnessSkillsExact versioned Harness Skill references.

Production checklist

  • Set ContextWindowTokens and MaxOutputTokens so long runs have an in-call bound.
  • Keep a finite iteration cap and delegation timeout.
  • Describe delegates for the orchestrating model, including what they should not do.
  • Report remaining todos and lost delegations honestly.
  • Use durable Orleans storage when Harness state must survive process restarts.
  • Remember that upstream Harness APIs remain experimental.
Documentation