Skip to main content

Harness Agents

A harness agent is an autonomous, software-engineering-oriented agent built on the Microsoft Agent Framework harness (create_harness_agent()): a tool-calling loop with a persistent todo list, plan/execute mode tracking, file-based session memory, jailed file access, Agent Skills, shell execution, hosted web search, context compaction, and a keep-going-until-the-todos-are-done loop -- all assembled behind one factory call.

Harness agents are the third declarative kind, next to Declarative Agents (kind: Prompt) and Declarative Workflows (kind: Workflow): a kind: Harness YAML in the same DECLARATIVE_AGENTS_DIR folder. The rule stays the same: the YAML is a specification, and ChatWalaʻau owns every input -- the model client, credentials, tools, workspace directories, and skills are always resolved by ChatWalaʻau, never taken from the file.

What you get

  • An agent that works a task list. The harness plans, tracks todos, switches between plan and execute modes, and -- while executing -- re-invokes itself until the todos are done (bounded at 10 iterations). A task indicator in the answer shows how far it got (v0.162.0).

  • A real workspace. With CODING_WORKSPACE_DIR set, the agent gets jailed file-access tools and a shell scoped to your workspace, plus file-based session memory (agent-file-memory/) for notes that persist across turns. Without the directory, those capabilities simply don't exist.

  • No approval step. File writes, shell commands and skill scripts run as soon as the agent calls them (v0.160.0). What the agent can touch is decided by what it has: without CODING_WORKSPACE_DIR it has no shell or file tools, and File write tools off makes it read-only.

  • Composable in a GUI. Create and edit harness agents in the same management modal as your other agents (they carry a HARNESS tag), with a full-screen editor: form panels for instructions and the building-block switches, a canvas showing the model, tools, and enabled blocks, and a live canonical YAML preview.

  • Run in chat as a run-target. Activate a harness agent in the modal and your next message runs it -- the composer shows Harness: <name>, because the YAML fixes the model and the reasoning effort. Selecting a harness agent and a workflow are mutually exclusive; activating a regular agent clears both.

  • Set its reasoning effort in the detail screen, or in the YAML:

    model:
    id: gpt-6-astra
    options:
    effort: medium # low | medium | high | xhigh | max; default xhigh

    Before v0.165.0 a harness ran at the provider's bare default and sent no reasoning configuration at all.

A minimal harness agent

kind: Harness
name: repo-fixer
displayName: Repo Fixer
model:
id: gpt-5.3 # exactly ONE catalog offering
instructions:
agent: |
Focus on this repository. Prefer small, verifiable changes.
tools:
- function:weather_get_current # optional: built-in tools / whole MCP servers
- skill:pptx # optional: narrow to specific skills (v0.166.0)
mode:
initial: execute # plan | execute
planApproval: skip # skip (default) | ask
loop:
maxIterations: 10 # capped at 10

Drop it into DECLARATIVE_AGENTS_DIR (or use the GUI) and it appears in the management modal. Anything the file gets wrong -- an unknown model, an unknown tool, an out-of-range budget -- is a blocking warning: the agent is listed, but cannot be selected until you fix it.

What ChatWalaʻau decides for you

ConcernPolicy
Chat historyIn-memory per conversation (reset on restart)
Todo list / loopAlways on; loop runs while todos remain in execute mode, max 10 iterations (v0.162.0)
File / shell toolsOnly under CODING_WORKSPACE_DIR; no approval step (v0.160.0)
SkillsLoaded from SKILLS_DIR, through the same loader chat uses -- your Skills modal selection, script execution and script types all apply (v0.141.0). Since v0.166.0 you can also narrow this agent to specific skills -- pick them from Add tools -> Skills on the editor canvas, or write skill:<name> entries in tools:. Naming none inherits every enabled skill, as before
Web searchOn by default, but obeys the per-model capability gate
Context compactionOn by default, sized from the offering's context_window unless the YAML narrows it
Token budgetsFrom the offering's context_window unless the YAML narrows them
Credentials / providerAlways ChatWalaʻau's -- never read from YAML

Harness agents are SPA-only for now: the OpenAI-compatible API and Teams keep following the active regular agent. In demo mode, harness agents are visible but read-only.

Plan approval

With Plan / Execute mode on, a harness agent starts by planning: it analyses the request, writes its todo list, and asks you any clarifying questions it needs. What happens next is your choice (v0.162.0):

Plan approvalAfter planning, the agent
Skip (default)Presents the plan briefly and switches to execute mode by itself
AskAsks for your approval and waits; reply "yes" (or ask for changes) to continue

Set it with Plan approval under "Plan / Execute mode" in the editor, or in YAML:

mode:
planApproval: ask # leave it out for skip

Clarifying questions are asked with either setting -- skip only drops the final "may I start?" question. The editor writes planApproval only when you choose Ask, so an agent saved with the default still loads on earlier versions.

note

Before v0.162.0 the agent always asked for approval, and the automatic loop then answered the question for you with "Continue working on the task" because todos were open. The loop now runs only in execute mode, so a question the agent asks while planning always waits for you.

Following a run: the task indicator

When a harness turn works through a todo list, a compact indicator appears inside the answer, above the text (v0.162.0):

[list] Tasks 3/7 done [loop] auto-continue 2/10 execute [Tasks]
PartMeaning
Tasks n/m doneCompleted todos out of all todos
auto-continue i/NWhich round of automatic continuation the agent is on, out of loop.maxIterations
plan / executeThe agent's current mode
Last line (after the turn)Why it ended: all tasks done, waiting for your answer, stopped at the round limit with tasks open, or stopped

