Skip to main content

Sessions

Sessions represent a series of interactions between a user and agents, maintaining conversation history, state, and metadata across multiple turns. ADK-TS provides a flexible session management system with pluggable storage backends.

Session Interface

A session contains all information about a conversation:
import type { Event } from "@iqai/adk";

interface Session {
  /**
   * Unique identifier of the session
   */
  id: string;

  /**
   * Name of the application
   */
  appName: string;

  /**
   * ID of the user
   */
  userId: string;

  /**
   * Session state (key-value store)
   */
  state: Record<string, any>;

  /**
   * Events in the session (conversation history)
   */
  events: Event[];

  /**
   * Last update timestamp (seconds since epoch)
   */
  lastUpdateTime: number;
}

Creating Sessions

Sessions are created using a SessionService:
import { InMemorySessionService } from "@iqai/adk";

const sessionService = new InMemorySessionService();

// Create a new session
const session = await sessionService.createSession(
  "my-app",      // App name
  "user-123",    // User ID
  {              // Initial state (optional)
    preferences: { theme: "dark" },
    subscription: "premium",
  },
  "session-456"  // Custom session ID (optional)
);

console.log(session.id); // "session-456" or auto-generated UUID

Session Services

BaseSessionService

All session services implement this interface:
export abstract class BaseSessionService {
  /**
   * Create a new session
   */
  abstract createSession(
    appName: string,
    userId: string,
    state?: Record<string, any>,
    sessionId?: string,
  ): Promise<Session>;

  /**
   * Retrieve an existing session
   */
  abstract getSession(
    appName: string,
    userId: string,
    sessionId: string,
    config?: GetSessionConfig,
  ): Promise<Session | undefined>;

  /**
   * List all sessions for a user
   */
  abstract listSessions(
    appName: string,
    userId: string,
  ): Promise<ListSessionsResponse>;

  /**
   * Delete a session
   */
  abstract deleteSession(
    appName: string,
    userId: string,
    sessionId: string,
  ): Promise<void>;
}

InMemorySessionService

For development and testing:
import { InMemorySessionService } from "@iqai/adk";

const sessionService = new InMemorySessionService();

// Sessions stored in memory (lost when process ends)
InMemorySessionService is great for development but not suitable for production as sessions are lost when the process restarts.

Database Session Services

For production use, implement BaseSessionService with your database:
import { BaseSessionService, Session } from "@iqai/adk";
import { createClient } from "@supabase/supabase-js";

export class SupabaseSessionService extends BaseSessionService {
  private supabase;

  constructor(url: string, key: string) {
    super();
    this.supabase = createClient(url, key);
  }

  async createSession(
    appName: string,
    userId: string,
    state?: Record<string, any>,
    sessionId?: string,
  ): Promise<Session> {
    const session: Session = {
      id: sessionId || crypto.randomUUID(),
      appName,
      userId,
      state: state || {},
      events: [],
      lastUpdateTime: Date.now() / 1000,
    };

    await this.supabase
      .from("sessions")
      .insert({
        id: session.id,
        app_name: appName,
        user_id: userId,
        state: session.state,
        events: session.events,
        last_update_time: session.lastUpdateTime,
      });

    return session;
  }

  async getSession(
    appName: string,
    userId: string,
    sessionId: string,
  ): Promise<Session | undefined> {
    const { data, error } = await this.supabase
      .from("sessions")
      .select("*")
      .eq("id", sessionId)
      .eq("app_name", appName)
      .eq("user_id", userId)
      .single();

    if (error || !data) return undefined;

    return {
      id: data.id,
      appName: data.app_name,
      userId: data.user_id,
      state: data.state,
      events: data.events,
      lastUpdateTime: data.last_update_time,
    };
  }

  // Implement other methods...
}

State Management

Session state is a scoped key-value store with three prefixes:
import { State } from "@iqai/adk";

// State prefixes
State.APP_PREFIX;  // "app:"  - Application-wide state
State.USER_PREFIX; // "user:" - User-specific state
State.TEMP_PREFIX; // "temp:" - Temporary session state

Using State

const agent = new LlmAgent({
  name: "stateful_agent",
  model: "gpt-4o",
  
  beforeAgentCallback: async (ctx) => {
    // Read state
    const userName = ctx.state.get("user:name", "Guest");
    const theme = ctx.state.get("app:theme", "light");
    const sessionCounter = ctx.state.get("temp:counter", 0);
    
    // Update state
    ctx.state.set("temp:counter", sessionCounter + 1);
    ctx.state.set("user:last_seen", Date.now());
    
    // Check if key exists
    if (ctx.state.has("user:preferences")) {
      const prefs = ctx.state.get("user:preferences");
    }
  },
});

State Deltas

State uses a delta-based system for efficiency:
const state = new State(
  { existing: "value" },  // Current value
  {}                       // Pending delta
);

state.set("new", "data");

