Execute AOPs

This guide shows how to execute Agent Operating Procedures (AOPs) using the TypeScript SDK with asynchronous execution - the recommended approach for production applications. AOPs are pre-configured AI workflows that can perform complex tasks like research, analysis, and content generation with optional user inputs for customization.

Why Async Execution? Long-running AOPs can take minutes to complete. Asynchronous execution prevents timeouts, provides real-time progress tracking, and ensures reliable execution of complex workflows.

Key features:

  • Production-ready async execution - Prevents timeouts for long-running workflows
  • Real-time progress monitoring - Track execution status with polling
  • Robust error handling - Comprehensive error handling and retry logic
  • Full TypeScript support - Complete type safety with proper interfaces
  • Thread-based tracking - Monitor execution progress via thread IDs
1

Install Package

pnpm add @athenaintel/sdk
2

Set Up Client

import type { AthenaIntelligence } from '@athenaintel/sdk';
import { AthenaIntelligenceClient, AthenaIntelligenceError } from '@athenaintel/sdk';
// Production client setup
const client = new AthenaIntelligenceClient({
apiKey: process.env.ATHENA_API_KEY,
});
// Custom API endpoint (if needed)
const customClient = new AthenaIntelligenceClient({
apiKey: process.env.ATHENA_API_KEY,
baseUrl: 'https://your-custom-api.example.com',
});
// Local development
const devClient = new AthenaIntelligenceClient({
apiKey: process.env.ATHENA_API_KEY,
baseUrl: 'http://localhost:8000',
});
3

TypeScript Type Definitions

Define proper interfaces to avoid using any types:

// Content types for message content
type MessageContent = string | Array<{ type: string; text?: string }>;
// Progress update interface
interface ProgressUpdate {
attempt: number;
maxAttempts: number;
status: string;
threadId: string;
updatedAt: string;
}
// AOP execution result
interface AOPExecutionResult {
status: 'completed' | 'failed' | 'timeout' | 'error';
threadId: string;
result?: string;
error?: string;
conversationAssetId?: string;
messageCount?: number | null;
threadStatus?: AthenaIntelligence.ThreadStatusResponseOut;
}
// Complete execution response
interface ExecutionResponse {
startResponse: AthenaIntelligence.AopAsyncExecuteResponseOut;
finalResult: AOPExecutionResult;
success: boolean;
error?: string;
statusCode?: number;
}
4

Content Helper Functions

Type-safe helper functions to extract text from message content:

// Helper function to extract text from message content
function extractMessageText(content: MessageContent): string {
if (typeof content === 'string') {
return content;
} else if (Array.isArray(content)) {
return content
.filter(part => part.type === 'text' && part.text)
.map(part => part.text!)
.join('\n');
}
return '';
}
// Helper function to safely get final assistant message text
function getFinalAssistantText(conversation: AthenaIntelligence.ConversationResult | undefined): string | null {
if (!conversation?.last_assistant_message?.content) {
return null;
}
return extractMessageText(conversation.last_assistant_message.content);
}
// Helper to safely extract text from conversation asset
function getConversationAssetText(conversationAsset: AthenaIntelligence.ConversationAssetInfo | undefined): string | null {
if (!conversationAsset?.last_message?.content) {
return null;
}
return extractMessageText(conversationAsset.last_message.content);
}
5

Async AOP Execution Pattern

The recommended approach - executeAsync starts execution and returns a thread_id for tracking:

// Execute AOP asynchronously and monitor until completion
async function executeAOPAsync(
assetId: string,
userInputs: Record<string, string> = {}
): Promise<ExecutionResponse> {
try {
console.log('🚀 Starting async AOP execution...');
// Step 1: Start async execution (returns immediately with thread_id)
const asyncResponse = await client.aop.executeAsync({
asset_id: assetId,
user_inputs: userInputs,
});
// executeAsync only STARTS the execution - it does NOT complete it
console.log('✅ AOP execution initiated (running in background)');
console.log('Thread ID:', asyncResponse.thread_id);
console.log('Status:', asyncResponse.status); // Will be "started" or similar
console.log('AOP Title:', asyncResponse.aop_title);
// Step 2: Monitor execution until it actually completes
const result = await monitorExecution(asyncResponse.thread_id);
return {
startResponse: asyncResponse,
finalResult: result,
success: true,
};
} catch (error) {
console.error('AOP execution failed:', error);
if (error instanceof AthenaIntelligenceError) {
return {
success: false,
error: `API Error: ${error.statusCode} - ${error.message}`,
statusCode: error.statusCode,
} as ExecutionResponse;
}
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
} as ExecutionResponse;
}
}
6

Production-Ready Progress Monitoring

Monitor execution progress using threads.getStatus with comprehensive error handling:

async function monitorExecution(threadId: string): Promise<AOPExecutionResult> {
const maxAttempts = 60; // 5 minutes with 5-second intervals
const pollInterval = 5000; // 5 seconds between polls
let consecutiveErrors = 0;
const maxConsecutiveErrors = 3;
console.log(`📊 Starting progress monitoring for thread: ${threadId}`);
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
// Poll thread status
const statusResponse = await client.threads.getStatus(threadId);
// Reset error counter on successful response
consecutiveErrors = 0;
console.log(`[${attempt}/${maxAttempts}] Status: ${statusResponse.status} (Updated: ${statusResponse.updated_at})`);
// Check if execution completed
if (statusResponse.status === 'completed') {
console.log('🎉 Thread execution finished successfully!');
// Extract final result safely
const conversationAsset = statusResponse.conversation_asset;
if (conversationAsset) {
const finalText = getConversationAssetText(conversationAsset);
return {
status: 'completed',
threadId,
result: finalText || 'Thread completed but no final message available',
conversationAssetId: conversationAsset.conversation_asset_id,
messageCount: conversationAsset.num_messages,
threadStatus: statusResponse,
};
} else {
return {
status: 'completed',
threadId,
result: 'Thread completed but no conversation asset available',
threadStatus: statusResponse,
};
}
} else if (statusResponse.status === 'failed') {
console.error('❌ Thread execution failed');
return {
status: 'failed',
threadId,
error: 'Thread execution failed',
threadStatus: statusResponse,
};
} else {
// Still running
console.log(`⏳ Still running... (${statusResponse.status})`);
// Wait before next poll
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
} catch (error) {
consecutiveErrors++;
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
console.error(`Status check failed (${consecutiveErrors}/${maxConsecutiveErrors}):`, errorMessage);
// Fail fast if too many consecutive errors
if (consecutiveErrors >= maxConsecutiveErrors) {
return {
status: 'error',
threadId,
error: `Too many consecutive status check failures: ${errorMessage}`,
};
}
// Wait before retrying
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
}
console.warn('⏰ Polling timeout reached');
return {
status: 'timeout',
threadId,
error: 'Monitoring timeout reached - execution may still be running',
};
}
7

Basic Usage Example

Execute an AOP with custom parameters:

async function basicExample() {
const result = await executeAOPAsync(
'asset_9249292-d118-42d3-95b4-00eccfe0754f',
{
company: 'Acme Corp',
quarter: 'Q1 2024',
analysis_type: 'comprehensive',
}
);
if (result.success) {
console.log('✅ Execution completed successfully');
console.log('Final result:', result.finalResult.result);
} else {
console.error('❌ Execution failed:', result.error);
}
}
// Run the example
basicExample();
8

Advanced Usage with Custom Monitoring

Customize monitoring behavior with progress callbacks:

// Custom monitoring with progress callbacks
async function executeWithCustomMonitoring(
assetId: string,
userInputs: Record<string, string>,
onProgress?: (status: ProgressUpdate) => void
): Promise<AOPExecutionResult> {
try {
// Start execution (returns immediately)
const asyncResponse = await client.aop.executeAsync({
asset_id: assetId,
user_inputs: userInputs,
});
console.log('🚀 AOP initiated:', asyncResponse.aop_title);
console.log('📍 Thread ID:', asyncResponse.thread_id);
// Monitor with callbacks
const result = await monitorWithCallback(asyncResponse.thread_id, onProgress);
return result;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
throw new Error(`AOP execution failed: ${errorMessage}`);
}
}
async function monitorWithCallback(
threadId: string,
onProgress?: (status: ProgressUpdate) => void
): Promise<AOPExecutionResult> {
const maxAttempts = 120; // 10 minutes for longer workflows
const pollInterval = 5000;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
const status = await client.threads.getStatus(threadId);
// Call progress callback if provided
if (onProgress) {
onProgress({
attempt,
maxAttempts,
status: status.status,
updatedAt: status.updated_at,
threadId: status.thread_id,
});
}
if (status.status === 'completed') {
const finalText = getConversationAssetText(status.conversation_asset);
return {
status: 'completed',
threadId,
result: finalText || 'No final result available',
threadStatus: status,
};
} else if (status.status === 'failed') {
throw new Error('Thread execution failed');
}
await new Promise(resolve => setTimeout(resolve, pollInterval));
} catch (error) {
if (attempt === maxAttempts) {
throw error;
}
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
console.warn(`Status check failed, retrying... (${errorMessage})`);
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
}
throw new Error('Monitoring timeout reached');
}
9

Multiple AOP Execution

Execute multiple AOPs concurrently:

interface AOPConfig {
name: string;
assetId: string;
inputs: Record<string, string>;
}
interface AOPStartResult {
name: string;
threadId?: string;
aopTitle?: string;
success: boolean;
error?: string;
}
async function executeMultipleAOPs() {
const aopConfigs: AOPConfig[] = [
{
name: 'Market Research',
assetId: 'asset_market_research',
inputs: { company: 'Tesla', quarter: 'Q3 2024' },
},
{
name: 'Competitor Analysis',
assetId: 'asset_competitor_analysis',
inputs: { company: 'Tesla', competitors: 'Ford,GM,Rivian' },
},
{
name: 'Financial Summary',
assetId: 'asset_financial_analysis',
inputs: { company: 'Tesla', period: 'quarterly' },
},
];
console.log(`🔄 Starting ${aopConfigs.length} AOPs concurrently...`);
// Step 1: Start all AOPs asynchronously (all return immediately with thread_ids)
const startPromises = aopConfigs.map(async (config): Promise<AOPStartResult> => {
try {
const response = await client.aop.executeAsync({
asset_id: config.assetId,
user_inputs: config.inputs,
});
console.log(`✅ ${config.name} started with thread: ${response.thread_id}`);
return {
name: config.name,
threadId: response.thread_id,
aopTitle: response.aop_title,
success: true,
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
name: config.name,
error: errorMessage,
success: false,
};
}
});
const startResults = await Promise.all(startPromises);
// Step 2: Monitor all successful starts until completion
const monitorPromises = startResults
.filter(result => result.success && result.threadId)
.map(async (result) => {
try {
const finalResult = await monitorExecution(result.threadId!);
return {
name: result.name,
threadId: result.threadId,
...finalResult,
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
name: result.name,
threadId: result.threadId,
status: 'error' as const,
error: errorMessage,
};
}
});
const finalResults = await Promise.all(monitorPromises);
// Log results
console.log('\n=== Execution Results ===');
finalResults.forEach(result => {
console.log(`${result.name}: ${result.status}`);
if (result.result) {
console.log(` Result: ${result.result.substring(0, 100)}...`);
}
if (result.error) {
console.log(` Error: ${result.error}`);
}
});
return finalResults;
}
10

Error Handling and Retry Logic

Implement comprehensive error handling for production:

async function executeAOPWithRetry(
assetId: string,
userInputs: Record<string, string>,
maxRetries: number = 3
): Promise<ExecutionResponse> {
let lastError: Error | null = null;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
console.log(`🔄 Attempt ${attempt}/${maxRetries} for AOP execution`);
const result = await executeAOPAsync(assetId, userInputs);
if (result.success) {
console.log(`✅ AOP succeeded on attempt ${attempt}`);
return result;
} else {
throw new Error(result.error || 'AOP execution failed');
}
} catch (error) {
lastError = error as Error;
// Don't retry on client errors (4xx)
if (error instanceof AthenaIntelligenceError &&
error.statusCode >= 400 && error.statusCode < 500) {
console.error(`❌ Client error (${error.statusCode}): ${error.message}`);
throw error;
}
// Retry on server errors or network issues
if (attempt < maxRetries) {
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
console.log(`⏳ Waiting ${delay}ms before retry...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
console.error('❌ AOP failed after all retry attempts');
throw lastError;
}
11

Type-Safe Options Interface

Define comprehensive options for AOP execution:

interface AOPExecutionOptions {
maxAttempts?: number;
pollInterval?: number;
onProgress?: (status: ProgressUpdate) => void;
retries?: number;
timeout?: number;
}
// Type-safe AOP executor
async function executeAOPTyped(
assetId: string,
userInputs: Record<string, string> = {},
options: AOPExecutionOptions = {}
): Promise<AOPExecutionResult> {
const {
maxAttempts = 60,
pollInterval = 5000,
onProgress,
timeout = 600000, // 10 minutes
} = options;
try {
// Start execution (returns immediately with thread_id)
const asyncResponse = await client.aop.executeAsync({
asset_id: assetId,
user_inputs: userInputs,
});
console.log('🚀 AOP initiated with thread:', asyncResponse.thread_id);
// Monitor with timeout
const result = await Promise.race([
monitorExecutionTyped(asyncResponse.thread_id, maxAttempts, pollInterval, onProgress),
createTimeoutPromise(timeout, asyncResponse.thread_id),
]);
return result;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
status: 'error',
threadId: 'unknown',
error: errorMessage,
};
}
}
async function monitorExecutionTyped(
threadId: string,
maxAttempts: number,
pollInterval: number,
onProgress?: (status: ProgressUpdate) => void
): Promise<AOPExecutionResult> {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
const status = await client.threads.getStatus(threadId);
if (onProgress) {
onProgress({
attempt,
maxAttempts,
status: status.status,
threadId: status.thread_id,
updatedAt: status.updated_at,
});
}
if (status.status === 'completed') {
return {
status: 'completed',
threadId,
result: getConversationAssetText(status.conversation_asset) || 'No result available',
conversationAssetId: status.conversation_asset?.conversation_asset_id,
messageCount: status.conversation_asset?.num_messages,
threadStatus: status,
};
} else if (status.status === 'failed') {
return {
status: 'failed',
threadId,
error: 'Thread execution failed',
threadStatus: status,
};
}
await new Promise(resolve => setTimeout(resolve, pollInterval));
} catch (error) {
if (attempt === maxAttempts) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
status: 'error',
threadId,
error: `Monitoring failed: ${errorMessage}`,
};
}
}
}
return {
status: 'timeout',
threadId,
error: 'Monitoring timeout reached',
};
}
function createTimeoutPromise(timeoutMs: number, threadId: string): Promise<AOPExecutionResult> {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
status: 'timeout',
threadId,
error: `Execution timeout after ${timeoutMs}ms`,
});
}, timeoutMs);
});
}
12

Complete Production Example

Here’s a complete production-ready class with full type safety:

import type { AthenaIntelligence } from '@athenaintel/sdk';
import { AthenaIntelligenceClient, AthenaIntelligenceError } from '@athenaintel/sdk';
class AOPExecutor {
private client: AthenaIntelligenceClient;
constructor(apiKey: string, baseUrl?: string) {
this.client = new AthenaIntelligenceClient({
apiKey,
baseUrl,
});
}
async executeAOP(
assetId: string,
userInputs: Record<string, string> = {},
options: AOPExecutionOptions = {}
): Promise<AOPExecutionResult> {
const { timeout = 600000, onProgress, retries = 3 } = options;
let attempt = 0;
let lastError: Error | undefined;
while (attempt < retries) {
try {
console.log(`🚀 Starting AOP execution (attempt ${attempt + 1}/${retries})`);
// Start async execution (returns immediately with thread_id)
const startResponse = await this.client.aop.executeAsync({
asset_id: assetId,
user_inputs: userInputs,
});
console.log(`✅ AOP initiated: ${startResponse.aop_title}`);
console.log(`📍 Thread ID: ${startResponse.thread_id}`);
// Monitor with timeout
const result = await Promise.race([
this.monitorExecution(startResponse.thread_id, onProgress),
this.createTimeoutPromise(timeout, startResponse.thread_id),
]);
if (result.status === 'completed') {
console.log('🎉 Thread execution finished successfully');
}
return result;
} catch (error) {
attempt++;
lastError = error as Error;
console.error(`❌ Attempt ${attempt} failed:`, lastError.message);
if (attempt >= retries) {
break;
}
// Exponential backoff
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
// All retries failed
return {
status: 'error',
threadId: 'unknown',
error: lastError?.message || 'AOP execution failed after all retries',
};
}
private async monitorExecution(
threadId: string,
onProgress?: (update: ProgressUpdate) => void
): Promise<AOPExecutionResult> {
const maxAttempts = 120; // Allow up to 10 minutes
const pollInterval = 5000;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const status = await this.client.threads.getStatus(threadId);
const update: ProgressUpdate = {
attempt,
maxAttempts,
status: status.status,
threadId: status.thread_id,
updatedAt: status.updated_at,
};
if (onProgress) {
onProgress(update);
}
console.log(`[${attempt}/${maxAttempts}] Status: ${status.status}`);
if (status.status === 'completed') {
const result = getConversationAssetText(status.conversation_asset);
return {
status: 'completed',
threadId,
result: result || 'Thread finished successfully',
conversationAssetId: status.conversation_asset?.conversation_asset_id,
messageCount: status.conversation_asset?.num_messages,
threadStatus: status,
};
} else if (status.status === 'failed') {
return {
status: 'failed',
threadId,
error: 'Thread execution failed',
threadStatus: status,
};
}
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
return {
status: 'timeout',
threadId,
error: 'Maximum polling attempts reached',
};
}
private createTimeoutPromise(timeoutMs: number, threadId: string): Promise<AOPExecutionResult> {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
status: 'timeout',
threadId,
error: `Execution timeout after ${timeoutMs}ms`,
});
}, timeoutMs);
});
}
}
// Usage example with full type safety
async function main() {
try {
const executor = new AOPExecutor(process.env.ATHENA_API_KEY!);
const result = await executor.executeAOP(
'asset_comprehensive_analysis',
{
company: 'OpenAI',
analysis_type: 'comprehensive',
time_period: '2024',
include_forecasts: 'true',
},
{
timeout: 600000, // 10 minutes
onProgress: (update: ProgressUpdate) => {
console.log(`📊 Progress: ${update.status} (${update.attempt}/${update.maxAttempts})`);
},
retries: 3,
}
);
if (result.status === 'completed') {
console.log('🎉 Final result:', result.result);
} else {
console.error('❌ Execution did not complete:', result.error);
}
} catch (error) {
console.error('💥 Execution failed:', error instanceof Error ? error.message : 'Unknown error');
}
}
main();
13

Key Recommendations

  1. Always use executeAsync - It returns immediately with a thread_id for tracking
  2. Poll with threads.getStatus - The thread status shows actual completion
  3. Never use any types - Use proper TypeScript interfaces for type safety
  4. Implement proper error handling - Handle network failures, timeouts, and API errors
  5. Use retry logic - Implement exponential backoff for resilient execution
  6. Monitor progress - Use callbacks to provide real-time updates
  7. Set appropriate timeouts - Based on expected AOP complexity

Important: executeAsync only starts execution and returns a thread_id. You must poll threads.getStatus to monitor actual completion. The execution happens asynchronously in the background.