Tasks opens the full list. While the agent works the list updates live; for a finished turn it shows the tasks as they were at the end of that turn. The indicator is saved with the message, so it is still there when you reopen the chat.

The loop's internal messages to the model -- Progress so far: ... and Continue working on the task. If it is complete, say so. -- are not part of the answer; the indicator is where that information lives. A plain follow-up question does not show an old, finished list.

How long a run can go

Each message you send is one run of the agent. Within that run the harness keeps going while todos remain and it is in execute mode, up to 10 loop iterations (loop.maxIterations, capped at 10), and each iteration can make up to 40 model round-trips of tool calls. That is the whole budget: nothing else stops a run, and nothing extends it.

A run that reaches the cap with todos still open ends normally, like any other answer. Send another message -- "continue" is enough -- and the agent picks the todo list up where it left off, because the conversation keeps its history.

note

Before v0.160.0 every write and shell command paused for an approval, and each pause started a fresh run, so a long task was bounded by approval-round settings (AUTONOMOUS_LOOP_NO_PROGRESS_ROUNDS, AUTONOMOUS_LOOP_MAX_ROUNDS). With the approval step gone those settings no longer exist; if they are still in your .env, the server warns about them at startup and ignores them.

Reading the tool activity

A harness agent works by calling a lot of small tools, and every call appears under the assistant's message. They read as what they do:

You seeThe agent did
Listed workspace files / Read workspace file / Wrote workspace fileUsed its jailed file access under CODING_WORKSPACE_DIR. "Read workspace file" also covers reading just a range of lines (v0.161.0)
Searched workspace contentGrepped the workspace
Saved to agent memory / Read agent memoryUsed its own file memory store (agent-file-memory)
Added a task / Completed a task / Checked remaining tasksUpdated its todo list — this is what drives the keep-going loop
Switched agent mode / Checked agent modeMoved between plan and execute
Ran commandRan a shell command in the workspace. The output is plain text -- colour codes are removed (v0.162.0)
Ran skill scriptExecuted a script belonging to an Agent Skill (needs CODING_ENABLED=true)
Ran skill script with a name that does not exist -- fixed in v0.162.0

Earlier versions could show Ran skill script with an invented name -- script_name: "noop" or skill_name: "none" -- answered by a "not found" error and repeated several times in a turn. The framework offered the script tool even when no skill had any scripts, so the model invented one. Since v0.162.0 the tool is offered only when one of your visible skills actually has a script. Skills with scripts work exactly as before.

note

Before v0.134.0 all of these were shown as MCP: <name> — a mislabel, not a different behaviour. None of them is an MCP call. MCP: now appears only for tools that genuinely come from a configured MCP server.

Editing a message starts the agent over

A harness agent keeps working state for the life of the conversation: its history, its todo list, and its current mode.

Editing or deleting a message resets all of it. So does deleting the conversation. That is what "go back and try again" means here -- the agent starts from the conversation as you have just left it, with nothing carried over.

You do thisThe agent
Edit an earlier messageStarts over: history, todos and mode are cleared
Delete a messageSame
Delete the conversationSame, and its shell process is released immediately
Send a normal follow-upKeeps everything, as usual
note

Before v0.137.0 only the visible messages were removed and the agent kept its memory of every earlier run. If a run had been interrupted part-way, the leftovers could make a later request invalid and the model provider would reject it with No tool output found for function call .... Rewinding now clears them.

There is no partial reset. The agent's internal messages do not line up one-to-one with the messages you see, so there is no point to rewind to.

If you want to keep a long-running todo list, finish or re-state it before editing an earlier message -- or branch the conversation instead of rewinding it.

Context compaction

A harness agent keeps its conversation in memory for as long as the chat is open, and a tool-heavy session can grow quickly. Compaction keeps that under control automatically, in two stages measured against the model's input budget:

AtWhat happens
50%Older tool results are collapsed into summaries; the most recent ones stay verbatim
80%The oldest exchanges are dropped

Since v0.161.0 the summaries also record the steps the agent took -- which tools it called, with what, and what came back -- not only what it said, so a long run keeps track of work it has already done. The summaries are a little larger as a result.

You do not need to configure anything. The budget comes from the model offering's context_window, and the output allowance defaults to 32,768 tokens (automatically reduced for smaller models). Override either one only if you have a reason to:

compaction:
maxContextWindowTokens: 200000 # default: the offering's context_window
maxOutputTokens: 16384 # default: 32768, capped at 1/8 of the window

Leaving a value out means "use the default" -- it does not turn compaction off. To actually turn it off:

compaction:
disabled: true

The agent's detail panel in the management modal shows the budget a run would really use, so you can confirm it rather than infer it.

note

Before v0.133.0, omitting maxOutputTokens silently disabled compaction while every surface still reported it as on. If you created a harness agent on an earlier version, it is fixed automatically -- no change to your YAML is needed.

note

Before v0.138.0, a long conversation could fail during a turn, a few tool calls in, with a provider error like No tool call found for shell call output with call_id .... The agent's internal history had accumulated repeated copies of the same tool call, which stopped compaction from keeping a tool call and its result together -- so one of the two was collapsed away and the request became invalid. The history is now kept free of those repeats, and nothing you type is affected: sending the same message twice still records it twice.