Sessions represent active conversations between users and agents. They track events, manage state, and provide a foundation for all agent interactions in ADK-TS.
Session Lifecycle
BaseSessionService
All session services extend the abstract BaseSessionService class:
// packages/adk/src/sessions/base-session-service.ts:27
export abstract class BaseSessionService {
abstract createSession (
appName : string ,
userId : string ,
state ?: Record < string , any >,
sessionId ?: string ,
) : Promise < Session >;
abstract getSession (
appName : string ,
userId : string ,
sessionId : string ,
config ?: GetSessionConfig ,
) : Promise < Session | undefined >;
abstract listSessions (
appName : string ,
userId : string ,
) : Promise < ListSessionsResponse >;
abstract deleteSession (
appName : string ,
userId : string ,
sessionId : string ,
) : Promise < void >;
abstract endSession (
appName : string ,
userId : string ,
sessionId : string ,
) : Promise < Session | undefined >;
async appendEvent ( session : Session , event : Event ) : Promise < Event >;
}
Session Structure
A session contains all conversation data:
// packages/adk/src/sessions/session.ts:6
interface Session {
id : string ; // Unique session identifier
appName : string ; // Application name
userId : string ; // User identifier
state : Record < string , any >; // Session state variables
events : Event []; // All conversation events
lastUpdateTime : number ; // Unix timestamp (seconds)
}
Creating Sessions
Basic Creation
With AgentBuilder
Resume Existing
import { InMemorySessionService } from '@iqai/adk' ;
const sessionService = new InMemorySessionService ();
const session = await sessionService . createSession (
'my-app' , // App name
'user-123' , // User ID
{ // Initial state (optional)
counter: 0 ,
language: 'en' ,
},
'custom-id' // Custom session ID (optional)
);
console . log ( 'Session ID:' , session . id );
console . log ( 'Initial state:' , session . state );
import { AgentBuilder , InMemorySessionService } from '@iqai/adk' ;
const sessionService = new InMemorySessionService ();
const { runner , session } = await AgentBuilder
. withModel ( 'gpt-4' )
. withSessionService ( sessionService )
. build ({
userId: 'user-123' ,
sessionId: 'optional-custom-id' ,
});
// Session created automatically
console . log ( 'Session:' , session . id );
import { AgentBuilder } from '@iqai/adk' ;
// Resume an existing session
const { runner , session } = await AgentBuilder
. withModel ( 'gpt-4' )
. withSessionService ( sessionService )
. build ({
userId: 'user-123' ,
sessionId: 'existing-session-id' ,
});
// Continues from previous state
console . log ( 'Events:' , session . events . length );
console . log ( 'State:' , session . state );
Session Services
InMemorySessionService
DatabaseSessionService
Example Setup
Simple in-memory storage for development: // packages/adk/src/sessions/in-memory-session-service.ts:14
import { InMemorySessionService } from '@iqai/adk' ;
const sessionService = new InMemorySessionService ();
const agent = await AgentBuilder
. withModel ( 'gpt-4' )
. withSessionService ( sessionService )
. build ();
Features:
Fast and lightweight
No external dependencies
Data lost on restart
Perfect for testing and development
Storage structure: Map<appName, Map<userId, Map<sessionId, Session>>>
└─ app-level state: Map<appName, Map<key, value>>
└─ user-level state: Map<appName, Map<userId, Map<key, value>>>
All session data is lost when the process exits. Use for development only.
Production-ready database persistence: // packages/adk/src/sessions/database-session-service.ts:76
import { createDatabaseSessionService } from '@iqai/adk' ;
const sessionService = createDatabaseSessionService (
'postgresql://user:pass@host:5432/db'
);
// Also supports:
// - SQLite: 'sqlite:///path/to/db.sqlite'
// - MySQL: 'mysql://user:pass@host:3306/db'
Features:
Automatic table creation
Transaction support
Multi-level state (app/user/session)
Session rewind capability
Production-ready
Database schema: CREATE TABLE sessions (
id VARCHAR ( 128 ),
app_name VARCHAR ( 128 ),
user_id VARCHAR ( 128 ),
state TEXT ,
create_time TIMESTAMP ,
update_time TIMESTAMP ,
PRIMARY KEY (app_name, user_id, id)
);
CREATE TABLE events (
id VARCHAR ( 128 ),
app_name VARCHAR ( 128 ),
user_id VARCHAR ( 128 ),
session_id VARCHAR ( 128 ),
invocation_id VARCHAR ( 256 ),
author VARCHAR ( 256 ),
timestamp TIMESTAMP ,
content TEXT ,
actions TEXT ,
PRIMARY KEY (id, app_name, user_id, session_id),
FOREIGN KEY (app_name, user_id, session_id)
REFERENCES sessions (app_name, user_id, id)
);
CREATE TABLE app_states (
app_name VARCHAR ( 128 ) PRIMARY KEY ,
state TEXT ,
update_time TIMESTAMP
);
CREATE TABLE user_states (
app_name VARCHAR ( 128 ),
user_id VARCHAR ( 128 ),
state TEXT ,
update_time TIMESTAMP ,
PRIMARY KEY (app_name, user_id)
);
Complete example from the examples: // apps/examples/src/04-persistence-and-sessions/agents/agent.ts:11
import * as path from 'node:path' ;
import * as os from 'node:os' ;
import { createDatabaseSessionService } from '@iqai/adk' ;
// Create SQLite database in temp directory
const dbDir = path . join ( os . tmpdir (), 'adk-examples' );
const dbPath = path . join ( dbDir , 'sessions.db' );
const connectionString = `sqlite:// ${ dbPath } ` ;
const sessionService = createDatabaseSessionService ( connectionString );
const { runner , session } = await AgentBuilder
. withModel ( 'gemini-3-flash-preview' )
. withSessionService ( sessionService )
. withEventsCompaction ({
compactionInterval: 3 , // Compact every 3 events
overlapSize: 1 , // Keep 1 event for context
})
. build ();
Managing Sessions
Create a Session
const session = await sessionService . createSession (
'my-app' ,
'user-123' ,
{ counter: 0 }
);
Append Events
const event = {
id: 'evt-1' ,
invocationId: 'inv-1' ,
author: 'user' ,
timestamp: Date . now () / 1000 ,
content: { parts: [{ text: 'Hello' }] },
};
await sessionService . appendEvent ( session , event );
Get Session
const retrieved = await sessionService . getSession (
'my-app' ,
'user-123' ,
session . id ,
{
numRecentEvents: 10 , // Only get last 10 events
afterTimestamp: 1234567890 , // Only events after timestamp
}
);
List User Sessions
const { sessions } = await sessionService . listSessions (
'my-app' ,
'user-123'
);
for ( const s of sessions ) {
console . log ( ` ${ s . id } : ${ new Date ( s . lastUpdateTime * 1000 ) } ` );
}
End Session
// Get final state before cleanup
const finalSession = await sessionService . endSession (
'my-app' ,
'user-123' ,
session . id
);
// Save to long-term memory if needed
await memoryService . addSessionToMemory ( finalSession );
Delete Session
await sessionService . deleteSession (
'my-app' ,
'user-123' ,
session . id
);
Event Structure
Events represent individual interactions:
interface Event {
id : string ; // Unique event ID
invocationId : string ; // Groups related events
author : string ; // 'user' or 'model'
branch ?: string ; // For branching conversations
timestamp : number ; // Unix seconds
// Content (user input or model response)
content ?: Content ;
// Actions (tool calls, state changes)
actions ?: {
stateDelta ?: Record < string , any >; // State changes
functionCalls ?: FunctionCall []; // Tool invocations
functionResponses ?: FunctionResponse []; // Tool results
compaction ?: CompactionData ; // Event summarization
};
// Metadata
partial ?: boolean ; // Streaming in progress
turnComplete ?: boolean ; // Response complete
errorCode ?: string ; // Error type
errorMessage ?: string ; // Error details
interrupted ?: boolean ; // User interrupted
longRunningToolIds ?: Set < string >; // Async tools in progress
groundingMetadata ?: any ; // RAG metadata
}
Session Rewind
Travel back in time by reverting to a previous state:
// apps/examples/src/04-persistence-and-sessions/index.ts:32
import { AgentBuilder } from '@iqai/adk' ;
// Make some changes
await runner . ask ( 'Increment counter by 100' );
// Get the invocation ID of the change
const invocationId = getLastInvocationIdWithStateDelta ( session );
// Rewind to before that change
await runner . rewind ({
userId: 'user-123' ,
sessionId: session . id ,
rewindBeforeInvocationId: invocationId ,
});
// State is now as if the increment never happened
const rewoundSession = await sessionService . getSession (
'my-app' ,
'user-123' ,
session . id
);
console . log ( 'Rewound state:' , rewoundSession . state );
Rewind removes all events and state changes that occurred at or after the specified invocation ID.
Finding Invocation IDs
// Get the last invocation that changed state
function getLastInvocationIdWithStateDelta ( session : Session ) : string | undefined {
for ( let i = session . events . length - 1 ; i >= 0 ; i -- ) {
const event = session . events [ i ];
if ( event . actions ?. stateDelta &&
Object . keys ( event . actions . stateDelta ). length > 0 ) {
return event . invocationId ;
}
}
return undefined ;
}
Events Compaction
Automatically summarize old events to save context window:
const { runner } = await AgentBuilder
. withModel ( 'gpt-4' )
. withSessionService ( sessionService )
. withEventsCompaction ({
compactionInterval: 5 , // Compact every 5 events
overlapSize: 2 , // Keep 2 recent events uncompacted
})
. build ();
How it works:
After N events, old events are summarized
Summary stored as a compaction event
Original events can be removed
Reduces context window usage
Maintains conversation continuity
State Management Integration
Sessions automatically track state changes. See State Management for details:
// State changes are captured in events
const event = {
id: 'evt-1' ,
invocationId: 'inv-1' ,
author: 'model' ,
timestamp: Date . now () / 1000 ,
actions: {
stateDelta: {
counter: 5 , // Session state
'user:name' : 'Alice' , // User state
'app:version' : '1.0' , // App state
},
},
};
await sessionService . appendEvent ( session , event );
// State automatically updated
console . log ( session . state . counter ); // 5
console . log ( session . state [ 'user:name' ]); // 'Alice'
Best Practices
Create sessions at conversation start
Use consistent appName and userId
Call endSession() before saving to memory
Clean up old sessions periodically
Don’t reuse sessions across different contexts
Use appropriate state scope (session/user/app)
Keep state minimal (only what you need)
Use temp: prefix for transient data
Validate state before use
Document your state schema
Check for stale sessions (lastUpdateTime)
Handle concurrent updates
Validate session exists before operations
Use rewind for error recovery
Log state changes for debugging
Complete Example
import {
AgentBuilder ,
createDatabaseSessionService ,
InMemoryArtifactService ,
} from '@iqai/adk' ;
// Setup
const sessionService = createDatabaseSessionService (
'postgresql://localhost/mydb'
);
const artifactService = new InMemoryArtifactService ();
// Create agent with session management
const { runner , session , sessionService : svc } = await AgentBuilder
. withModel ( 'gpt-4' )
. withInstruction ( 'You are a helpful assistant with state.' )
. withSessionService ( sessionService )
. withArtifactService ( artifactService )
. withEventsCompaction ({
compactionInterval: 10 ,
overlapSize: 2 ,
})
. build ({
userId: 'user-123' ,
});
// Interact
await runner . ask ( 'Set my name to Alice' );
await runner . ask ( 'What is my name?' );
// List sessions
const { sessions } = await svc . listSessions ( 'my-app' , 'user-123' );
console . log ( 'User has' , sessions . length , 'sessions' );
// End and cleanup
const finalSession = await svc . endSession (
session . appName ,
session . userId ,
session . id
);
// Optional: save to memory
if ( memoryService ) {
await memoryService . addSessionToMemory ( finalSession );
}
// Delete if no longer needed
await svc . deleteSession ( session . appName , session . userId , session . id );