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_DIRset, the agent gets jailed file-access tools and a shell that both start in the conversation's project folder (the workspace root unless you pick one), plus file-based session memory (agent-file-memory/) for notes that persist across turns. Without the directory, those capabilities simply don't exist. -
Project rules. With
projectRules: {enabled: true}the agent follows the project folder'sAGENTS.mdandCLAUDE.md, the way coding agents do (v0.208.0). -
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_DIRit 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
HARNESStag), 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-astraoptions:effort: medium # low | medium | high | xhigh | max; default xhighBefore 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
| Concern | Policy |
|---|---|
| Chat history | Per conversation, saved after every turn and restored after a restart (v0.207.0, see below) |
| Todo list / loop | Always on; loop runs while todos remain in execute mode, max 10 iterations (v0.162.0) |
| File / shell tools | Only under CODING_WORKSPACE_DIR; both start in the conversation's project folder (v0.208.0); no approval step (v0.160.0) |
| Project rules | Off unless the YAML sets projectRules: {enabled: true} or the editor's Project rules (AGENTS.md / CLAUDE.md) switch is on (v0.208.0, see below) |
| Skills | Loaded 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 search | On by default, but obeys the per-model capability gate |
| Computer Use | Available when you add the Computer Use tools to the agent (Windows, local backend only); the same per-model gate and screenshot handling as chat (v0.170.0) |
| CodeAct | The compute sandbox execute_code, while App Settings > CodeAct is on; obeys the per-model capability gate. Turn it off for one agent with codeAct: {disabled: true} or the editor's CodeAct (compute sandbox) switch (v0.172.0) |
| Context compaction | On by default, sized from the offering's context_window unless the YAML narrows it |
| Token budgets | From the offering's context_window unless the YAML narrows them |
| Credentials / provider | Always 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 approval | After planning, the agent |
|---|---|
| Skip (default) | Presents the plan briefly and switches to execute mode by itself |
| Ask | Asks 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.
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]
| Part | Meaning |
|---|---|
| Tasks n/m done | Completed todos out of all todos |
| auto-continue i/N | Which round of automatic continuation the agent is on, out of loop.maxIterations |
| plan / execute | The 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.
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 see | The agent did |
|---|---|
| Listed workspace files / Read workspace file / Wrote workspace file | Used its jailed file access under CODING_WORKSPACE_DIR. "Read workspace file" also covers reading just a range of lines (v0.161.0) |
| Searched workspace content | Grepped the workspace |
| Saved to agent memory / Read agent memory | Used its own file memory store (agent-file-memory) |
| Added a task / Completed a task / Checked remaining tasks | Updated its todo list — this is what drives the keep-going loop |
| Switched agent mode / Checked agent mode | Moved between plan and execute |
| Ran command | Ran a shell command in the workspace. The output is plain text -- colour codes are removed (v0.162.0) |
| Ran skill script | Executed 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.0Earlier 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.
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.
Project folders and project rules
Since v0.208.0 each harness conversation works in one folder of the workspace. While a
harness agent is the run-target, the composer on the chat page shows a folder icon; its
tooltip names the current folder (Project folder: projects/shop). Click it to open the
Project folder dialog:
- browse the workspace one level at a time and press Use this folder;
- press New folder to create a folder for a new project, right where you are;
- press Use workspace root, or pick one of the five folders you used last;
- folders that hold an
AGENTS.mdor aCLAUDE.mdcarry a badge.
The agent's file tools and its shell both start in that folder, which is also what the
File Explorer shows. The folder belongs to the conversation: it is saved with the chat and
shown again when you reopen it, and a new chat starts at the workspace root. You can switch
folders between messages -- the agent keeps its history, todo list and mode, and is told
the new folder. The folder must exist inside the workspace; a path through a link or a
junction, and the agent's own agent-file-memory folder, are refused with an error.
Following AGENTS.md / CLAUDE.md
Turn project rules on per agent, in the YAML or with the editor's switch:
projectRules:
enabled: true # default false
The agent then reads the project folder's AGENTS.md and CLAUDE.md at the start of
every turn and follows them as the project's conventions -- how to build, test and
format, its structure, what not to touch:
| Rule | What happens |
|---|---|
| Both files present | Both apply, AGENTS.md first. A CLAUDE.md with the same text, or one that only says @AGENTS.md, counts once |
@docs/style.md lines | Replaced by that .md file of the folder, one level deep (not inside code blocks) |
| A rules file the agent writes | Applies from the next message |
| Size | At most 64 KiB per file and 32,000 characters in total; a cut is marked in the text |
| Not read | Files in sub-folders or parent folders, files reached through links, binary files |
The rules rank below ChatWalaʻau's own rules: they cannot change the safety rules, the
tool limits or your instructions. The task indicator shows what applied for the turn --
Project: projects/shop · Rules: AGENTS.md, CLAUDE.md -- and the log names the files and
their sizes, never their text.
A cloned repository's AGENTS.md is text written by someone else, and the harness shell
runs without an approval step. Keep projectRules off for agents that work on code you
have not reviewed.
The harness file tools used to start in agent-file-access/ while the shell started at
the workspace root. Both now start in the project folder. Files written earlier are still
in agent-file-access/; pick that folder as the project folder if a conversation needs
the old layout.
Restarts keep the agent's state
Since v0.207.0 a harness conversation carries on where it stopped when the server
restarts. At the end of every turn -- finished, failed or stopped -- the agent's
working state is saved next to the chat, in
.sessions/<chat id>.harness/<harness id>.json: its history (including what context
compaction already folded away), its todo list, its mode and its loop state. The next
turn after a restart loads it back.
A conversation saved by an earlier version keeps working after an upgrade. If it used Claude's hosted web search before v0.210.0, that old search reaches the model as a short note with its source links; the saved conversation is not changed.
The same happens after things that rebuild the agent without a restart: saving an MCP server, reloading the model catalog, changing Skills, or a long pause during which many other harness chats pushed this one out of memory. Before v0.207.0 each of these started every open harness conversation from scratch.
| After this | The agent resumes with |
|---|---|
| A server restart, an MCP / catalog / Skills change | Everything: history, todos, mode |
| An edit of the harness YAML | The history only; todos and mode start from the YAML |
| An edit that also switches the model | The history without the previous model's reasoning |
The saved state belongs to the chat: deleting the chat deletes it, archiving moves it
into .archived/ with the chat, and a temporary chat's state expires with it.
Export does not include it, so an imported or branched chat starts a new agent
session. If the file cannot be read (for example after a downgrade), it is renamed
*.corrupt-<time>.json and the conversation starts fresh; the chat itself is never
affected. The file holds tool output in plain text, like the chat file next to it.
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 this | The agent |
|---|---|
| Edit an earlier message | Starts over: history, todos and mode are cleared |
| Delete a message | Same |
| Delete the conversation | Same; its saved state is deleted and its shell process released |
| Send a normal follow-up | Keeps everything, as usual |
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. Editing or deleting a message also deletes the saved state, so a restart afterwards does not bring the old state back.
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:
| At | What 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.
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.
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.