// Only "new" key is in delta
console.log(state.hasDelta()); // true

// Both current and delta values are accessible
console.log(state.toDict()); // { existing: "value", new: "data" }
State changes are automatically persisted by the session service after each agent invocation.

Events

Events represent individual turns in the conversation:
import { Event } from "@iqai/adk";

const event = new Event({
  id: Event.newId(),
  author: "user",  // "user" or agent name
  content: {
    role: "user",
    parts: [{ text: "Hello, agent!" }],
  },
  invocationId: "inv-123",
  timestamp: Math.floor(Date.now() / 1000),
});

// Check event type
event.isFinalResponse(); // true if no pending function calls

// Extract function calls
const functionCalls = event.getFunctionCalls();
const functionResponses = event.getFunctionResponses();

Event Structure

interface Event {
  /** Unique event ID */
  id: string;
  
  /** Who created this event */
  author: string; // "user" or agent name
  
  /** Event content */
  content?: {
    role: string;
    parts: Part[];
  };
  
  /** Text extracted from content */
  text?: string;
  
  /** Invocation ID (links related events) */
  invocationId: string;
  
  /** Event timestamp */
  timestamp: number;
  
  /** Branch for multi-agent isolation */
  branch?: string;
  
  /** Actions to perform */
  actions: EventActions;
  
  /** Partial response (streaming) */
  partial?: boolean;
}

Using Sessions with Runner

import { Runner, InMemorySessionService, LlmAgent } from "@iqai/adk";

const sessionService = new InMemorySessionService();
const agent = new LlmAgent({
  name: "assistant",
  model: "gpt-4o",
});

const runner = new Runner({
  appName: "my-app",
  agent,
  sessionService,
});

// Create session
const session = await sessionService.createSession("my-app", "user-123");

// Run conversation
for await (const event of runner.runAsync({
  userId: "user-123",
  sessionId: session.id,
  newMessage: { parts: [{ text: "Hello!" }] },
})) {
  console.log(event.text);
}

// Retrieve updated session
const updatedSession = await sessionService.getSession(
  "my-app",
  "user-123",
  session.id
);

console.log(updatedSession.events.length); // Includes new events

Session Configuration

Control what gets loaded:
interface GetSessionConfig {
  /** Load only N recent events */
  numRecentEvents?: number;
  
  /** Load only events from specific branch */
  branch?: string;
  
  /** Load events after specific timestamp */
  afterTimestamp?: number;
}

const session = await sessionService.getSession(
  "my-app",
  "user-123",
  "session-456",
  {
    numRecentEvents: 10, // Only last 10 events
  }
);

Event Compaction

Reduce token usage by compacting old events:
import { EventsCompactionConfig } from "@iqai/adk";

const agent = new LlmAgent({
  name: "efficient_agent",
  model: "gpt-4o",
  compactionConfig: new EventsCompactionConfig({
    targetEvents: 20,        // Keep last 20 events
    minEventsBeforeCompact: 50, // Compact when >50 events
    summarizeOldEvents: true,   // Summarize compacted events
  }),
});
Event compaction automatically summarizes old conversation history to reduce context length while preserving important information.

Multi-User Sessions

Manage sessions for multiple users:
// List all sessions for a user
const { sessions } = await sessionService.listSessions(
  "my-app",
  "user-123"
);

for (const sessionSummary of sessions) {
  console.log(`Session ${sessionSummary.id}`);
  console.log(`Last active: ${sessionSummary.lastUpdateTime}`);
}

// Delete old sessions
for (const sessionSummary of sessions) {
  const age = Date.now() / 1000 - sessionSummary.lastUpdateTime;
  if (age > 30 * 24 * 60 * 60) { // 30 days
    await sessionService.deleteSession(
      "my-app",
      "user-123",
      sessionSummary.id
    );
  }
}

Session Rewinding

Rollback conversation to a previous state:
await runner.rewind({
  userId: "user-123",
  sessionId: session.id,
  rewindBeforeInvocationId: "inv-456",
});

// Session now excludes events from invocation "inv-456" onwards
Rewinding is useful for implementing “undo” functionality or branching conversations.

Best Practices

  1. Session IDs: Use meaningful session IDs (e.g., user-123_chat-20240301) for debugging.
  2. State Scoping: Use appropriate prefixes:
    • app: for application-wide configuration
    • user: for user preferences and data
    • temp: for transient session data
  3. Event Limits: Implement event compaction to prevent excessive context length.
  4. Database Indexes: Index sessions by (appName, userId, id) for fast lookups.
  5. Session Cleanup: Periodically delete inactive sessions to manage storage costs.
  6. Security: Never expose raw session IDs to clients; use signed tokens or user authentication.
  • Agents - Learn how agents interact with sessions
  • Memory - Implement long-term knowledge storage
  • Flows - Understand how sessions flow through request processing

Build docs developers (and LLMs) love