TypeScriptADK-TS
Sessions

State Management

Handle session-specific data and cross-session information with flexible state scoping

State management provides agents with flexible storage for dynamic information during conversations. While session events hold the complete interaction history, state manages key-value data needed to personalize interactions, track progress, and share information across different scopes.

What is Session State?

Session state acts as the agent's structured memory system, organizing information by scope and persistence requirements to make interactions effective and context-aware.

Core Purposes

  • Personalization: Remember user preferences and settings across sessions
  • Progress Tracking: Monitor steps in multi-turn processes and workflows
  • Information Sharing: Share data between sessions, users, and application components
  • Temporary Storage: Handle intermediate calculations and processing data

Scoped Memory

Think of session state as organized filing cabinets - session-specific drawers, user-specific drawers, application-wide drawers, and temporary scratch paper that gets discarded.

State Structure and Characteristics

Key-Value Organization

State is organized as serializable key-value pairs with scope-based prefixes:

  • Keys: Always strings with descriptive names and optional scope prefixes
  • Values: Must be JSON-serializable (strings, numbers, booleans, simple arrays/objects)
  • Mutability: Content changes as conversations evolve
  • Persistence: Depends on SessionService implementation and scope prefix

Serialization Requirements

Serialization Constraints

Store only JSON-serializable data in state. Avoid complex objects, class instances, functions, or connections. Use simple identifiers to reference complex objects stored elsewhere.

Supported Types:

  • Primitive types (string, number, boolean)
  • Simple arrays and objects
  • JSON-serializable data structures
  • Basic nested objects with primitive values

Avoid:

  • Custom class instances
  • Functions and closures
  • Database connections
  • Complex object references

State Scoping with Prefixes

State keys use prefixes to define scope and persistence behavior, enabling different levels of data sharing across the application hierarchy.

📝 Session State

No prefix - specific to current conversation

👤 User State

user: prefix - shared across all user sessions

🌐 App State

app: prefix - global application data

⏱️ Temporary State

temp: prefix - transient data that never persists

Session State (No Prefix)

Data specific to the current conversation thread:

Characteristics:

  • Scope: Current session only
  • Persistence: Follows SessionService implementation (in-memory vs database vs cloud)
  • Use Cases: Task progress, conversation flags, temporary preferences

Examples:

session.state['current_booking_step'] = 'confirmation';
session.state['needs_clarification'] = true;
session.state['conversation_topic'] = 'travel_planning';

User State (user: prefix)

Data shared across all sessions for a specific user:

Characteristics:

  • Scope: All sessions for the same userId within appName
  • Persistence: Always persistent with database/cloud implementations
  • Use Cases: User preferences, profile information, cross-session data

Examples:

session.state['user:preferred_language'] = 'es';
session.state['user:theme_setting'] = 'dark';
session.state['user:notification_preferences'] = {
  email: true,
  sms: false,
  push: true
};

App State (app: prefix)

Global data shared across all users and sessions:

Characteristics:

  • Scope: All users and sessions within the application
  • Persistence: Always persistent with database/cloud implementations
  • Use Cases: Global settings, shared templates, system-wide configuration

Examples:

session.state['app:api_endpoint'] = 'https://api.example.com/v2';
session.state['app:feature_flags'] = {
  new_ui: true,
  beta_features: false
};
session.state['app:global_discount_code'] = 'WELCOME2024';

Temporary State (temp: prefix)

Transient data that never persists across restarts:

Characteristics:

  • Scope: Current session processing only
  • Persistence: Never persisted, always discarded after processing
  • Use Cases: Intermediate calculations, temporary API responses, processing flags

Examples:

session.state['temp:api_response'] = { status: 'success', data: {...} };
session.state['temp:calculation_result'] = 42.5;
session.state['temp:validation_status'] = 'pending';

State Access Methods

Direct Property Access

Simple dictionary-style access for basic operations:

// Get values
const theme = session.state['user:theme'];
const step = session.state['current_step'];

// Set values (direct modification - see warnings below)
session.state['current_step'] = 'confirmation';
session.state['user:preference'] = 'dark_mode';

Session State Object Methods

The actual implementation provides additional convenience methods:

// Get with default values
const theme = session.state.get?.('user:theme', 'light') || session.state['user:theme'] || 'light';

// Check existence
const hasPreferences = session.state.hasOwnProperty('user:preferences');

// Bulk operations via state delta (recommended)
const stateDelta = {
  'current_step': 'payment',
  'user:last_action': 'checkout',
  'temp:processing': true
};

State Update Patterns

State should be updated through the event system for proper tracking and persistence:

import { Event, EventActions } from '@iqai/adk';

// Create state changes through event actions
const stateDelta = {
  'user:preferred_language': 'es',
  'current_step': 'payment',
  'temp:processing_time': Date.now()
};

const event = new Event({
  author: 'user',
  actions: new EventActions({
    stateDelta: stateDelta
  }),
  content: { parts: [{ text: 'User updated preferences' }] },
  timestamp: Date.now() / 1000
});

// Apply through session service for proper persistence
await sessionService.appendEvent(session, event);

Benefits:

  • Auditability: Changes recorded in event history
  • Thread Safety: Atomic updates prevent race conditions
  • Persistence: Guaranteed storage with persistent services
  • Consistency: Proper timestamp and metadata tracking
  • Prefix Handling: Automatic scope separation in database implementations

Direct Modification Limitations

While possible, direct state modification has significant drawbacks:

// ❌ Direct modification (not recommended for production)
session.state['user:preference'] = 'new_value';

Limitations:

  • No Event History: Changes not recorded for audit trails
  • Persistence Issues: May not save properly with persistent services
  • Thread Safety: Risk of race conditions in concurrent scenarios
  • Missing Metadata: No timestamp or context tracking
  • Prefix Processing: Database services may not handle scope prefixes correctly

Recommended Update Method

For persistent, traceable state changes, use EventActions.stateDelta within the appendEvent flow rather than direct modification.

State Persistence Behavior

In-Memory Implementation

import { InMemorySessionService } from '@iqai/adk';

const sessionService = new InMemorySessionService();
  • Storage: Application memory only
  • Persistence: None - lost on restart
  • Prefix Handling: All prefixes maintained in memory with proper scoping
  • Performance: Fastest access and updates
  • Use Cases: Development, testing, temporary sessions

Database Implementation

import { DatabaseSessionService } from '@iqai/adk';
import { Kysely } from 'kysely';

const sessionService = new DatabaseSessionService({
  db: database, // Your configured Kysely instance
  skipTableCreation: false // Let service create tables automatically
});
  • Storage: SQLite, PostgreSQL, MySQL, or other supported databases
  • Persistence: Survives application restarts
  • Prefix Handling: Automatic separation into app_states, user_states, and sessions tables
  • Transactions: ACID compliance for data consistency
  • Backup: Standard database backup and recovery

Cloud Implementation

import { VertexAiSessionService } from '@iqai/adk';

const sessionService = new VertexAiSessionService({
  project: 'your-project',
  location: 'us-central1',
  agentEngineId: 'your-agent-engine-id'
});
  • Storage: Google Cloud Vertex AI Agent Engine infrastructure
  • Persistence: Enterprise-grade reliability and backup
  • Prefix Handling: Managed by Google Cloud services
  • Scalability: Automatic scaling and distribution
  • Integration: Native integration with other Google Cloud services

Prefix Processing Internals

Database Service Prefix Handling

The DatabaseSessionService automatically separates state by prefixes:

// Input state with prefixes
const stateDelta = {
  'app:feature_enabled': true,        // → app_states table
  'user:theme': 'dark',              // → user_states table
  'current_step': 'payment',         // → sessions table
  'temp:api_call': 'in_progress'     // → discarded (not persisted)
};

// Automatic separation:
// app_states.state: { "feature_enabled": true }
// user_states.state: { "theme": "dark" }
// sessions.state: { "current_step": "payment" }

State Merging on Retrieval

When retrieving sessions, all state scopes are merged back together:

const session = await sessionService.getSession(appName, userId, sessionId);

// Merged state contains all scopes with prefixes restored:
console.log(session.state);
// {
//   "current_step": "payment",           // session scope
//   "app:feature_enabled": true,         // app scope
//   "user:theme": "dark"                 // user scope
// }

