Skip to main content
Commands (in) | MetaharnessCommands (in) in the source-owned Metaharness documentation.Metaharnessreferencemetaharnessreferenceevaluatordeveloperresearcherreference

Commands

One JSON object per line on stdin, each tagged metaharness.command/1.

{"format":"metaharness.command/1","id":"c1","command":"tool.decide","call_id":"…","decision":"allow"}

Every command carries an id and produces exactly one command.result. A command that can be silently ignored is a control surface that cannot be tested.

A control this adapter cannot honour is refused by name, at run start rather than at the call — so a run that will fail on control fails before it spends money.

The seven commands

CommandNeedsNotes
tool.decidecall tierDecide one pending tool call.
frame.setcall tierPut a new frame in force at the next boundary.
message.injectturn tierAdd text to the conversation between turns.
steera mid-turn tierSteer a running turn. Claude Code headless has no such tier — on that adapter this is always refused by name.
permission.setrun tierChange the permission posture mid-run, in the vendor's own vocabulary.
interruptStop the running turn. Every adapter must deliver this.
haltStop the run. Every adapter must deliver this: a control surface with no way out is not a control surface.

frame.set is not partially deliverable

An adapter that could inject the frame's text but not enforce it would tell the model "strictly only these operations" and make that false.

So a run whose configuration needs frame.set without call-level enforcement is refused at start. An embedder that genuinely wants advisory text uses message.inject, which claims nothing.

frame.set takes effect at the next turn or step boundary, and its result says which — a frame that took effect mid-turn would mean a call adjudicated against a frame the model was never shown.

Four decisions

DecisionWhat it does
allowLet the call run.
denyRefuse the call, and tell the model why.
replaceRun the call with this input instead.
abstainClaim nothing; let the vendor's own permission pipeline decide.

allow grants — a departure worth naming

The harness honours a hook allow and bypasses the rest of its permission pipeline. So an allow from metaharness overrides a stricter rule elsewhere in the vendor's settings.

A run that also relies on such a rule must use deny-only policy, and say so.

Which is why abstain exists

Without abstain, the only way to let a call through is allow — which grants. A run with no frame, deciding allow because it had nothing to narrow with, would silently be a run with the vendor's permission system switched off.

Abstaining says the true thing instead: metaharness adjudicated nothing here.

It is the convention proven on Claude Code, where the reference hook passes a call through by exiting 0 and emitting no permissionDecision at all — because saying allow there would claim an authority the layer does not have.

abstain is the default policy's answer when no frame is in force.

deny requires a non-empty reason

Both vendors' hook wires require it, and the reason is the only part the model can act on: the difference between a wall and an instruction.

replace is never silently downgraded

Both vendors' hook wires carry an updated input, and hiding that would push embedders into deny-and-re-prompt — a whole turn spent expressing something the wire already supports. A replace an adapter cannot deliver is refused by name; it never quietly becomes an allow.

Five refusal codes

CodeMeaning
UNSUPPORTED_CONTROLThis adapter cannot honour this command at all. Emitted at run start for every command the run's configuration will need.
UNKNOWN_CALLThe call_id does not correlate to an open request.
TOO_LATEThe window closed — the decision deadline expired, or the turn ended.
MALFORMEDThe command did not parse, or a required field is missing.
SHADOWEDThe vendor would accept this and another layer would silently override it.

SHADOWED is refused rather than delivered on a simple principle: a control that appears to work and does not is worse than one that is absent.