Complete guide to advanced features in Tawk Agents SDK.
- Message Helpers
- Lifecycle Hooks
- Advanced Tracing
- Safe Execution
- Background Results
- RunState Management
- TypeScript Utilities
Build and manipulate conversations programmatically.
import { user, assistant, system, toolMessage } from '../../src';
const messages = [
system('You are a helpful assistant'),
user('Hello!'),
assistant('Hi! How can I help?'),
user('Tell me about AI'),
];
// Use in agent
const result = await run(agent, messages);import { getLastTextContent, filterMessagesByRole, extractAllText } from '../../src';
// Get last message content
const lastText = getLastTextContent(messages);
// "Tell me about AI"
// Filter by role
const userMessages = filterMessagesByRole(messages, 'user');
// [{ role: 'user', content: 'Hello!' }, { role: 'user', content: 'Tell me about AI' }]
// Extract all text
const allText = extractAllText(messages);
// "You are a helpful assistant\nHello!\nHi! How can I help?\nTell me about AI"Hook into agent execution lifecycle for custom logic.
class AgentHooks {
onStart(context: any): void | Promise<void>
onComplete(context: any, result: any): void | Promise<void>
onError(context: any, error: Error): void | Promise<void>
onToolCall(context: any, toolName: string, args: any): void | Promise<void>
onToolResult(context: any, toolName: string, result: any): void | Promise<void>
}import { Agent, run, AgentHooks } from '../../src';
class LoggingAgent extends AgentHooks {
onStart(context: any) {
console.log('[Agent] Starting execution');
}
onToolCall(context: any, toolName: string, args: any) {
console.log(`[Tool] Calling ${toolName}`, args);
}
onToolResult(context: any, toolName: string, result: any) {
console.log(`[Tool] Result from ${toolName}`, result);
}
onComplete(context: any, result: any) {
console.log('[Agent] Execution complete');
}
onError(context: any, error: Error) {
console.error('[Agent] Error:', error);
}
}
const agent = new Agent({
name: 'hooked-agent',
model: openai('gpt-4o'),
instructions: 'You are a helpful assistant.',
tools: { /* your tools */ }
});
// Apply hooks
Object.setPrototypeOf(agent, LoggingAgent.prototype);
const result = await run(agent, 'Hello');class MonitoringAgent extends AgentHooks {
private startTime: number = 0;
private toolMetrics: Map<string, number[]> = new Map();
onStart() {
this.startTime = Date.now();
}
onToolCall(context: any, toolName: string) {
if (!this.toolMetrics.has(toolName)) {
this.toolMetrics.set(toolName, []);
}
this.toolMetrics.get(toolName)!.push(Date.now());
}
onToolResult(context: any, toolName: string) {
const times = this.toolMetrics.get(toolName)!;
const duration = Date.now() - times[times.length - 1];
console.log(`Tool ${toolName} took ${duration}ms`);
}
onComplete() {
const totalTime = Date.now() - this.startTime;
console.log(`Total execution: ${totalTime}ms`);
console.log('Tool usage:', Object.fromEntries(this.toolMetrics));
}
}Beyond- Transfer-safe - transfer markers preserved custom traces.
import { withTrace, withFunctionSpan, getCurrentTrace } from '../../src';
await withTrace(
{
name: 'Custom Operation',
userId: 'user-123',
sessionId: 'session-456',
metadata: {
environment: 'production',
version: '1.0.0'
},
},
async (trace) => {
// Operation 1
const result1 = await withFunctionSpan(
trace,
'database_query',
{ query: 'SELECT * FROM users WHERE id = ?' },
async () => {
return await db.query('SELECT * FROM users WHERE id = ?', [123]);
},
{ queryType: 'read', table: 'users' }
);
// Operation 2
const result2 = await withFunctionSpan(
trace,
'external_api',
{ endpoint: '/api/data' },
async () => {
return await fetch('/api/data').then(r => r.json());
}
);
return { result1, result2 };
}
);import { getCurrentTrace, createContextualSpan } from '../../src';
// Inside a tool or hook
const trace = getCurrentTrace();
if (trace) {
const span = createContextualSpan('My Operation', {
input: { param: 'value' },
metadata: { custom: 'data' }
});
try {
// Do work
const result = await doWork();
span?.end({ output: result });
} catch (error) {
span?.end({ output: { error: String(error) }, level: 'ERROR' });
}
}Error-safe execution with automatic error handling.
import { safeExecute } from '../../src';
const result = await safeExecute(async () => {
// Potentially failing operation
const data = await riskyOperation();
return data;
});
if (result.success) {
console.log('Success:', result.result);
} else {
console.error('Error:', result.error);
console.error('Stack:', result.stack);
}import { safeExecuteWithTimeout } from '../../src';
const result = await safeExecuteWithTimeout(
async () => {
return await slowDatabaseQuery();
},
5000 // 5 second timeout
);
if (result.success) {
console.log('Query completed:', result.result);
} else {
if (result.timeout) {
console.error('Query timed out after 5s');
} else {
console.error('Query failed:', result.error);
}
}const safeTool = tool({
description: 'Tool with built-in error handling',
inputSchema: z.object({ url: z.string() }),
execute: async ({ url }) => {
const result = await safeExecuteWithTimeout(
async () => {
const response = await fetch(url);
return await response.json();
},
3000
);
if (!result.success) {
return {
error: true,
message: result.timeout ? 'Request timed out' : result.error,
};
}
return result.result;
},
});Execute long-running tasks asynchronously.
import { backgroundResult, isBackgroundResult } from '../../src';
const longRunningTool = tool({
description: 'Start a long-running task',
inputSchema: z.object({ taskId: z.string() }),
execute: async ({ taskId }) => {
// Start background task
const promise = processLargeDataset(taskId);
// Return immediately
return backgroundResult(promise, {
status: 'processing',
taskId,
estimatedTime: '5 minutes',
});
},
});
// Check result type
const result = await longRunningTool.execute({ taskId: '123' });
if (isBackgroundResult(result)) {
console.log('Task started:', result.metadata);
// Optionally wait for completion
const finalResult = await result.promise;
console.log('Task completed:', finalResult);
}const pollingTool = tool({
description: 'Poll for task completion',
inputSchema: z.object({ taskId: z.string() }),
execute: async ({ taskId }, context) => {
const status = await checkTaskStatus(taskId);
if (status.completed) {
return status.result;
}
// Return background result with polling
const promise = new Promise((resolve) => {
const interval = setInterval(async () => {
const check = await checkTaskStatus(taskId);
if (check.completed) {
clearInterval(interval);
resolve(check.result);
}
}, 5000);
});
return backgroundResult(promise, {
status: 'polling',
progress: status.progress,
});
},
});Advanced state management for interruption and resumption.
import { run, RunState } from '../../src';
// Initial run
const result1 = await run(agent, 'Start complex task');
// Save state for later
if (result1.state) {
await saveToDatabase(result1.state);
}
// Later... resume from saved state
const savedState = await loadFromDatabase();
const result2 = await run(agent, savedState);
console.log('Resumed and completed:', result2.finalOutput);import { needsApproval, resume } from '../../src';
const result = await run(agent, 'Perform action');
if (needsApproval(result)) {
console.log('Waiting for approval...');
// Wait for user approval
const approved = await getUserApproval();
if (approved) {
// Resume execution
const finalResult = await resume(result.state, { approved: true });
console.log('Completed:', finalResult.finalOutput);
}
}class WorkflowManager {
async executeWorkflow(steps: string[]) {
let state: RunState | undefined;
for (const step of steps) {
const result = state
? await run(agent, state)
: await run(agent, step);
console.log(`Step completed: ${step}`);
if (!result.state?.isMaxTurnsExceeded()) {
state = result.state;
} else {
break;
}
}
return state;
}
}Advanced type helpers for better TypeScript development.
import type { Expand, Prettify } from '../../src';
// Expand complex intersection types
type ComplexType = Type1 & Type2 & Type3;
type ExpandedType = Expand<ComplexType>; // Flattened for IDE
// Prettify for better IDE hints
type Config = Prettify<AgentConfig & CustomConfig>;import type { DeepPartial } from '../../src';
// Make all properties optional recursively
type PartialAgentConfig = DeepPartial<AgentConfig>;
const partialConfig: PartialAgentConfig = {
name: 'test',
// All other fields optional, even nested ones
};import type { RequireKeys, OptionalKeys } from '../../src';
// Require specific keys
type RequiredConfig = RequireKeys<AgentConfig, 'name' | 'model'>;
// Get only optional keys
type OnlyOptional = OptionalKeys<AgentConfig>;import type { KeysOfType } from '../../src';
// Get keys of specific type
type StringKeys = KeysOfType<AgentConfig, string>;
type FunctionKeys = KeysOfType<AgentConfig, Function>;import type { UnwrapPromise } from '../../src';
async function fetchData(): Promise<{ id: number; name: string }> {
return { id: 1, name: 'test' };
}
// Extract the resolved type
type Data = UnwrapPromise<ReturnType<typeof fetchData>>;
// { id: number; name: string }import type { ArrayElement } from '../../src';
const tools = [tool1, tool2, tool3];
// Get element type
type Tool = ArrayElement<typeof tools>;import type { SnakeToCamelCase } from '../../src';
// Convert snake_case to camelCase at type level
type SnakeKeys = 'user_id' | 'first_name' | 'last_name';
type CamelKeys = SnakeToCamelCase<SnakeKeys>;
// 'userId' | 'firstName' | 'lastName'Here's a complete example using multiple advanced features:
import {
Agent,
run,
tool,
AgentHooks,
withTrace,
safeExecuteWithTimeout,
backgroundResult,
isBackgroundResult,
user,
assistant,
getLastTextContent,
} from '../../src';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
// Custom monitoring hooks
class MonitoredAgent extends AgentHooks {
onStart() {
console.log('🚀 Agent starting');
}
onToolCall(context: any, toolName: string, args: any) {
console.log(`🔧 Calling ${toolName}`, args);
}
onComplete(context: any, result: any) {
console.log('✅ Agent completed');
}
}
// Safe tool with timeout
const dataTool = tool({
description: 'Fetch data safely',
inputSchema: z.object({ url: z.string() }),
execute: async ({ url }) => {
const result = await safeExecuteWithTimeout(
async () => {
const response = await fetch(url);
return await response.json();
},
5000
);
return result.success ? result.result : { error: result.error };
},
});
// Background task tool
const processTool = tool({
description: 'Process large dataset',
inputSchema: z.object({ datasetId: z.string() }),
execute: async ({ datasetId }) => {
const promise = processLargeDataset(datasetId);
return backgroundResult(promise, { status: 'processing', datasetId });
},
});
// Create agent with hooks
const agent = new Agent({
name: 'AdvancedAgent',
model: openai('gpt-4o'),
instructions: 'You are an advanced agent with monitoring.',
tools: {
fetchData: dataTool,
processData: processTool,
},
});
Object.setPrototypeOf(agent, MonitoredAgent.prototype);
// Execute with custom tracing
await withTrace(
{
name: 'Advanced Agent Run',
userId: 'user-123',
metadata: { feature: 'advanced-example' },
},
async (trace) => {
// Build conversation
const messages = [
user('Fetch data from https://api.example.com/data'),
assistant('I\'ll fetch that data for you.'),
user('Now process dataset-123'),
];
const result = await run(agent, messages);
console.log('Final output:', result.finalOutput);
// Check for background results
if (result.metadata.hasBackgroundTasks) {
console.log('Background tasks running...');
}
}
);For more examples, see the examples directory.