Runtime lifecycle ​
An agent run is where stable agent configuration meets one application request. The application owns the request boundary; Anvia owns the bounded model-and-tool loop inside it.
Runtime objects ​
An Agent owns the model, instructions, context, tools, memory registration, policies, observability, and default limits.
A session scope adds a durable conversation identity to a run when the agent has memory configured.
A call to generate() or stream() supplies one input and optional run controls:
const result = await supportAgent.generate({
prompt: input.message,
maxTurns: 4,
toolConcurrency: 2,
trace: {
name: 'support-run',
userId: user.id,
metadata: { ticketId: input.ticketId },
}
})An agent run accepts one options object containing either prompt or messages. A run with session uses prompt because it loads the transcript from the configured store.
Run sequence ​
For a normal run, Anvia:
- normalizes the input and validates run options;
- creates a run ID and loads memory when a session is active;
- starts observers and calls
lifecycle.onStart; - evaluates input guardrails and commits accepted input to memory;
- retrieves context documents and dynamic tool definitions for the current turn;
- builds the normalized completion request and applies request middleware;
- calls the model, using the inherited or run-level retry policy for transient failures;
- applies response middleware and records usage and step lifecycle data;
- executes requested local tools, pausing first when approval is required;
- stores assistant and tool messages, then starts another model turn; and
- applies output guardrails, commits the completed run, closes observers, and returns the result.
Retrieval is evaluated again for each model turn. A tool result or steering message can therefore change the documents and dynamic tools selected next.
Lifecycle callbacks ​
Use lifecycle for application-owned callbacks around the run:
import type { AgentLifecycle } from '@anvia/core'
const lifecycle = {
onStart({ runId, maxTurns }) {
console.log('run started', runId, maxTurns)
},
onStepFinish({ step, usage }) {
console.log('step finished', step, usage.totalTokens)
},
onToolStart({ step, toolName }) {
console.log('tool started', step, toolName)
},
onToolFinish(event) {
console.log('tool finished', event.toolName, event.success)
},
onFinish(event) {
if (event.status === 'completed') console.log('run completed', event.output)
if (event.status === 'blocked') console.log('run blocked', event.stage)
if (event.status === 'suspended') console.log('run suspended', event.interaction.type)
},
onError({ error }) {
console.error('run failed', error)
},
} satisfies AgentLifecycle
const observedAgent = new Agent({
id: 'observed-support',
model,
lifecycle,
})Agent-level and run-level lifecycle callbacks are composed in that order. Callback input is snapshotted. If a lifecycle callback throws, the run fails and the error lifecycle is invoked.
Use observers for tracing and telemetry integrations. Lifecycle callbacks are application behavior; observers are integration surfaces with their own failure policy.
Streaming lifecycle ​
stream() runs the same model, tool, guardrail, memory, and observer lifecycle while exposing events such as:
turn_startandgeneration_start;text_delta,reasoning_delta, and optionaltool_call_delta;tool_call,tool_result, and nestedagent_tool_event;source,provider_tool_call, andguardrail_decision;turn_end,steering_applied,interaction_response,memory_compaction, terminal outcomes, anderror.
A stream segment ends directly with response, interaction, or blocked. When approval or a structured answer is required, start the next phase with agent.stream({ continuation, response }). Closing an active stream early cancels that phase, finalizes memory and observers, and prevents silent work from continuing in the background.
Filter events before sending them to a client because reasoning, tool inputs, tool results, retrieved context, and provider metadata may contain private data.
Continue with Errors and limits.