---
title: "Tool invocation rules"
manual: "LLM Foundation"
version: "0.41"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-llm:api-tool-invocation-policy@0.41"
source: "Api/ToolInvocationPolicy.rst"
rendered: "2026-10-20T08:48:47+00:00"
---

# Tool invocation rules {#api-tool-invocation-policy}

An installation may add a `ToolInvocationRuleInterface` implementation to
restrict calls by their final arguments, actor, configuration, target or prior
outcomes. Symfony autoconfiguration registers the rule. Its `identifier()`
must return a unique, stable code matching `[a-z][a-z0-9_.-]*`, at most
64 ASCII characters long.

The existing `ToolCallPolicyInterface` first checks whether the tool remains
permitted. The invocation policy then calls every rule until one denies. A rule
returns `ToolInvocationDecision::allow()` or
`ToolInvocationDecision::deny('outside_site')`. Reason codes use the same
format as identifiers; arguments and exception text never form a denial message.
No rules preserves the existing permission behavior.

The context contains the tool name, final arguments, configuration and
`ToolExecutionContext`. The latter carries the initiating actor, acting
backend user and run reference. Input resumes supply the validated human input
after it has replaced the model's values for declared input fields. Approval
resumes evaluate the same policy immediately before the approved call executes.

Implement `ToolTargetResolverInterface` when the rule needs a target. Return
a `ToolInvocationTarget` with the authoritative kind and identifier, or
`null` for calls the resolver does not understand. Multiple resolvers
claiming a call, or an exception while resolving it, refuse execution. A missing
target remains `null`; a rule requiring a target must explicitly deny it.
Target references are stored in the history, so they must contain identifiers,
never credentials or document content.

`ToolInvocationHistory` is independent of the model transcript. Each
observation contains the tool, outcome and optional target reference. Suspensions
persist it and both resume paths carry it forward. A rule whose
`requiresCompleteHistory()` returns `true` is refused when a legacy or
damaged state cannot prove its history. A bare `runLoop()` call starts a
complete empty history only when it assembles a fresh prompt and all three seed
counters are zero. A call with `skipAssembly` or any nonzero seed counter
must supply authoritative `ToolExecutionContext::$initialInvocationHistory`
to prove its prior observations; otherwise the history remains incomplete.
A rule exception refuses execution as
`rule_failed`. Denials retain the rule identifier and stable reason in the
`invocation_denied` governance event without arguments or result content. The
event's reason is the bounded rule code, separate from the `ToolDenialReason`
used for `tool_denied` events.
