TypeScriptADK-TS
Context

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.

  • 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