Skip to main content
Your task agent gets four tools. All four are reads. Every operation returns a JSON envelope with an "ok" key — failures never raise into the agent loop; they come back as {"ok": false, "error": "..."}.
Through v0.8 this page described ten operations, including mailbox reads, trace inspection, and rule writes. Those moved to the package-owned repair agent in v0.9. The reason is measured, not aesthetic: handed the larger surface, an agent spent its turns operating the harness, and most of the rules it wrote were about its own diagnostic protocol.

Operations

That is the entire surface. There is no fifth tool, no admin mode, and no escape hatch.

Example: a task agent consulting its rules

In practice your agent makes these calls when it judges prior rules relevant. Nothing obliges it to: the harness performs no rule lookup on its behalf and makes no tool call before a turn.
Reads include provisional candidates — rules under evaluation, clearly labeled as such. That is deliberate: a rule must be in force to be measurable. Prefer an active rule when a candidate conflicts with it, and use harness_rule_status when you need to be sure which is which.

What the task agent deliberately cannot do

Everything in this table is rejected at dispatch with unsupported capability, including hallucinated calls. None of it has a side effect.

Why the surface shrank

In a measured AppWorld run of the larger design — the whole rule corpus in context, ten tools, and a check-your-mailbox-every-turn mandate — the agent spent its turns operating the harness instead of doing the task. Nine of the thirteen rules it wrote were about gaming its own diagnostic protocol rather than about the work. Two lessons are baked into the current shape. Every tool an agent can call is a turn it might spend not doing the task, so the task surface is four reads. And diagnosis is a different job from execution, so it belongs to a different agent with a different prompt — one whose only output is a candidate rule.

Delivery

harness.task_tools implements the ToolDispatcher protocol, so the simplest wiring is direct:
Or convert to your framework’s native format in one call:
Attaching the toolset is optional. A task agent with no harness tools still benefits from the loop: evaluation, notices, repair, and validation all run regardless. The tools only decide whether the agent can consult what was learned. The schema is exported as TASK_OP_SCHEMAS if you need to inspect it; repair schemas and dispatch stay package-internal.
Route every harness_* name to this dispatcher rather than filtering names yourself. Enforcement lives in TaskToolset.call, so a hallucinated administrative call is rejected safely instead of reaching your domain executor — see the security model.