Performance Optimization

Access Patterns

  • Batch Operations: Group multiple state changes in single event
  • Prefix Organization: Use consistent prefix patterns for efficient queries
  • Scope Awareness: Place data in appropriate scope to minimize retrieval overhead
  • Lazy Loading: Load only necessary state data when possible

Storage Efficiency

  • Data Minimization: Store only essential information in each scope
  • Compression: Use compact data representations for large objects
  • Cleanup Policies: Remove obsolete temporary and session data regularly
  • Indexing: Database services automatically optimize queries with proper indexing

Monitoring

  • State Size Tracking: Monitor total state data volume per scope
  • Access Frequency: Track read/write patterns across prefixes
  • Performance Metrics: Measure state operation latency by scope
  • Error Rates: Track state operation failures and prefix-related issues

Security and Privacy

Data Protection

  • Encryption: All persistent state encrypted at rest and in transit
  • Access Control: Scope-based access restrictions (users can only access their user: data)
  • Audit Logging: Track state access and modifications through event system
  • Data Classification: Separate sensitive data by appropriate scope prefixes

Privacy Compliance

  • Data Minimization: Store only necessary user information in user: scope
  • Retention Policies: Implement appropriate data retention periods per scope
  • User Rights: Provide mechanisms for user: scope data access and deletion
  • Cross-Border Compliance: Handle international data transfer requirements

Best Practices

  • Sensitive Data: Never store passwords or tokens in any state scope
  • User Consent: Obtain proper consent for persistent user: scope data
  • Regular Cleanup: Remove expired temp: and obsolete session data
  • Secure Defaults: Use secure configurations by default across all scopes

Common Patterns and Use Cases

E-commerce Application

Shopping Cart Management:

// Session-specific items
session.state['cart_items'] = [
  { id: 'item1', quantity: 2 },
  { id: 'item2', quantity: 1 }
];

// User preferences across sessions
session.state['user:saved_items'] = ['item3', 'item7', 'item12'];
session.state['user:shipping_address'] = {
  street: '123 Main St',
  city: 'Anytown',
  country: 'US'
};

// App-wide settings
session.state['app:shipping_rates'] = {
  standard: 5.99,
  express: 12.99
};

// Temporary calculations
session.state['temp:cart_total'] = 89.47;

Multi-Step Workflow

Form Processing:

// Progress tracking
session.state['current_step'] = 'personal_info';
session.state['completed_steps'] = ['intro', 'terms'];
session.state['form_data'] = {
  email: 'user@example.com',
  phone: '+1234567890'
};

// User workflow preferences
session.state['user:preferred_flow'] = 'express';
session.state['user:save_progress'] = true;

// Temporary validation
session.state['temp:validation_errors'] = ['phone_format'];

Personalization System

User Experience Customization:

// User-specific preferences
session.state['user:ui_preferences'] = {
  theme: 'dark',
  language: 'es',
  timezone: 'America/Los_Angeles'
};

// User interaction history
session.state['user:recent_searches'] = ['hotels paris', 'flights tokyo'];
session.state['user:interaction_count'] = 47;

// App-wide personalization settings
session.state['app:personalization_enabled'] = true;
session.state['app:supported_languages'] = ['en', 'es', 'fr', 'de'];

Best Practices

Design Principles

  • Clear Scope Assignment: Choose appropriate prefixes based on data lifecycle
  • Consistent Naming: Use descriptive, consistent key naming conventions
  • Minimal Storage: Store only essential, dynamic information
  • Type Consistency: Maintain consistent data types for similar keys across scopes

Implementation Guidelines

  • Event-Driven Updates: Always use EventActions.stateDelta for state changes
  • Error Handling: Implement robust error handling for all state operations
  • Testing Strategy: Test state behavior across different SessionService implementations
  • Documentation: Document state schema and usage patterns for your application

Production Considerations

  • Monitoring: Implement comprehensive state monitoring across all scopes
  • Backup Strategy: Ensure proper backup strategies for persistent state scopes
  • Migration Planning: Plan for state schema evolution and data migration
  • Performance Optimization: Optimize state operations for production load patterns