---
title: "ADR-210: A caller can ask which tools run without approval"
manual: "TYPO3 LLM Extension"
version: "0.38"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-llm:adr-210@0.38"
source: "Adr/Adr210ACallerCanAskWhichToolsRunWithoutApproval.rst"
rendered: "2026-09-30T10:31:37+00:00"
---

# ADR-210: A caller can ask which tools run without approval {#adr-210}

-   *Status:* Accepted
-   *Date:* 2026-09-26
-   *Authors:* Netresearch DTT GmbH

## Context {#context}

`ToolLoopServiceInterface::runLoop()` suspends a run as soon as the
model calls a tool that needs a human approval, and throws
`ToolApprovalRequiredException` with the state to resume from
([ADR-084](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-084@0.38)). A caller that has no approval step, such as an
editor dialog that runs one task and shows the answer, cannot resume, so for
it the exception is a failed run.

Such a caller wants to offer the model only the tools that never suspend. The
rule that decides this, `ToolApprovalRule::requiresApproval()`
([ADR-134](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-134@0.38)), is `@internal`, and so is the registry it needs
the tool from. `ToolCallPolicyInterface` (`@api`) answers which tools
may be offered, but its `ToolPolicyDecision` says nothing about approval.
A consumer is therefore left to re-derive the rule from internal classes, or to
offer every tool and fail when one suspends.

## Decision {#decision}

**A new** `@api` **interface,** `UnattendedToolFilterInterface`, **with
one method:** `unattended(list<string> $toolNames): list<string>` returns the
names, in their order, of the tools that run without an approval.

-   It asks `ToolApprovalRule::requiresApproval()` about the tool the
    registry holds under each name. The rule stays the only place that decides,
    so a local tool that gains a write effect, or any tool that gains the
    approval marker, leaves the unattended set without a change here.
-   It inherits the rule's exemption for remote tools. A remote tool is judged
    on the operator's declaration, not on its effect
    ([ADR-134](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-134@0.38)), so an imported tool without a declaration stays
    in the unattended set whatever it does — exactly as it runs without an
    approval in an agent run. An operator who wants such a tool kept out of a
    caller without an approval step declares it approval-bound on its server
    record.
-   A tool that asks for typed input (`RequiresInputInterface`) is left
    out as well. It needs no approval, but the loop suspends for its input
    (`WAITING_FOR_INPUT`), and a caller without an approval step has no input
    step either. "Unattended" means no human pause of either kind.
-   A name the registry does not know is left out. An unknown tool cannot be
    shown to be free of approval, and leaving it in would offer the model a
    call that suspends.
-   It filters; it does not decide what may be offered. A caller first asks
    `ToolCallPolicyInterface::filterOfferable()` and then narrows that list,
    so the five policy gates of [ADR-094](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-094@0.38) still apply.

**A separate interface, not a method on** `ToolCallPolicyInterface`.
Adding a method to an `@api` interface breaks every class that implements it.
The two questions are also different: the policy answers whether a tool may
take part in a run at all, this filter answers whether it can take part
without a human.

## Consequences {#consequences}

● A caller without an approval step runs the tool loop on
`ToolCallPolicyInterface::filterOfferable()` narrowed by
`UnattendedToolFilterInterface::unattended()` and meets neither an
approval nor an input suspension from a registered tool.

● The approval rule has one reader more and still one definition.

◐ An operator setting that makes a read tool approval-bound, such as the
web page reader's ([ADR-202](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-202@0.38)), removes that tool from the
unattended set too, so a caller without an approval step offers it no
longer. That is what the setting asks for.

✕ The answer is per tool, not per call. A tool whose approval depends on its
arguments would need a finer contract; no tool in this extension has one.
