TypeScriptADK-TS

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.

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): Implement runAsyncImpl, make decisions, and yield Event objects (responses, tool calls, state updates)
  • Tools (BaseTool, FunctionTool, AgentTool): Provide external capabilities and return results wrapped in events
  • Callbacks: Customize behavior; BaseAgent offers before/after agent callbacks, LlmAgent adds 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 artifactDelta for 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 conversation
  • events: 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 invocationId within InvocationContext
  • May include transfers to sub-agents, multiple LLM calls, and tool executions
  • Use temp_ keys in stateDelta for 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: