Runtime Architecture Deep Dive
Understanding the agent execution engine and event-driven architecture
The ADK-TS Runtime is the core execution engine powering agent applications during user interactions. It orchestrates agents, tools, and services through an event-driven architecture, serving as the coordination layer that manages component interactions via a cooperative async generator pattern.
Core Responsibilities
The Runtime handles five critical areas of agent execution:
- Execution Orchestration: Coordinates agent, tool, and callback execution in the proper sequence
- Event Processing: Manages event flow between components using async generators
- State Management: Persists session state changes and maintains conversation history
- Service Integration: Connects with LLMs, storage systems, and external services
- Resource Management: Controls lifecycle and resource allocation throughout execution
Event-Driven Architecture
The Runtime operates on events—agents yield events to communicate with the Runner, which processes them and coordinates with services before allowing agents to continue.
Getting Started
The ADK-TS Runtime can be used at different levels of abstraction depending on your needs.
Method 1: Using AgentBuilder (Recommended for Quick Start)
The fastest way to get started with ADK-TS is using AgentBuilder, which handles session and runner setup automatically:
import { AgentBuilder } from "@iqai/adk";
// Simple one-liner for basic interactions
const response = await AgentBuilder.withModel("gemini-2.0-flash-exp").ask(
"What is the capital of France?",
);
console.log(response); // "The capital of France is Paris."For more control with streaming support, build the agent and access the runner:
import { AgentBuilder } from "@iqai/adk";
// Build agent with full configuration
const { runner, session, agent } = await AgentBuilder.create("travel-assistant")
.withModel("gemini-2.0-flash-exp")
.withInstruction("You are a helpful travel planning assistant")
.build();
// Stream events as they occur
for await (const event of runner.runAsync({
userId: "user_123",
sessionId: session.id,
newMessage: { parts: [{ text: "Plan a 3-day trip to Paris" }] },
})) {
if (event.partial) {
process.stdout.write(event.content?.parts?.[0]?.text || "");
} else {
console.log("\nComplete:", event.content?.parts);
}
}Automatic Session Management
AgentBuilder automatically creates and manages an in-memory session—no setup required. For production apps requiring persistent sessions, switch to a database-backed SessionService and use the low-level approach below.
Method 2: Direct Runtime Setup (Full Control)
For production applications or when you need explicit control over services, use the low-level Runtime API:
import { LlmAgent, Runner, InMemorySessionService } from "@iqai/adk";
// Step 1: Create your agent
const agent = new LlmAgent({
name: "assistant",
model: "gemini-2.0-flash-exp",
description: "A helpful assistant",
instruction: "You are a helpful assistant. Be concise and friendly.",
});
// Step 2: Set up services
const sessionService = new InMemorySessionService();
const session = await sessionService.createSession("my-app", "user_123");
// Step 3: Create the Runner
const runner = new Runner({
appName: "my-app",
agent,
sessionService,
});
// Step 4: Process user input
for await (const event of runner.runAsync({
userId: "user_123",
sessionId: session.id,
newMessage: { parts: [{ text: "Hello! How are you?" }] },
})) {
console.log(`${event.author}:`, event.content?.parts?.[0]?.text);
}How It Works Under the Hood
Both approaches follow the same execution model. Here's what happens when you send a message:
1. Session Management
A Session is created or loaded, identified by appName and userId. The session stores conversation history and state.
2. Runner Orchestration
The Runner receives your message and appends it to the session. It then calls the agent's async generator and begins iterating through events.
3. Agent Execution
The Agent processes the message and yields Event objects containing:
- Model responses (text, images, audio)
- Tool calls and their results
- State updates via
stateDelta - Control signals (transfer, escalate)
4. Event Processing Loop
For each event the agent yields:
- If
partial=true: The Runner immediately forwards it (for streaming UI updates) without persisting - If
partial=false: The Runner commits the event to the session, applies state changes, updates memory, then forwards it
5. Completion
The loop continues until the agent yields a final response event (no pending tool calls). The Runner completes and the invocation ends.
The key difference between the two approaches: AgentBuilder.ask() consumes all events internally and returns just the final response, while the low-level API gives you access to every event for real-time streaming.
Key Components of the Runtime
Understanding these core components—and how they interact—helps you design predictable ADK-TS agents.
Runner
The Runner is the orchestrator for each user interaction. It enters through runAsync, creates an InvocationContext with a unique invocationId, and loads the session via SessionService while appending the user message as an event.
As the agent yields events, the Runner processes each one, coordinating with services (session, memory, artifacts) before forwarding events upstream. The loop repeats until the agent finishes its response.
Execution Logic Components
This is where your application's logic lives:
- Agents (
BaseAgent,LlmAgent): ImplementrunAsyncImpl, make decisions, and yieldEventobjects (responses, tool calls, state updates) - Tools (
BaseTool,FunctionTool,AgentTool): Provide external capabilities and return results wrapped in events - Callbacks: Customize behavior;
BaseAgentoffers before/after agent callbacks,LlmAgentadds before/after model and tool callbacks
All of these yield events, pause until the Runner processes them, then resume with the latest state.
Event
Events are the messages exchanged between Runner and execution logic. Common contents:
- User inputs and agent responses
- Tool calls and tool results
- State updates via
stateDelta(temp_keys are ephemeral) - Control signals (transfer, escalate, skip summarization)
Each event has content plus actions (EventActions) that describe side effects like state or artifact changes.
Services
Services provide durable and shared resources:
- SessionService: Manages sessions, applies
stateDelta, appends events - ArtifactService: Stores/retrieves blobs; events can record
artifactDeltafor version tracking - MemoryService: Optional long-term semantic memory updated after non-partial events
The Runner uses these services while processing each non-partial event.
Session
Sessions hold conversation context:
state: Key-value store for the conversationevents: Ordered history of all events- Metadata: Session ID, timestamps, related details
Sessions are managed by SessionService and consumed by both Runner and agents.
Invocation
An invocation is everything that happens for a single user query:
- Identified by a stable
invocationIdwithinInvocationContext - May include transfers to sub-agents, multiple LLM calls, and tool executions
- Use
temp_keys instateDeltafor data that should not persist beyond this request
These components interact continuously through the event loop to process user requests in a coordinated, predictable manner.
Runtime Architecture Deep Dive
Explore each component of the runtime in detail, ordered from fundamental concepts to advanced features:
🔄 Event Loop Pattern
The cooperative async generator pattern that powers ADK-TS
🎯 Runner & Event Processing
How the Runner orchestrates execution and processes events
📋 Invocation Lifecycle
End-to-end walkthrough from user query to final response
⚙️ Runtime Configuration
Configure streaming, speech, artifacts, and execution limits
⏮️ Session Rewind
Restore sessions to previous states for debugging or undo