Persisted agent runtime
Inject Netresearch for runs
that need persisted status, approval, input or background execution. It is a
consumer interface: call it rather than implementing or decorating it.
Methods and outcome cases may be added in minor releases; handle unknown
outcomes with a default branch.
Starting a run
Resolve a configuration as in Step 5: Select a database configuration and construct an actor from the authenticated backend user at the request boundary. Never accept user IDs, admin flags or grants directly from request data.
use Netresearch\NrLlm\Domain\ValueObject\AiActorContext;
use Netresearch\NrLlm\Domain\ValueObject\ChatMessage;
use Netresearch\NrLlm\Service\Agent\AgentRunRequest;
$actor = AiActorContext::fromBackendUser($backendUser);
$request = new AgentRunRequest(
configuration: $configuration,
messages: [ChatMessage::user('Summarize this page.')],
actor: $actor,
allowedToolNames: [],
maxIterations: 3,
);
$result = $agentRuntime->run($request);
allowed offers no tools. null starts from the enabled
set; configuration, actor and skill policy still narrow it. A list narrows
the offer to those names. The requested round cap is clamped to the instance
ceiling. Raw response capture is off by default.
The returned Agent has outcome, run and steps.
On completion, loop holds the answer and aggregate usage. An
approval suspension carries suspended; failures carry error.
Run outcomes return as results, while invalid resume requests can throw
Agent. A blank run means persistence failed:
do not present that result as a stored, resumable run.
Lifecycle methods
run($request, $on Step = null) - Executes synchronously. The optional callback receives each step as it is recorded, including live text deltas that are not persisted.
enqueue($request) - Stores and dispatches the run, returning its UUID. The default synchronous message transport can execute it before this method returns.
defer($request) - Stores background work without executing it in the caller. Route it to an
asynchronous consumer or run
nrllm:.agent: work runQueued ($run Uuid, $on Step = null) - Worker entry point. Claims the queued run atomically; returns
nullif it is already claimed, cancelled or otherwise unavailable. approve($actor, $run Uuid, $decision, $on Step = null) - Decides the whole pending turn and continues it. Build an
Approvalwith the reviewed turn's digest; a missing or stale digest is refused. Approval does not bypass current tool or actor permissions. A continuation can suspend again.Decision submitInput ($actor, $run Uuid, $submission, $on Step = null) - Validates an
Inputagainst the pending schema and turn digest, claims the decision and continues. Input remains untrusted data.Submission cancel($actor, $run Uuid) - Cancels a non-terminal run; returns whether this caller won the guarded transition. Cancellation is cooperative at step boundaries; an in-flight call without cancellation support can finish its current step.
cancelIf Waiting ($actor, $run Uuid) - Withdraws only a run waiting for approval or input. A run already released for execution is not cancelled. Restricted to its initiator or an admin.
endProcess ($actor, $run Uuid) - Releases a held guided process and cancels a waiting run. It does not undo approved writes. A queued or running run cannot be ended through this method. Restricted to its initiator or an admin.
status($actor, $run Uuid) - Returns the run or
nullwhen unknown. Resumable transcript state is stripped from this public readout. events($actor, $run Uuid, $after Sequence = - 1) - Returns events in ascending sequence order, strictly after the supplied cursor. An empty list alone does not prove that the run exists.
Approval, identity and storage
A tool approval creates a waiting run. A guardrail approval_
verdict fails the run with a policy reason; it does not create that approval
workflow. See Agent runs for the operator's view.
Workers restore the initiating actor rather than borrowing an ambient backend user. Service accounts need explicit scopes and do not acquire backend-user privileges. Re-check authorization at the application's HTTP boundary.
Waiting runs retain verbatim state needed to resume. Event-log privacy settings do not suppress that state. See Data retention & purge for background-answer storage and expiry.
For an unpersisted loop, inject
Netresearch. Its
run accepts messages, configuration, Tool
and an explicit allowed-tool list. It can throw approval or input suspension
exceptions; callers must provide their own lifecycle handling. Prefer the
persisted runtime when users need to resume work. See
Tool/function calling and Registering a tool.