Agent runs 

An agent run is one execution of the tool-calling agent loop. A run can pause and wait for a human before it continues: to approve a tool call it wants to make, or to supply a piece of typed input it asked for. The Agent Runs module is the inbox where you make those decisions and review runs that have finished.

Agent runs are admin-only. The module lives in Admin Tools > LLM > Agent Runs. Any admin may act on any run — there is no per-user ownership; the acting backend user is recorded for audit only. The page works fully with JavaScript off (JavaScript only adds focus and a deny confirmation).

The inbox 

The module shows two lists:

  • Awaiting your decision — runs paused for an approval or for input, each rendered as a card with the controls to resolve it.
  • Recent runs — a read-only table of the most recently finished runs (configuration, status, created, finished, and cost when non-zero).

If the store cannot be read, the page shows a warning box rather than a silently empty inbox — an empty list therefore means "nothing waiting", not "load failed".

Approving a tool call 

A run pauses for approval (status WAITING_FOR_APPROVAL) when the agent wants to call a tool that opts in to human approval, or when a guardrail demands it. The card lists every tool call in the pending turn — the tool name and, in a collapsible Arguments block, the exact arguments the model proposed. A call whose tool is no longer registered is flagged.

One Approve or Deny covers the whole pending turn, not a single call. Denying ends the run.

Providing input 

A run pauses for input (status WAITING_FOR_INPUT) when the agent asks for typed data against a declared schema. The card renders a form with one field per schema property (text, number, integer or checkbox, with the field description shown). Submitting validates and coerces the values against the current schema; invalid input re-renders the form in place, keeping what you typed and pointing at the error, rather than losing the run.

Running queued runs asynchronously 

By default a queued run executes in-process, synchronously, with no setup — suitable for interactive and small workloads. For genuinely asynchronous execution, route the queue message to the Doctrine transport and run a consumer:

// settings.php / additional.php
$GLOBALS['TYPO3_CONF_VARS']['SYS']['messenger']['routing']
    [\Netresearch\NrLlm\Service\Agent\Queue\AgentRunQueuedMessage::class] = 'doctrine';
Copied!
vendor/bin/typo3 messenger:consume doctrine
Copied!

Reclaiming stale runs 

When a run is executed asynchronously, the worker holds a 15-minute lease that it renews at every step. If the worker dies, the run is left RUNNING with a lease nobody renews. The reaper reclaims those runs — it puts them back on the queue, or, after three failed attempts, dead-letters them so they stop occupying the running set:

vendor/bin/typo3 nrllm:agent:reap
Copied!

--limit (default 50) bounds how many stale runs one invocation handles. Schedule it from cron or the scheduler's Execute console commands task. It only concerns asynchronous runs — interactive runs hold no lease — and does nothing useful without a running consumer.

Retention and privacy 

A waiting run stores the transcript it needs to resume — the pending tool calls and the conversation so far — verbatim, so it can pick up exactly where it paused. Unlike the per-step event log, this resumable state is kept in full regardless of the configured privacy level, and is cleared when the run settles to a terminal status.

Nothing is deleted until a purge runs. Finished runs are removed on the privacy.retention.agentRun window; runs still waiting for a decision use the separate, deliberately longer privacy.retention.approval window, so a purge never destroys work an approver has not got to yet. Set the approval window generously if approvers may take days.

See Data retention & purge for the retention settings and the purge command that covers agent runs along with every other content-bearing table.