TypeScriptADK-TS
Context

ToolContext

Enhanced context for tool execution with memory search and artifact management

ToolContext extends CallbackContext with additional capabilities specifically designed for tool implementations. It provides memory search, artifact listing, and enhanced event action management.

Overview

ToolContext is the most capable context available for tool implementations, providing access to memory services, enhanced artifact operations, and function call tracking.

import { ToolContext } from "@iqai/adk";

Key Features

  • All CallbackContext Features: Mutable state, artifact load/save operations
  • Memory Search: Query long-term memory stores and knowledge bases
  • Artifact Listing: Enumerate available artifacts in the session
  • Function Call Tracking: Link tool executions to originating LLM function calls
  • Enhanced Event Actions: Direct access to action management via actions property

Properties

functionCallId

Unique identifier linking this tool execution to the LLM function call:

functionCallId?: string

This ID helps correlate tool responses with their originating function calls for debugging and event tracking.

actions

Direct access to event actions for flow control:

get actions(): EventActions

Provides convenient access to eventActions with a shorter property name.

Enhanced Methods

listArtifacts()

Lists all artifact filenames attached to the current session:

async listArtifacts(): Promise<string[]>
async function examineSessionArtifacts(toolContext: ToolContext) {
  const artifacts = await toolContext.listArtifacts();

  console.log(`Found ${artifacts.length} artifacts:`);
  artifacts.forEach(filename => {
    console.log(`- ${filename}`);
  });

  // Save artifact inventory to state
  toolContext.state.artifactInventory = {
    count: artifacts.length,
    files: artifacts,
    lastChecked: new Date().toISOString()
  };

  return artifacts;
}

searchMemory()

Search the memory service for relevant information:

async searchMemory(query: string): Promise<SearchMemoryResponse>
async function findRelevantContext(toolContext: ToolContext, userQuery: string) {
  try {
    const memoryResults = await toolContext.searchMemory(userQuery);

    // Process search results
    const relevantMemories = memoryResults.memories || [];

    // Update state with findings
    toolContext.state.lastMemorySearch = {
      query: userQuery,
      resultCount: relevantMemories.length,
      timestamp: new Date().toISOString()
    };

    return relevantMemories;
  } catch (error) {
    console.error("Memory search failed:", error);
    toolContext.state.memorySearchError = {
      query: userQuery,
      error: error instanceof Error ? error.message : String(error),
      timestamp: new Date().toISOString()
    };
    return [];
  }
}

Memory search requires a memory service to be configured in the runner or agent setup.

Tool Implementation Patterns

Basic Tool Structure

Most tools using ToolContext follow this pattern:

import { BaseTool, ToolContext } from "@iqai/adk";

export class MyTool extends BaseTool {
  constructor() {
    super({
      name: "my_tool",
      description: "Example tool using ToolContext"
    });
  }

  async runAsync(args: any, context: ToolContext): Promise<any> {
    // Your tool implementation here
    return await this.processWithContext(args, context);
  }

  private async processWithContext(args: any, context: ToolContext) {
    // Use context features
    const artifacts = await context.listArtifacts();
    const memories = await context.searchMemory(args.query);

    // Update state
    context.state.toolExecution = {
      toolName: this.name,
      functionCallId: context.functionCallId,
      timestamp: new Date().toISOString()
    };

    return { success: true };
  }
}

Memory-Enhanced Tool

Create tools that leverage memory for enhanced responses:

import { FunctionTool, ToolContext } from "@iqai/adk";

async function researchTopic(
  params: { topic: string; depth: "basic" | "detailed" },
  toolContext: ToolContext
) {
  // Search memory for existing knowledge
  const existingKnowledge = await toolContext.searchMemory(params.topic);

  // Check for cached research
  const cacheKey = `research_${params.topic}_${params.depth}`;
  const cachedResearch = await toolContext.loadArtifact(`${cacheKey}.json`);

  if (cachedResearch) {
    toolContext.state.cacheHit = true;
    return JSON.parse(cachedResearch.text || '{}');
  }

  // Perform new research combining memory and external sources
  const research = {
    topic: params.topic,
    depth: params.depth,
    existingKnowledge: existingKnowledge.memories || [],
    newFindings: [], // Your research logic here
    timestamp: new Date().toISOString()
  };

  // Cache the results
  await toolContext.saveArtifact(`${cacheKey}.json`, {
    text: JSON.stringify(research, null, 2)
  });

  // Update search context in state
  toolContext.state.lastResearch = {
    topic: params.topic,
    knowledgeFound: existingKnowledge.memories?.length || 0,
    cached: false
  };

  return research;
}

