Skip to main content
ReasonBlocks evaluates trajectory health with six trajectory monitors that run server-side on rb-api. MonitorSteeringInjection posts the trace to POST /monitors/evaluate each step; when a monitor fires, the server returns steering text that the middleware appends to the system message as the [REASONBLOCKS] block.
Earlier SDK versions shipped a local pure-Python monitor suite (reasonblocks.monitors.suite.evaluate_all, DEFAULT_WEIGHTS). That suite has been removed — monitor evaluation now runs server-side only. If you need a local trajectory signal (for example, to drive TokenSavingMiddleware’s early-exit nudge), supply your own signals_fn; see token saving.

How monitor steering works

MonitorSteeringInjection.retrieve posts the current trace to POST /monitors/evaluate with the model identifier, task_profile, and any explicit weight overrides. The server runs the six monitors, applies its injection gate, optionally falls back to an E2 pattern lookup, and returns:
{
    "inject":              bool,           # whether to fire an injection this step
    "intervention_text":   str | None,     # text to append to the system message
    "fired":               list[str],      # monitors whose score reached the fire threshold
    "scores":              dict[str, float],
    "composite":           float,          # weighted sum across the six monitors
    "failure_type":        str | None,     # categorical failure label
    "intervention_source": str | None,     # which path produced intervention_text
}
When inject is True and the text is new (different from the last firing), the middleware queues a PendingInjection carrying that text plus monitors_fired and failure_type. Per-run guardrails are enforced client-side after the server’s decision:
  • Hard cap of 5 monitor injections per run.
  • Cooldowns by FSM state: every 2 steps in SLOW/SKIP, every 3 in NORMAL, every 5 in FAST.
  • Duplicate suppression: the same intervention text is not re-queued back-to-back.
The task_profile argument selects the server-side weight preset (coding, pr_review, qa). See Monitor weights and profiles for the per-profile weight tables and how to override individual weights.

The six monitors

Scores are in [0, 1] where 1 is maximum badness. The weighted sum is the composite; the coding weights are shown below (other profiles redistribute them).
Examines what the agent has concluded, not what it called, and flags when its stated claims contradict each other or earlier findings. It carries the highest weight because it’s the only detector that looks at the agent’s reasoning output rather than its tool-call pattern.
Detects the same action or search repeated with reworded inputs. It is embedding-based, so it recognizes that “session timeout”, “session expiry”, and “session token expiration” are the same loop — something edit-distance similarity would miss.
Fires when the agent reaches a conclusion without verifying its work — no test run, no re-read of the file it changed, no confirmation step before declaring done.
Detects repeated re-summarization cycles: the agent compresses its context, re-derives the same state, and churns without making forward progress.
Fires when a run that should be converging instead sprawls — new tools and tangents introduced deep into the trajectory.
Detects the agent quietly drifting away from the original task without acknowledging the change of direction.

How monitor results gate E1 retrieval

The middleware consumes the server-side evaluation to gate E1 retrieval, so vector search stays off healthy steps. A two-step lookback keeps the gate open across consecutive at-risk steps.
allow E1 if:
  any monitor fired this step
  OR composite this step > 0.15
  OR any monitor fired on the previous 2 steps
If MonitorSteeringInjection is not configured at all, E1 is allowed unconditionally for backward compatibility.
Inspect what fired on each step via mw.step_log[i].monitors_fired and the composite via the live telemetry. E1 stays quiet when the run looks healthy, so an empty monitors_fired over the trailing window is expected on a clean run.