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 athena-intelligence
2

Set Up Client

1import type { AthenaIntelligence } from 'athena-intelligence';
2import { AthenaIntelligenceClient, AthenaIntelligenceError } from 'athena-intelligence';
3
4// Production client setup
5const client = new AthenaIntelligenceClient({
6 apiKey: process.env.ATHENA_API_KEY,
7});
8
9// Custom API endpoint (if needed)
10const customClient = new AthenaIntelligenceClient({
11 apiKey: process.env.ATHENA_API_KEY,
12 baseUrl: 'https://your-custom-api.example.com',
13});
14
15// Local development
16const devClient = new AthenaIntelligenceClient({
17 apiKey: process.env.ATHENA_API_KEY,
18 baseUrl: 'http://localhost:8000',
19});
3

TypeScript Type Definitions

Define proper interfaces to avoid using any types:

1// Content types for message content
2type MessageContent = string | Array<{ type: string; text?: string }>;
3
4// Progress update interface
5interface ProgressUpdate {
6 attempt: number;
7 maxAttempts: number;
8 status: string;
9 threadId: string;
10 updatedAt: string;
11}
12
13// AOP execution result
14interface AOPExecutionResult {
15 status: 'completed' | 'failed' | 'timeout' | 'error';
16 threadId: string;
17 result?: string;
18 error?: string;
19 conversationAssetId?: string;
20 messageCount?: number | null;
21 threadStatus?: AthenaIntelligence.ThreadStatusResponseOut;
22}
23
24// Complete execution response
25interface ExecutionResponse {
26 startResponse: AthenaIntelligence.AopAsyncExecuteResponseOut;
27 finalResult: AOPExecutionResult;
28 success: boolean;
29 error?: string;
30 statusCode?: number;
31}
4

Content Helper Functions

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

1// Helper function to extract text from message content
2function extractMessageText(content: MessageContent): string {
3 if (typeof content === 'string') {
4 return content;
5 } else if (Array.isArray(content)) {
6 return content
7 .filter(part => part.type === 'text' && part.text)
8 .map(part => part.text!)
9 .join('\n');
10 }
11 return '';
12}
13
14// Helper function to safely get final assistant message text
15function getFinalAssistantText(conversation: AthenaIntelligence.ConversationResult | undefined): string | null {
16 if (!conversation?.last_assistant_message?.content) {
17 return null;
18 }
19 return extractMessageText(conversation.last_assistant_message.content);
20}
21
22// Helper to safely extract text from conversation asset
23function getConversationAssetText(conversationAsset: AthenaIntelligence.ConversationAssetInfo | undefined): string | null {
24 if (!conversationAsset?.last_message?.content) {
25 return null;
26 }
27 return extractMessageText(conversationAsset.last_message.content);
28}
5

Async AOP Execution Pattern

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

1// Execute AOP asynchronously and monitor until completion
2async function executeAOPAsync(
3 assetId: string,
4 userInputs: Record<string, string> = {}
5): Promise<ExecutionResponse> {
6 try {
7 console.log('🚀 Starting async AOP execution...');
8
9 // Step 1: Start async execution (returns immediately with thread_id)
10 const asyncResponse = await client.aop.executeAsync({
11 asset_id: assetId,
12 user_inputs: userInputs,
13 });
14
15 // executeAsync only STARTS the execution - it does NOT complete it
16 console.log('✅ AOP execution initiated (running in background)');
17 console.log('Thread ID:', asyncResponse.thread_id);
18 console.log('Status:', asyncResponse.status); // Will be "started" or similar
19 console.log('AOP Title:', asyncResponse.aop_title);
20
21 // Step 2: Monitor execution until it actually completes
22 const result = await monitorExecution(asyncResponse.thread_id);
23
24 return {
25 startResponse: asyncResponse,
26 finalResult: result,
27 success: true,
28 };
29 } catch (error) {
30 console.error('AOP execution failed:', error);
31
32 if (error instanceof AthenaIntelligenceError) {
33 return {
34 success: false,
35 error: `API Error: ${error.statusCode} - ${error.message}`,
36 statusCode: error.statusCode,
37 } as ExecutionResponse;
38 }
39
40 return {
41 success: false,
42 error: error instanceof Error ? error.message : 'Unknown error occurred',
43 } as ExecutionResponse;
44 }
45}
6

