The Agent Loop
The loop's defining contract: the request never drops.
Once a binding kicks in, the model is told about the Hierarchy's tools via `tools`; if the model emits a `tool_call`, the gateway runs it against Trellis, appends the result as a `role: "tool"` message, and calls the model AGAIN — that is one "turn". Turns continue until the model gives a final answer or a budget runs out.
#The request never drops
This is the subsystem's defining contract: no way a tool call can fail ever surfaces to the user as an error — each one comes back to the model as a readable tool result, and the loop keeps running. The table below names three of them; the judge's own way of blocking a call is covered separately, further down.
| Situation | What the model receives |
|---|---|
| Hierarchy gate refusal | Text prefixed with "⛔" naming which parent must be called first — the model sees it and self-corrects by calling the parent. |
| Transport error (Trellis unreachable, the tool raises…) | A `tool error: <reason>` text. |
| Timeout | A `tool error: '<tool>' timed out after <N>s` text. |
#Turn and time budgets
- max_turns
- Set on the binding (1–50, default 6 in the panel). Without a binding (a web_search-only loop) a global default is used.
- tool_timeout_sec
- Set on the binding (1–300s, default 30s). Applied to each tool call individually.
- toplam duvar-saati bütçesi
- A single hard ceiling, independent of the binding and admin-tunable (default 120s) — it covers the TOTAL time across ALL turns of the loop. This ceiling can end the loop even before max_turns is reached.
When a turn or the total budget runs out, the request STILL does not error: the last assistant answer available is returned to the client with a short note explaining the budget ran out. If the model never produced anything at all (the very first turn's self-call itself failed), the loop silently returns nothing and the request continues down its normal, non-agentic path — as if the feature did not exist.
A single turn's tool-call count also has a cap (default 8): calls beyond it are simply never executed in that turn, AND the assistant message carried into the next turn is rewritten to list only the calls that actually ran — the model never believes a call executed when it didn't.
#Every turn is a self-call
Every turn of the loop is made as a self-call back through the gateway's OWN front door (`/v1/chat/completions`) — the loop routes its own traffic through the exact path it processes everyone else's. Each self-call carries an `X-MCP-Internal` header; seeing it, the gateway SKIPS every return-path injection (system prompt, Second Brain, guardrails, and the loop itself) for that turn — without the skip, every turn would re-apply the system prompt and memory injection, and the loop would call itself forever.
#The judge runs inside this loop
When a judge policy is configured, every tool call passes through the `tool_call` surface right BEFORE it runs, and its result through the `context` surface right BEFORE it reaches the model. The details of those two surfaces (actions, clustering, modes) are covered on Platform & Admin → Judge and Judge Policies; the one thing specific to this loop is that a `block` verdict here never produces a 422 — the model gets a readable "tool error: blocked by policy…" result instead, and the loop keeps running under the exact same "the request never drops" contract as above.