Skip to main content

Overview

The BaseAgent class is the foundation for all agent types in ADK-TS. It provides core functionality for agent execution, lifecycle management, sub-agent hierarchies, and callback hooks. Location: @iqai/adk package, exported from packages/adk/src/agents/base-agent.ts:27

Constructor

config
object
required
Configuration object for the agent
Configuration Properties:
config.name
string
required
The agent’s name. Must be a valid identifier (alphanumeric + underscore, starting with letter or underscore). Cannot be “user” as it’s reserved for end-user input.
config.description
string
default:"''"
Description of the agent’s capability. Used by parent agents to determine delegation. One-line descriptions are preferred.
config.subAgents
BaseAgent[]
default:"[]"
Sub-agents that this agent can delegate to. Each sub-agent can only be added once.
config.beforeAgentCallback
BeforeAgentCallback
Callback(s) invoked before agent execution. Can be a single callback or array of callbacks.
config.afterAgentCallback
AfterAgentCallback
Callback(s) invoked after agent execution. Can be a single callback or array of callbacks.
import { BaseAgent } from "@iqai/adk";

class MyAgent extends BaseAgent {
  protected async *runAsyncImpl(ctx: InvocationContext) {
    // Implementation
    yield new Event({ /* ... */ });
  }
}

const agent = new MyAgent({
  name: "my_agent",
  description: "A custom agent implementation",
  subAgents: [subAgent1, subAgent2]
});

Properties

name

name
string
The agent’s name. Must be unique within the agent tree and cannot be “user”.

description

description
string
Description of the agent’s capability used for delegation decisions.

parentAgent

parentAgent
BaseAgent | undefined
The parent agent of this agent. Automatically set when added as a sub-agent.
An agent can only be added as a sub-agent once. To reuse an agent, create multiple instances with different names.

subAgents

subAgents
BaseAgent[]
The sub-agents of this agent.

beforeAgentCallback

beforeAgentCallback
BeforeAgentCallback | undefined
Callback or list of callbacks invoked before the agent run. When a list is provided, callbacks are called in order until one returns a non-undefined value.
Callback Signature:
type SingleAgentCallback = (
  callbackContext: CallbackContext
) => Promise<Content | undefined> | Content | undefined;

type BeforeAgentCallback = SingleAgentCallback | SingleAgentCallback[];
Returns:
  • undefined - Continue with agent execution
  • Content - Skip agent run and return this content to user

afterAgentCallback

afterAgentCallback
AfterAgentCallback | undefined
Callback or list of callbacks invoked after the agent run. When a list is provided, callbacks are called in order until one returns a non-undefined value.
Returns:
  • undefined - Use the agent’s generated response
  • Content - Replace agent response with this content

Methods

runAsync()

Entry method to run an agent via text-based conversation.
parentContext
InvocationContext
required
The parent invocation context
Returns: AsyncGenerator<Event, void, unknown>
import { InvocationContext, Event } from "@iqai/adk";

for await (const event of agent.runAsync(context)) {
  console.log('Event:', event);
  // Process streaming events
}
Location: packages/adk/src/agents/base-agent.ts:112

runLive()

Entry method to run an agent via video/audio-based conversation.
parentContext
InvocationContext
required
The parent invocation context
Returns: AsyncGenerator<Event, void, unknown>
for await (const event of agent.runLive(context)) {
  console.log('Live event:', event);
  // Process live audio/video events
}
Location: packages/adk/src/agents/base-agent.ts:125

findAgent()

Finds an agent by name in this agent and its descendants.
name
string
required
The name of the agent to find
Returns: BaseAgent | undefined
const targetAgent = rootAgent.findAgent("research_agent");
if (targetAgent) {
  console.log('Found:', targetAgent.name);
}
Location: packages/adk/src/agents/base-agent.ts:336

findSubAgent()

Finds an agent by name in this agent’s descendants (excludes self).
name
string
required
The name of the agent to find
Returns: BaseAgent | undefined
const subAgent = parentAgent.findSubAgent("writer_agent");
Location: packages/adk/src/agents/base-agent.ts:349

rootAgent (getter)

Gets the root agent of this agent by traversing up the parent chain. Returns: BaseAgent
const root = agent.rootAgent;
console.log('Root agent:', root.name);
Location: packages/adk/src/agents/base-agent.ts:322

Protected Methods

These methods should be implemented by subclasses:

runAsyncImpl()

Core logic to run this agent via text-based conversation. Must be implemented by subclasses.
ctx
InvocationContext
required
The invocation context for this agent
Returns: AsyncGenerator<Event, void, unknown>
protected async *runAsyncImpl(
  ctx: InvocationContext
): AsyncGenerator<Event, void, unknown> {
  // Custom agent logic
  const response = await this.generateResponse(ctx);
  
  yield new Event({
    invocationId: ctx.invocationId,
    author: this.name,
    branch: ctx.branch,
    content: { parts: [{ text: response }] }
  });
}
Location: packages/adk/src/agents/base-agent.ts:296

runLiveImpl()

Core logic to run this agent via video/audio-based conversation. Must be implemented by subclasses.
ctx
InvocationContext
required
The invocation context for this agent
Returns: AsyncGenerator<Event, void, unknown>
protected async *runLiveImpl(
  ctx: InvocationContext
): AsyncGenerator<Event, void, unknown> {
  // Live conversation logic
  yield new Event({ /* ... */ });
}
Location: packages/adk/src/agents/base-agent.ts:311