Production-Ready Progress Monitoring

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

1async function monitorExecution(threadId: string): Promise<AOPExecutionResult> {
2 const maxAttempts = 60; // 5 minutes with 5-second intervals
3 const pollInterval = 5000; // 5 seconds between polls
4 let consecutiveErrors = 0;
5 const maxConsecutiveErrors = 3;
6
7 console.log(`📊 Starting progress monitoring for thread: ${threadId}`);
8
9 for (let attempt = 1; attempt <= maxAttempts; attempt++) {
10 try {
11 // Poll thread status
12 const statusResponse = await client.threads.getStatus(threadId);
13
14 // Reset error counter on successful response
15 consecutiveErrors = 0;
16
17 console.log(`[${attempt}/${maxAttempts}] Status: ${statusResponse.status} (Updated: ${statusResponse.updated_at})`);
18
19 // Check if execution completed
20 if (statusResponse.status === 'completed') {
21 console.log('🎉 Thread execution finished successfully!');
22
23 // Extract final result safely
24 const conversationAsset = statusResponse.conversation_asset;
25 if (conversationAsset) {
26 const finalText = getConversationAssetText(conversationAsset);
27
28 return {
29 status: 'completed',
30 threadId,
31 result: finalText || 'Thread completed but no final message available',
32 conversationAssetId: conversationAsset.conversation_asset_id,
33 messageCount: conversationAsset.num_messages,
34 threadStatus: statusResponse,
35 };
36 } else {
37 return {
38 status: 'completed',
39 threadId,
40 result: 'Thread completed but no conversation asset available',
41 threadStatus: statusResponse,
42 };
43 }
44 } else if (statusResponse.status === 'failed') {
45 console.error('❌ Thread execution failed');
46 return {
47 status: 'failed',
48 threadId,
49 error: 'Thread execution failed',
50 threadStatus: statusResponse,
51 };
52 } else {
53 // Still running
54 console.log(`⏳ Still running... (${statusResponse.status})`);
55
56 // Wait before next poll
57 await new Promise(resolve => setTimeout(resolve, pollInterval));
58 }
59 } catch (error) {
60 consecutiveErrors++;
61 const errorMessage = error instanceof Error ? error.message : 'Unknown error';
62 console.error(`Status check failed (${consecutiveErrors}/${maxConsecutiveErrors}):`, errorMessage);
63
64 // Fail fast if too many consecutive errors
65 if (consecutiveErrors >= maxConsecutiveErrors) {
66 return {
67 status: 'error',
68 threadId,
69 error: `Too many consecutive status check failures: ${errorMessage}`,
70 };
71 }
72
73 // Wait before retrying
74 await new Promise(resolve => setTimeout(resolve, pollInterval));
75 }
76 }
77
78 console.warn('⏰ Polling timeout reached');
79 return {
80 status: 'timeout',
81 threadId,
82 error: 'Monitoring timeout reached - execution may still be running',
83 };
84}
7

Basic Usage Example

Execute an AOP with custom parameters:

1async function basicExample() {
2 const result = await executeAOPAsync(
3 'asset_9249292-d118-42d3-95b4-00eccfe0754f',
4 {
5 company: 'Acme Corp',
6 quarter: 'Q1 2024',
7 analysis_type: 'comprehensive',
8 }
9 );
10
11 if (result.success) {
12 console.log('✅ Execution completed successfully');
13 console.log('Final result:', result.finalResult.result);
14 } else {
15 console.error('❌ Execution failed:', result.error);
16 }
17}
18
19// Run the example
20basicExample();
8

Advanced Usage with Custom Monitoring

Customize monitoring behavior with progress callbacks:

1// Custom monitoring with progress callbacks
2async function executeWithCustomMonitoring(
3 assetId: string,
4 userInputs: Record<string, string>,
5 onProgress?: (status: ProgressUpdate) => void
6): Promise<AOPExecutionResult> {
7 try {
8 // Start execution (returns immediately)
9 const asyncResponse = await client.aop.executeAsync({
10 asset_id: assetId,
11 user_inputs: userInputs,
12 });
13
14 console.log('🚀 AOP initiated:', asyncResponse.aop_title);
15 console.log('📍 Thread ID:', asyncResponse.thread_id);
16
17 // Monitor with callbacks
18 const result = await monitorWithCallback(asyncResponse.thread_id, onProgress);
19
20 return result;
21 } catch (error) {
22 const errorMessage = error instanceof Error ? error.message : 'Unknown error';
23 throw new Error(`AOP execution failed: ${errorMessage}`);
24 }
25}
26
27async function monitorWithCallback(
28 threadId: string,
29 onProgress?: (status: ProgressUpdate) => void
30): Promise<AOPExecutionResult> {
31 const maxAttempts = 120; // 10 minutes for longer workflows
32 const pollInterval = 5000;
33
34 for (let attempt = 1; attempt <= maxAttempts; attempt++) {
35 try {
36 const status = await client.threads.getStatus(threadId);
37
38 // Call progress callback if provided
39 if (onProgress) {
40 onProgress({
41 attempt,
42 maxAttempts,
43 status: status.status,
44 updatedAt: status.updated_at,
45 threadId: status.thread_id,
46 });
47 }
48
49 if (status.status === 'completed') {
50 const finalText = getConversationAssetText(status.conversation_asset);
51 return {
52 status: 'completed',
53 threadId,
54 result: finalText || 'No final result available',
55 threadStatus: status,
56 };
57 } else if (status.status === 'failed') {
58 throw new Error('Thread execution failed');
59 }
60
61 await new Promise(resolve => setTimeout(resolve, pollInterval));
62 } catch (error) {
63 if (attempt === maxAttempts) {
64 throw error;
65 }
66 const errorMessage = error instanceof Error ? error.message : 'Unknown error';
67 console.warn(`Status check failed, retrying... (${errorMessage})`);
68 await new Promise(resolve => setTimeout(resolve, pollInterval));
69 }
70 }
71
72 throw new Error('Monitoring timeout reached');
73}
9

Multiple AOP Execution

Execute multiple AOPs concurrently:

1interface AOPConfig {
2 name: string;
3 assetId: string;
4 inputs: Record<string, string>;
5}
6
7interface AOPStartResult {
8 name: string;
9 threadId?: string;
10 aopTitle?: string;
11 success: boolean;
12 error?: string;
13}
14
15async function executeMultipleAOPs() {
16 const aopConfigs: AOPConfig[] = [
17 {
18 name: 'Market Research',
19 assetId: 'asset_market_research',
20 inputs: { company: 'Tesla', quarter: 'Q3 2024' },
21 },
22 {
23 name: 'Competitor Analysis',
24 assetId: 'asset_competitor_analysis',
25 inputs: { company: 'Tesla', competitors: 'Ford,GM,Rivian' },
26 },
27 {
28 name: 'Financial Summary',
29 assetId: 'asset_financial_analysis',
30 inputs: { company: 'Tesla', period: 'quarterly' },
31 },
32 ];
33
34 console.log(`🔄 Starting ${aopConfigs.length} AOPs concurrently...`);
35
36 // Step 1: Start all AOPs asynchronously (all return immediately with thread_ids)
37 const startPromises = aopConfigs.map(async (config): Promise<AOPStartResult> => {
38 try {
39 const response = await client.aop.executeAsync({
40 asset_id: config.assetId,
41 user_inputs: config.inputs,
42 });
43
44 console.log(`✅ ${config.name} started with thread: ${response.thread_id}`);
45
46 return {
47 name: config.name,
48 threadId: response.thread_id,
49 aopTitle: response.aop_title,
50 success: true,
51 };
52 } catch (error) {
53 const errorMessage = error instanceof Error ? error.message : 'Unknown error';
54 return {
55 name: config.name,
56 error: errorMessage,
57 success: false,
58 };
59 }
60 });
61
62 const startResults = await Promise.all(startPromises);
63
64 // Step 2: Monitor all successful starts until completion
65 const monitorPromises = startResults
66 .filter(result => result.success && result.threadId)
67 .map(async (result) => {
68 try {
69 const finalResult = await monitorExecution(result.threadId!);
70 return {
71 name: result.name,
72 threadId: result.threadId,
73 ...finalResult,
74 };
75 } catch (error) {
76 const errorMessage = error instanceof Error ? error.message : 'Unknown error';
77 return {
78 name: result.name,
79 threadId: result.threadId,
80 status: 'error' as const,
81 error: errorMessage,
82 };
83 }
84 });
85
86 const finalResults = await Promise.all(monitorPromises);
87
88 // Log results
89 console.log('\n=== Execution Results ===');
90 finalResults.forEach(result => {
91 console.log(`${result.name}: ${result.status}`);
92 if (result.result) {
93 console.log(` Result: ${result.result.substring(0, 100)}...`);
94 }
95 if (result.error) {
96 console.log(` Error: ${result.error}`);
97 }
98 });
99
100 return finalResults;
101}
10

Error Handling and Retry Logic

Implement comprehensive error handling for production:

1async function executeAOPWithRetry(
2 assetId: string,
3 userInputs: Record<string, string>,
4 maxRetries: number = 3
5): Promise<ExecutionResponse> {
6 let lastError: Error | null = null;
7
8 for (let attempt = 1; attempt <= maxRetries; attempt++) {
9 try {
10 console.log(`🔄 Attempt ${attempt}/${maxRetries} for AOP execution`);
11
12 const result = await executeAOPAsync(assetId, userInputs);
13
14 if (result.success) {
15 console.log(`✅ AOP succeeded on attempt ${attempt}`);
16 return result;
17 } else {
18 throw new Error(result.error || 'AOP execution failed');
19 }
20 } catch (error) {
21 lastError = error as Error;
22
23 // Don't retry on client errors (4xx)
24 if (error instanceof AthenaIntelligenceError &&
25 error.statusCode >= 400 && error.statusCode < 500) {
26 console.error(`❌ Client error (${error.statusCode}): ${error.message}`);
27 throw error;
28 }
29
30 // Retry on server errors or network issues
31 if (attempt < maxRetries) {
32 const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
33 console.log(`⏳ Waiting ${delay}ms before retry...`);
34 await new Promise(resolve => setTimeout(resolve, delay));
35 }
36 }
37 }
38
39 console.error('❌ AOP failed after all retry attempts');
40 throw lastError;
41}
11

Type-Safe Options Interface

Define comprehensive options for AOP execution:

1interface AOPExecutionOptions {
2 maxAttempts?: number;
3 pollInterval?: number;
4 onProgress?: (status: ProgressUpdate) => void;
5 retries?: number;
6 timeout?: number;
7}
8
9// Type-safe AOP executor
10async function executeAOPTyped(
11 assetId: string,
12 userInputs: Record<string, string> = {},
13 options: AOPExecutionOptions = {}
14): Promise<AOPExecutionResult> {
15 const {
16 maxAttempts = 60,
17 pollInterval = 5000,
18 onProgress,
19 timeout = 600000, // 10 minutes
20 } = options;
21
22 try {
23 // Start execution (returns immediately with thread_id)
24 const asyncResponse = await client.aop.executeAsync({
25 asset_id: assetId,
26 user_inputs: userInputs,
27 });
28
29 console.log('🚀 AOP initiated with thread:', asyncResponse.thread_id);
30
31 // Monitor with timeout
32 const result = await Promise.race([
33 monitorExecutionTyped(asyncResponse.thread_id, maxAttempts, pollInterval, onProgress),
34 createTimeoutPromise(timeout, asyncResponse.thread_id),
35 ]);
36
37 return result;
38 } catch (error) {
39 const errorMessage = error instanceof Error ? error.message : 'Unknown error';
40 return {
41 status: 'error',
42 threadId: 'unknown',
43 error: errorMessage,
44 };
45 }
46}
47
48async function monitorExecutionTyped(
49 threadId: string,
50 maxAttempts: number,
51 pollInterval: number,
52 onProgress?: (status: ProgressUpdate) => void
53): Promise<AOPExecutionResult> {
54 for (let attempt = 1; attempt <= maxAttempts; attempt++) {
55 try {
56 const status = await client.threads.getStatus(threadId);
57
58 if (onProgress) {
59 onProgress({
60 attempt,
61 maxAttempts,
62 status: status.status,
63 threadId: status.thread_id,
64 updatedAt: status.updated_at,
65 });
66 }
67
68 if (status.status === 'completed') {
69 return {
70 status: 'completed',
71 threadId,
72 result: getConversationAssetText(status.conversation_asset) || 'No result available',
73 conversationAssetId: status.conversation_asset?.conversation_asset_id,
74 messageCount: status.conversation_asset?.num_messages,
75 threadStatus: status,
76 };
77 } else if (status.status === 'failed') {
78 return {
79 status: 'failed',
80 threadId,
81 error: 'Thread execution failed',
82 threadStatus: status,
83 };
84 }
85
86 await new Promise(resolve => setTimeout(resolve, pollInterval));
87 } catch (error) {
88 if (attempt === maxAttempts) {
89 const errorMessage = error instanceof Error ? error.message : 'Unknown error';
90 return {
91 status: 'error',
92 threadId,
93 error: `Monitoring failed: ${errorMessage}`,
94 };
95 }
96 }
97 }
98
99 return {
100 status: 'timeout',
101 threadId,
102 error: 'Monitoring timeout reached',
103 };
104}
105
106function createTimeoutPromise(timeoutMs: number, threadId: string): Promise<AOPExecutionResult> {
107 return new Promise((resolve) => {
108 setTimeout(() => {
109 resolve({
110 status: 'timeout',
111 threadId,
112 error: `Execution timeout after ${timeoutMs}ms`,
113 });
114 }, timeoutMs);
115 });
116}
12

Complete Production Example

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

1import type { AthenaIntelligence } from 'athena-intelligence';
2import { AthenaIntelligenceClient, AthenaIntelligenceError } from 'athena-intelligence';
3
4class AOPExecutor {
5 private client: AthenaIntelligenceClient;
6
7 constructor(apiKey: string, baseUrl?: string) {
8 this.client = new AthenaIntelligenceClient({
9 apiKey,
10 baseUrl,
11 });
12 }
13
14 async executeAOP(
15 assetId: string,
16 userInputs: Record<string, string> = {},
17 options: AOPExecutionOptions = {}
18 ): Promise<AOPExecutionResult> {
19 const { timeout = 600000, onProgress, retries = 3 } = options;
20
21 let attempt = 0;
22 let lastError: Error | undefined;
23
24 while (attempt < retries) {
25 try {
26 console.log(`🚀 Starting AOP execution (attempt ${attempt + 1}/${retries})`);
27
28 // Start async execution (returns immediately with thread_id)
29 const startResponse = await this.client.aop.executeAsync({
30 asset_id: assetId,
31 user_inputs: userInputs,
32 });
33
34 console.log(`✅ AOP initiated: ${startResponse.aop_title}`);
35 console.log(`📍 Thread ID: ${startResponse.thread_id}`);
36
37 // Monitor with timeout
38 const result = await Promise.race([
39 this.monitorExecution(startResponse.thread_id, onProgress),
40 this.createTimeoutPromise(timeout, startResponse.thread_id),
41 ]);
42
43 if (result.status === 'completed') {
44 console.log('🎉 Thread execution finished successfully');
45 }
46
47 return result;
48 } catch (error) {
49 attempt++;
50 lastError = error as Error;
51 console.error(`❌ Attempt ${attempt} failed:`, lastError.message);
52
53 if (attempt >= retries) {
54 break;
55 }
56
57 // Exponential backoff
58 const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
59 await new Promise(resolve => setTimeout(resolve, delay));
60 }
61 }
62
63 // All retries failed
64 return {
65 status: 'error',
66 threadId: 'unknown',
67 error: lastError?.message || 'AOP execution failed after all retries',
68 };
69 }
70
71 private async monitorExecution(
72 threadId: string,
73 onProgress?: (update: ProgressUpdate) => void
74 ): Promise<AOPExecutionResult> {
75 const maxAttempts = 120; // Allow up to 10 minutes
76 const pollInterval = 5000;
77
78 for (let attempt = 1; attempt <= maxAttempts; attempt++) {
79 const status = await this.client.threads.getStatus(threadId);
80
81 const update: ProgressUpdate = {
82 attempt,
83 maxAttempts,
84 status: status.status,
85 threadId: status.thread_id,
86 updatedAt: status.updated_at,
87 };
88
89 if (onProgress) {
90 onProgress(update);
91 }
92
93 console.log(`[${attempt}/${maxAttempts}] Status: ${status.status}`);
94
95 if (status.status === 'completed') {
96 const result = getConversationAssetText(status.conversation_asset);
97 return {
98 status: 'completed',
99 threadId,
100 result: result || 'Thread finished successfully',
101 conversationAssetId: status.conversation_asset?.conversation_asset_id,
102 messageCount: status.conversation_asset?.num_messages,
103 threadStatus: status,
104 };
105 } else if (status.status === 'failed') {
106 return {
107 status: 'failed',
108 threadId,
109 error: 'Thread execution failed',
110 threadStatus: status,
111 };
112 }
113
114 await new Promise(resolve => setTimeout(resolve, pollInterval));
115 }
116
117 return {
118 status: 'timeout',
119 threadId,
120 error: 'Maximum polling attempts reached',
121 };
122 }
123
124 private createTimeoutPromise(timeoutMs: number, threadId: string): Promise<AOPExecutionResult> {
125 return new Promise((resolve) => {
126 setTimeout(() => {
127 resolve({
128 status: 'timeout',
129 threadId,
130 error: `Execution timeout after ${timeoutMs}ms`,
131 });
132 }, timeoutMs);
133 });
134 }
135}
136
137// Usage example with full type safety
138async function main() {
139 try {
140 const executor = new AOPExecutor(process.env.ATHENA_API_KEY!);
141
142 const result = await executor.executeAOP(
143 'asset_comprehensive_analysis',
144 {
145 company: 'OpenAI',
146 analysis_type: 'comprehensive',
147 time_period: '2024',
148 include_forecasts: 'true',
149 },
150 {
151 timeout: 600000, // 10 minutes
152 onProgress: (update: ProgressUpdate) => {
153 console.log(`📊 Progress: ${update.status} (${update.attempt}/${update.maxAttempts})`);
154 },
155 retries: 3,
156 }
157 );
158
159 if (result.status === 'completed') {
160 console.log('🎉 Final result:', result.result);
161 } else {
162 console.error('❌ Execution did not complete:', result.error);
163 }
164 } catch (error) {
165 console.error('💥 Execution failed:', error instanceof Error ? error.message : 'Unknown error');
166 }
167}
168
169main();
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.