ReadonlyContext
Safe read-only access to basic invocation information for instruction providers and monitoring
ReadonlyContext provides safe, read-only access to invocation information without modification capabilities. It's designed for security-first scenarios where you need to observe execution state but shouldn't alter it.
Overview
ReadonlyContext is the base context class that provides essential invocation information in a read-only format. It's primarily used for instruction providers, template rendering, and monitoring scenarios where safety is paramount.
import { ReadonlyContext } from "@iqai/adk";
Properties
userContent
The original user message that started the current invocation.
get userContent(): Content | undefined
Returns undefined
if the invocation wasn't triggered by user content (e.g., system-initiated operations).
invocationId
Unique identifier for the current invocation, useful for tracking and logging.
get invocationId(): string
agentName
Name of the agent currently executing within this context.
get agentName(): string
state
Read-only view of the current session state.
get state(): Readonly<Record<string, any>>
The state object is frozen to prevent accidental modifications and returns a snapshot of the current session state.
Common Use Cases
Instruction Providers
ReadonlyContext is perfect for generating dynamic instructions based on current execution state:
import { LlmAgent, ReadonlyContext } from "@iqai/adk";
const dynamicInstruction = (ctx: ReadonlyContext): string => {
const userName = ctx.state.user?.name || "User";
const sessionCount = ctx.state.sessionCount || 0;
return `You are helping ${userName}. This is session #${sessionCount + 1}.
Invocation ID: ${ctx.invocationId}`;
};
const agent = new LlmAgent({
name: "adaptive-agent",
description: "An agent with dynamic instructions",
model: "gemini-2.5-flash",
instruction: dynamicInstruction,
});
Template Rendering
Use ReadonlyContext to render templates based on current state:
function renderTemplate(ctx: ReadonlyContext): string {
const template = `
Hello! I'm ${ctx.agentName}.
Current session state: ${JSON.stringify(ctx.state, null, 2)}
Processing invocation: ${ctx.invocationId}
`;
return template.trim();
}
Monitoring and Logging
ReadonlyContext provides safe access for monitoring operations:
function logInvocationInfo(ctx: ReadonlyContext): void {
console.log(`[${ctx.invocationId}] Agent: ${ctx.agentName}`);
console.log(`[${ctx.invocationId}] State keys:`, Object.keys(ctx.state));
if (ctx.userContent) {
const textParts = ctx.userContent.parts
?.filter(part => part.text)
.map(part => part.text);
console.log(`[${ctx.invocationId}] User message:`, textParts?.join(' '));
}
}
Security Benefits
Immutable State Access
ReadonlyContext ensures that state cannot be accidentally modified:
function safeStateAccess(ctx: ReadonlyContext) {
// This works - reading state
const userPreferences = ctx.state.preferences;
// This would throw an error - state is frozen
// ctx.state.newProperty = "value"; // TypeError: Cannot add property
}
Prevention of Side Effects
By providing only read access, ReadonlyContext prevents unintended side effects in observation code:
// Safe - no risk of modifying execution state
const analyzeState = (ctx: ReadonlyContext) => {
return {
agentName: ctx.agentName,
stateSize: Object.keys(ctx.state).length,
hasUserContent: !!ctx.userContent,
invocationId: ctx.invocationId
};
};
Advanced Patterns
Conditional Instruction Generation
Generate instructions based on state conditions:
const conditionalInstruction = (ctx: ReadonlyContext): string => {
const isFirstTime = !ctx.state.previousInteractions;
const userLevel = ctx.state.userLevel || "beginner";
if (isFirstTime) {
return "Welcome! I'll provide detailed explanations as this is your first interaction.";
}
if (userLevel === "expert") {
return "I'll provide concise, technical responses suitable for your expertise level.";
}
return "I'll adapt my responses based on our previous interactions.";
};
State Validation for Instructions
Validate state before generating instructions:
const validatedInstruction = (ctx: ReadonlyContext): string => {
// Validate required state properties
if (!ctx.state.userProfile) {
return "Please set up your user profile first by sharing your preferences.";
}
if (!ctx.state.currentTask) {
return "I'm ready to help! What would you like to work on?";
}
const task = ctx.state.currentTask;
return `Continuing with your ${task.type} task: "${task.name}". How can I assist?`;
};
Multi-Language Support
Use state information for localization:
const localizedInstruction = (ctx: ReadonlyContext): string => {
const language = ctx.state.userLanguage || "en";
const userName = ctx.state.user?.name || "User";
const greetings = {
en: `Hello ${userName}! How can I help you today?`,
es: `¡Hola ${userName}! ¿Cómo puedo ayudarte hoy?`,
fr: `Bonjour ${userName}! Comment puis-je vous aider aujourd'hui?`,
};
return greetings[language] || greetings.en;
};
Best Practices
State Access Patterns
// Good - Safe state access with fallbacks
const safeAccess = (ctx: ReadonlyContext) => {
const userPrefs = ctx.state.preferences || {};
const theme = userPrefs.theme || "default";
return theme;
};
// Good - Check for property existence
const conditionalAccess = (ctx: ReadonlyContext) => {
if ("activeTask" in ctx.state) {
return `Working on: ${ctx.state.activeTask.name}`;
}
return "No active task";
};
Error Handling
const robustInstruction = (ctx: ReadonlyContext): string => {
try {
const config = ctx.state.agentConfig;
if (!config) {
return "I'm ready to help! Please let me know what you'd like to do.";
}
return `I'm configured for ${config.mode} mode. ${config.description}`;
} catch (error) {
console.warn("Error accessing state in instruction provider:", error);
return "I'm here to help! What can I do for you?";
}
};
Performance Considerations
// Good - Minimal state access
const efficientInstruction = (ctx: ReadonlyContext): string => {
const { userLevel, currentMode } = ctx.state;
return `Mode: ${currentMode || "standard"}, Level: ${userLevel || "intermediate"}`;
};
// Avoid - Expensive operations in instruction providers
const inefficientInstruction = (ctx: ReadonlyContext): string => {
// Don't do complex processing in instruction providers
const allStateKeys = Object.keys(ctx.state);
const sortedKeys = allStateKeys.sort();
const summary = sortedKeys.map(key => `${key}: ${ctx.state[key]}`).join(", ");
return `Full state: ${summary}`;
};
Limitations
ReadonlyContext intentionally provides limited functionality:
- No State Modification: Cannot change session state
- No Service Access: No access to artifact, memory, or session services
- No Event Actions: Cannot trigger actions or modify execution flow
For scenarios requiring these capabilities, use CallbackContext, ToolContext, or InvocationContext as appropriate.
Related Contexts
- CallbackContext: When you need to modify state or manage artifacts
- ToolContext: For tool implementations requiring memory search and enhanced features
- InvocationContext: For complete framework access in agent implementations