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
Configuration object for the agent
Configuration Properties:
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.
Description of the agent’s capability. Used by parent agents to determine delegation. One-line descriptions are preferred.
Sub-agents that this agent can delegate to. Each sub-agent can only be added once.
config.beforeAgentCallback
Callback(s) invoked before agent execution. Can be a single callback or array of callbacks.
config.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
The agent’s name. Must be unique within the agent tree and cannot be “user”.
description
Description of the agent’s capability used for delegation decisions.
parentAgent
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
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.
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).
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
- Plugin callbacks are executed first
- If no plugin returns content, canonical callbacks are executed in order
- Execution stops at first callback returning content
- 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
- Plugin callbacks are executed first
- If no plugin returns content, canonical callbacks are executed in order
- Execution stops at first callback returning content
- 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