Callback Execution Flow

Before Agent Callbacks

  1. Plugin callbacks are executed first
  2. If no plugin returns content, canonical callbacks are executed in order
  3. Execution stops at first callback returning content
  4. Returning content skips agent execution and uses the returned content
const agent = new MyAgent({
  name: "validator",
  beforeAgentCallback: [
    // First callback - validate input
    async (ctx) => {
      if (!isValidInput(ctx.userContent)) {
        return { parts: [{ text: "Invalid input" }] };
      }
      return undefined; // Continue to next callback
    },
    // Second callback - check permissions
    async (ctx) => {
      if (!hasPermission(ctx.userId)) {
        return { parts: [{ text: "Access denied" }] };
      }
      return undefined; // Continue with agent execution
    }
  ]
});

After Agent Callbacks

  1. Plugin callbacks are executed first
  2. If no plugin returns content, canonical callbacks are executed in order
  3. Execution stops at first callback returning content
  4. Returning content replaces the agent’s response
const agent = new MyAgent({
  name: "formatter",
  afterAgentCallback: async (ctx) => {
    // Post-process agent response
    const response = getLastResponse(ctx.session.events);
    const formatted = formatMarkdown(response);
    return { parts: [{ text: formatted }] };
  }
});

Agent Hierarchy and Transfer

Agents can be organized in hierarchical structures:
import { LlmAgent } from "@iqai/adk";

// Create specialized sub-agents
const researcher = new LlmAgent({
  name: "researcher",
  description: "Researches topics using search tools",
  model: "gpt-4",
  tools: [searchTool]
});

const writer = new LlmAgent({
  name: "writer",
  description: "Writes polished content",
  model: "gpt-4"
});

const editor = new LlmAgent({
  name: "editor",
  description: "Edits and improves text",
  model: "gpt-4"
});

// Create orchestrator agent
const coordinator = new LlmAgent({
  name: "coordinator",
  description: "Coordinates content creation workflow",
  model: "gpt-4",
  subAgents: [researcher, writer, editor]
});

// Parent relationships are automatically set
console.log(researcher.parentAgent?.name); // "coordinator"
console.log(coordinator.rootAgent.name); // "coordinator"
console.log(researcher.rootAgent.name); // "coordinator"

Transfer Context Tracking

The framework automatically tracks agent transfers for telemetry:
// When an agent transfers to a sub-agent:
// - transferChain is updated: ["coordinator", "researcher"]
// - transferDepth is incremented
// - rootAgentName remains "coordinator"
// - Telemetry events are recorded

Name Validation

Agent names must follow these rules:
  • Must start with a letter (a-z, A-Z) or underscore (_)
  • Can only contain letters, digits (0-9), and underscores
  • Cannot be “user” (reserved for end-user input)
// Valid names
new MyAgent({ name: "agent_1" });     // ✓
new MyAgent({ name: "_helper" });     // ✓
new MyAgent({ name: "ResearchBot" }); // ✓

// Invalid names
new MyAgent({ name: "1agent" });      // ✗ starts with digit
new MyAgent({ name: "my-agent" });    // ✗ contains hyphen
new MyAgent({ name: "user" });        // ✗ reserved keyword

Complete Example

import { BaseAgent, InvocationContext, Event } from "@iqai/adk";

class CustomAgent extends BaseAgent {
  private apiKey: string;

  constructor(config: { name: string; apiKey: string }) {
    super({
      name: config.name,
      description: "Custom API integration agent",
      beforeAgentCallback: async (ctx) => {
        console.log(`Starting ${config.name}`);
        return undefined;
      },
      afterAgentCallback: async (ctx) => {
        console.log(`Completed ${config.name}`);
        return undefined;
      }
    });
    this.apiKey = config.apiKey;
  }

  protected async *runAsyncImpl(
    ctx: InvocationContext
  ): AsyncGenerator<Event, void, unknown> {
    // Extract user message
    const lastEvent = ctx.session.events
      .slice()
      .reverse()
      .find(e => e.author === "user");
    
    const userMessage = lastEvent?.content?.parts
      .map(p => p.text || "")
      .join("");

    // Call custom API
    const response = await this.callCustomAPI(userMessage);

    // Yield response event
    yield new Event({
      invocationId: ctx.invocationId,
      author: this.name,
      branch: ctx.branch,
      content: { parts: [{ text: response }] }
    });
  }

  private async callCustomAPI(input: string): Promise<string> {
    // Custom API logic
    return `Processed: ${input}`;
  }
}

// Usage
const agent = new CustomAgent({
  name: "custom_agent",
  apiKey: "sk-..."
});

Type Definitions

type SingleAgentCallback = (
  callbackContext: CallbackContext
) => Promise<Content | undefined> | Content | undefined;

type BeforeAgentCallback = SingleAgentCallback | SingleAgentCallback[];

type AfterAgentCallback = SingleAgentCallback | SingleAgentCallback[];

interface TransferContext {
  transferChain: string[];
  transferDepth: number;
  rootAgentName: string;
  rootSpanContext?: SpanContext;
  previousSpanContext?: SpanContext;
}

See Also

Build docs developers (and LLMs) love