const researchTool = new FunctionTool(researchTopic, {
  name: "research_topic",
  description: "Research a topic using memory and external sources"
});

Artifact Processing Tool

Tools that work with multiple artifacts:

async function processDocuments(
  params: { operation: "summarize" | "analyze" | "compare" },
  toolContext: ToolContext
) {
  // Get all available documents
  const allArtifacts = await toolContext.listArtifacts();
  const documents = allArtifacts.filter(name =>
    name.endsWith('.txt') || name.endsWith('.md') || name.endsWith('.json')
  );

  if (documents.length === 0) {
    return { error: "No documents found to process" };
  }

  const results = [];

  for (const docName of documents) {
    const document = await toolContext.loadArtifact(docName);
    if (!document?.text) continue;

    let result;
    switch (params.operation) {
      case "summarize":
        result = { summary: document.text.substring(0, 200) + "..." };
        break;
      case "analyze":
        result = {
          wordCount: document.text.split(' ').length,
          paragraphs: document.text.split('\n\n').length
        };
        break;
      case "compare":
        // Compare with memory knowledge
        const related = await toolContext.searchMemory(document.text.substring(0, 100));
        result = { relatedMemories: related.memories?.length || 0 };
        break;
    }

    results.push({
      document: docName,
      result: result
    });
  }

  // Save processing results
  const reportName = `processing_report_${Date.now()}.json`;
  await toolContext.saveArtifact(reportName, {
    text: JSON.stringify({
      operation: params.operation,
      processedDocuments: results,
      timestamp: new Date().toISOString()
    }, null, 2)
  });

  // Update state
  toolContext.state.lastProcessing = {
    operation: params.operation,
    documentsProcessed: results.length,
    reportFile: reportName
  };

  return {
    operation: params.operation,
    processedCount: results.length,
    results: results,
    reportFile: reportName
  };
}

Event Action Management

Flow Control Actions

ToolContext provides direct access to flow control through the actions property:

async function smartExitTool(
  params: { condition: string },
  toolContext: ToolContext
) {
  // Check condition against memory and state
  const memoryCheck = await toolContext.searchMemory(params.condition);
  const stateCheck = toolContext.state.exitConditions?.[params.condition];

  if (memoryCheck.memories?.length > 0 || stateCheck) {
    // Exit the current loop/flow
    toolContext.actions.escalate = true;

    return {
      action: "exit",
      reason: "Condition met",
      condition: params.condition
    };
  }

  return {
    action: "continue",
    condition: params.condition,
    status: "not_met"
  };
}

Agent Transfer

Tools can transfer control to other agents:

async function intelligentTransfer(
  params: { userQuery: string },
  toolContext: ToolContext
) {
  // Search memory for best agent for this query
  const agentSuggestions = await toolContext.searchMemory(`agent for ${params.userQuery}`);

  // Analyze current state
  const currentAgent = toolContext.agentName;
  const userHistory = toolContext.state.userHistory || [];

  // Determine best agent based on memory and context
  let targetAgent = "general_assistant"; // default

  if (params.userQuery.includes("code")) {
    targetAgent = "code_assistant";
  } else if (params.userQuery.includes("research")) {
    targetAgent = "research_assistant";
  }

  // Transfer to the determined agent
  toolContext.actions.transferToAgent = targetAgent;

  // Update state with transfer info
  toolContext.state.lastTransfer = {
    from: currentAgent,
    to: targetAgent,
    reason: params.userQuery,
    timestamp: new Date().toISOString()
  };

  return {
    action: "transfer",
    fromAgent: currentAgent,
    toAgent: targetAgent,
    reason: "Specialized agent better suited for this query"
  };
}

Advanced Use Cases

Multi-Source Information Synthesis

Combine memory, artifacts, and state for comprehensive responses:

async function synthesizeInformation(
  params: { topic: string },
  toolContext: ToolContext
) {
  // Gather information from all sources
  const [memories, artifacts, state] = await Promise.all([
    toolContext.searchMemory(params.topic),
    toolContext.listArtifacts(),
    Promise.resolve(toolContext.state)
  ]);

  // Load relevant artifacts
  const relevantArtifacts = artifacts.filter(name =>
    name.toLowerCase().includes(params.topic.toLowerCase())
  );

  const artifactContents = await Promise.all(
    relevantArtifacts.map(async name => ({
      name,
      content: await toolContext.loadArtifact(name)
    }))
  );

  // Synthesize information
  const synthesis = {
    topic: params.topic,
    sources: {
      memories: memories.memories?.length || 0,
      artifacts: artifactContents.length,
      stateReferences: Object.keys(state).filter(key =>
        key.toLowerCase().includes(params.topic.toLowerCase())
      ).length
    },
    summary: `Found information about ${params.topic} from multiple sources`,
    details: {
      memoryInsights: memories.memories?.slice(0, 3) || [],
      relevantFiles: relevantArtifacts,
      stateData: state[params.topic] || null
    }
  };

  // Save comprehensive report
  await toolContext.saveArtifact(`synthesis_${params.topic}_${Date.now()}.json`, {
    text: JSON.stringify(synthesis, null, 2)
  });

  return synthesis;
}

Context-Aware Decision Making

Use all available context for intelligent decisions:

async function makeContextualDecision(
  params: { decision: string; options: string[] },
  toolContext: ToolContext
) {
  // Gather contextual information
  const userHistory = toolContext.state.userHistory || [];
  const recentChoices = toolContext.state.recentChoices || [];
  const memoryGuidance = await toolContext.searchMemory(
    `decision ${params.decision} options ${params.options.join(' ')}`
  );

  // Analyze patterns
  const patterns = {
    userPreference: analyzeUserPreferences(userHistory, params.options),
    pastChoices: recentChoices.slice(-5),
    memoryInsights: memoryGuidance.memories || []
  };

  // Make intelligent choice
  const recommendedOption = patterns.userPreference || params.options[0];

  // Update decision history
  toolContext.state.recentChoices = [
    ...recentChoices.slice(-9), // Keep last 9
    {
      decision: params.decision,
      recommended: recommendedOption,
      timestamp: new Date().toISOString(),
      functionCallId: toolContext.functionCallId
    }
  ];

  return {
    decision: params.decision,
    recommended: recommendedOption,
    reasoning: patterns,
    confidence: calculateConfidence(patterns)
  };
}

function analyzeUserPreferences(history: any[], options: string[]): string | null {
  // Analyze user history to determine preferences
  // Implementation depends on your specific needs
  return null;
}

function calculateConfidence(patterns: any): number {
  // Calculate confidence score based on available patterns
  return 0.7; // Placeholder
}

Best Practices

Error Handling

async function robustToolExecution(
  params: any,
  toolContext: ToolContext
) {
  try {
    // Attempt operations with fallbacks
    let result;

    try {
      const memories = await toolContext.searchMemory(params.query);
      result = processWithMemory(memories);
    } catch (memoryError) {
      console.warn("Memory search failed, using local processing:", memoryError);
      result = processLocally(params);
    }

    // Save successful result
    await toolContext.saveArtifact("operation_result.json", {
      text: JSON.stringify(result, null, 2)
    });

    return result;

  } catch (error) {
    // Log error and update state
    toolContext.state.lastError = {
      tool: "robustToolExecution",
      error: error instanceof Error ? error.message : String(error),
      timestamp: new Date().toISOString(),
      functionCallId: toolContext.functionCallId
    };

    return { error: "Operation failed", details: String(error) };
  }
}

function processWithMemory(memories: any) {
  // Implementation with memory results
  return { source: "memory", data: memories };
}

function processLocally(params: any) {
  // Fallback local processing
  return { source: "local", data: params };
}

Resource Optimization

async function optimizedToolExecution(
  params: any,
  toolContext: ToolContext
) {
  // Check cache first
  const cacheKey = `tool_cache_${JSON.stringify(params)}`;
  const cached = await toolContext.loadArtifact(cacheKey);

  if (cached) {
    toolContext.state.cacheHits = (toolContext.state.cacheHits || 0) + 1;
    return JSON.parse(cached.text || '{}');
  }

  // Batch operations when possible
  const [artifacts, memories] = await Promise.all([
    toolContext.listArtifacts(),
    toolContext.searchMemory(params.query || "")
  ]);

  // Process efficiently
  const result = {
    artifacts: artifacts.length,
    memories: memories.memories?.length || 0,
    processed: true
  };

  // Cache result
  await toolContext.saveArtifact(cacheKey, {
    text: JSON.stringify(result, null, 2)
  });

  return result;
}
  • CallbackContext: Base class providing state management and artifact operations
  • ReadonlyContext: For read-only access without modification capabilities
  • InvocationContext: For complete framework access in agent implementations