@messenger-box/tailwind-ui-inbox 10.0.3-alpha.71 → 10.0.3-alpha.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/components/AIAgent/AIAgent.d.ts +14 -0
  3. package/lib/components/AIAgent/AIAgent.d.ts.map +1 -0
  4. package/lib/components/AIAgent/AIAgent.js +1148 -0
  5. package/lib/components/AIAgent/AIAgent.js.map +1 -0
  6. package/lib/components/AIAgent/index.d.ts +2 -0
  7. package/lib/components/AIAgent/index.d.ts.map +1 -0
  8. package/lib/components/InboxMessage/InputComponent.d.ts +9 -0
  9. package/lib/components/InboxMessage/InputComponent.d.ts.map +1 -0
  10. package/lib/components/InboxMessage/InputComponent.js +210 -0
  11. package/lib/components/InboxMessage/InputComponent.js.map +1 -0
  12. package/lib/components/InboxMessage/MessageInput.d.ts.map +1 -1
  13. package/lib/components/InboxMessage/MessageInput.js +14 -10
  14. package/lib/components/InboxMessage/MessageInput.js.map +1 -1
  15. package/lib/components/InboxMessage/MessageInputComponent.d.ts +9 -0
  16. package/lib/components/InboxMessage/MessageInputComponent.d.ts.map +1 -0
  17. package/lib/components/InboxMessage/MessageInputComponent.js +173 -0
  18. package/lib/components/InboxMessage/MessageInputComponent.js.map +1 -0
  19. package/lib/components/InboxMessage/Messages.d.ts.map +1 -1
  20. package/lib/components/InboxMessage/Messages.js +4 -54
  21. package/lib/components/InboxMessage/Messages.js.map +1 -1
  22. package/lib/components/InboxMessage/MessagesBuilderUi.d.ts +17 -0
  23. package/lib/components/InboxMessage/MessagesBuilderUi.d.ts.map +1 -0
  24. package/lib/components/InboxMessage/MessagesBuilderUi.js +162 -0
  25. package/lib/components/InboxMessage/MessagesBuilderUi.js.map +1 -0
  26. package/lib/components/InboxMessage/UploadImageButton.d.ts +1 -0
  27. package/lib/components/InboxMessage/UploadImageButton.d.ts.map +1 -1
  28. package/lib/components/InboxMessage/UploadImageButton.js +3 -3
  29. package/lib/components/InboxMessage/UploadImageButton.js.map +1 -1
  30. package/lib/components/InboxMessage/index.d.ts +3 -0
  31. package/lib/components/InboxMessage/index.d.ts.map +1 -1
  32. package/lib/components/InboxMessage/message-widgets/CommonMessage.d.ts.map +1 -1
  33. package/lib/components/InboxMessage/message-widgets/CommonMessage.js +11 -6
  34. package/lib/components/InboxMessage/message-widgets/CommonMessage.js.map +1 -1
  35. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts +14 -0
  36. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts.map +1 -0
  37. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js +1525 -0
  38. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js.map +1 -0
  39. package/lib/components/InboxMessage/message-widgets/PlainMessage.d.ts.map +1 -1
  40. package/lib/components/InboxMessage/message-widgets/PlainMessage.js +6 -3
  41. package/lib/components/InboxMessage/message-widgets/PlainMessage.js.map +1 -1
  42. package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.d.ts.map +1 -1
  43. package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.js +207 -12
  44. package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.js.map +1 -1
  45. package/lib/components/InboxMessage/message-widgets/index.d.ts +1 -0
  46. package/lib/components/InboxMessage/message-widgets/index.d.ts.map +1 -1
  47. package/lib/components/index.d.ts +2 -1
  48. package/lib/components/index.d.ts.map +1 -1
  49. package/lib/compute.d.ts.map +1 -1
  50. package/lib/compute.js +79 -3
  51. package/lib/compute.js.map +1 -1
  52. package/lib/config/env-config.d.ts +6 -0
  53. package/lib/config/env-config.d.ts.map +1 -1
  54. package/lib/config/env-config.js +19 -1
  55. package/lib/config/env-config.js.map +1 -1
  56. package/lib/container/AiInbox.d.ts +15 -0
  57. package/lib/container/AiInbox.d.ts.map +1 -0
  58. package/lib/container/AiInbox.js +1520 -0
  59. package/lib/container/AiInbox.js.map +1 -0
  60. package/lib/container/AiInboxWithLoader.d.ts +36 -0
  61. package/lib/container/AiInboxWithLoader.d.ts.map +1 -0
  62. package/lib/container/AiInboxWithLoader.js +300 -0
  63. package/lib/container/AiInboxWithLoader.js.map +1 -0
  64. package/lib/container/AiLandingInput.d.ts +4 -0
  65. package/lib/container/AiLandingInput.d.ts.map +1 -0
  66. package/lib/container/AiLandingInput.js +164 -0
  67. package/lib/container/AiLandingInput.js.map +1 -0
  68. package/lib/container/Inbox.d.ts.map +1 -1
  69. package/lib/container/Inbox.js +6 -4
  70. package/lib/container/Inbox.js.map +1 -1
  71. package/lib/container/InboxAiMessagesLoader.d.ts +36 -0
  72. package/lib/container/InboxAiMessagesLoader.d.ts.map +1 -0
  73. package/lib/container/InboxAiMessagesLoader.js +47 -0
  74. package/lib/container/InboxAiMessagesLoader.js.map +1 -0
  75. package/lib/container/InboxContainer.d.ts +12 -0
  76. package/lib/container/InboxContainer.d.ts.map +1 -0
  77. package/lib/container/InboxContainer.js +31 -0
  78. package/lib/container/InboxContainer.js.map +1 -0
  79. package/lib/container/InboxTemplate1.d.ts +15 -0
  80. package/lib/container/InboxTemplate1.d.ts.map +1 -0
  81. package/lib/container/InboxTemplate1.js +1375 -0
  82. package/lib/container/InboxTemplate1.js.map +1 -0
  83. package/lib/container/InboxTemplate1WithLoader.d.ts +36 -0
  84. package/lib/container/InboxTemplate1WithLoader.d.ts.map +1 -0
  85. package/lib/container/InboxTemplate2.d.ts +15 -0
  86. package/lib/container/InboxTemplate2.d.ts.map +1 -0
  87. package/lib/container/InboxTemplate2.js +1426 -0
  88. package/lib/container/InboxTemplate2.js.map +1 -0
  89. package/lib/container/InboxWithAiLoader.d.ts +15 -0
  90. package/lib/container/InboxWithAiLoader.d.ts.map +1 -0
  91. package/lib/container/InboxWithAiLoader.js +56 -0
  92. package/lib/container/InboxWithAiLoader.js.map +1 -0
  93. package/lib/container/ServiceInbox.js +1 -1
  94. package/lib/container/ServiceInbox.js.map +1 -1
  95. package/lib/container/ThreadMessages.js +1 -1
  96. package/lib/container/ThreadMessages.js.map +1 -1
  97. package/lib/container/ThreadMessagesInbox.js +1 -1
  98. package/lib/container/ThreadMessagesInbox.js.map +1 -1
  99. package/lib/container/Threads.js +1 -1
  100. package/lib/container/Threads.js.map +1 -1
  101. package/lib/container/index.d.ts +4 -1
  102. package/lib/container/index.d.ts.map +1 -1
  103. package/lib/index.js +1 -1
  104. package/lib/machines/aiAgentMachine.d.ts +3 -0
  105. package/lib/machines/aiAgentMachine.d.ts.map +1 -0
  106. package/lib/machines/aiAgentMachine.js +1040 -0
  107. package/lib/machines/aiAgentMachine.js.map +1 -0
  108. package/lib/machines/types.d.ts +77 -0
  109. package/lib/machines/types.d.ts.map +1 -0
  110. package/lib/routes.json +40 -0
  111. package/lib/templates/InboxWithAi.d.ts +15 -0
  112. package/lib/templates/InboxWithAi.d.ts.map +1 -0
  113. package/lib/templates/InboxWithAi.js +405 -0
  114. package/lib/templates/InboxWithAi.js.map +1 -0
  115. package/lib/templates/InboxWithAi.tsx +502 -0
  116. package/package.json +7 -5
  117. package/src/components/AIAgent/AIAgent.tsx +1351 -0
  118. package/src/components/AIAgent/README.md +82 -0
  119. package/src/components/AIAgent/index.ts +1 -0
  120. package/src/components/InboxMessage/InputComponent.tsx +263 -0
  121. package/src/components/InboxMessage/MessageInput.tsx +73 -66
  122. package/src/components/InboxMessage/MessageInputComponent.tsx +245 -0
  123. package/src/components/InboxMessage/Messages.tsx +2 -56
  124. package/src/components/InboxMessage/MessagesBuilderUi.tsx +205 -0
  125. package/src/components/InboxMessage/UploadImageButton.tsx +3 -2
  126. package/src/components/InboxMessage/index.ts +3 -0
  127. package/src/components/InboxMessage/message-widgets/CommonMessage.tsx +39 -21
  128. package/src/components/InboxMessage/message-widgets/ModernMessageGroup.tsx +1968 -0
  129. package/src/components/InboxMessage/message-widgets/PlainMessage.tsx +6 -2
  130. package/src/components/InboxMessage/message-widgets/SlackLikeMessageGroup.tsx +306 -54
  131. package/src/components/InboxMessage/message-widgets/index.ts +1 -0
  132. package/src/components/index.ts +4 -0
  133. package/src/compute.ts +83 -2
  134. package/src/config/env-config.ts +6 -0
  135. package/src/container/AiInbox.tsx +1796 -0
  136. package/src/container/AiInboxWithLoader.tsx +356 -0
  137. package/src/container/AiLandingInput.tsx +168 -0
  138. package/src/container/Inbox.tsx +8 -5
  139. package/src/container/InboxAiMessagesLoader.tsx +68 -0
  140. package/src/container/InboxContainer.tsx +35 -0
  141. package/src/container/InboxTemplate1.tsx +1542 -0
  142. package/src/container/InboxTemplate1WithLoader.tsx +338 -0
  143. package/src/container/InboxTemplate2.tsx +1606 -0
  144. package/src/container/InboxWithAiLoader.tsx +76 -0
  145. package/src/container/index.ts +15 -1
  146. package/src/machines/aiAgentMachine.ts +1248 -0
  147. package/src/machines/types.ts +59 -0
  148. package/src/templates/InboxWithAi.tsx +502 -0
@@ -0,0 +1,1248 @@
1
+ import { createMachine, assign, fromPromise } from 'xstate';
2
+ import { AIAgentContext, AIAgentEvent, Message, MCPData } from './types';
3
+ import { config } from '../config';
4
+ const { CLIENT_URL, VITE_ANTHROPIC_API_KEY, VITE_OPENAI_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, NEWS_API_KEY } =
5
+ config;
6
+ const env = {
7
+ ANTHROPIC_API_KEY: VITE_ANTHROPIC_API_KEY || ANTHROPIC_API_KEY,
8
+ OPENAI_API_KEY: VITE_OPENAI_API_KEY || OPENAI_API_KEY,
9
+ NEWS_API_KEY: NEWS_API_KEY,
10
+ };
11
+
12
+ // API configuration - using environment variables from dev.env
13
+ const API_CONFIG = {
14
+ // Use environment variables for API keys
15
+ ANTHROPIC_API_KEY: env?.ANTHROPIC_API_KEY,
16
+ OPENAI_API_KEY: env?.OPENAI_API_KEY,
17
+ NEWS_API_KEY: env?.NEWS_API_KEY,
18
+
19
+ // Configurable proxy endpoints
20
+ ANTHROPIC_ENDPOINT: '/api/anthropic/messages',
21
+ OPENAI_ENDPOINT: '/api/openai',
22
+
23
+ // Direct MCP search APIs (these are public and don't have CORS issues)
24
+ NEWS_API_ENDPOINT: 'https://newsapi.org/v2/everything',
25
+ REDDIT_SEARCH_ENDPOINT: 'https://www.reddit.com/search.json',
26
+ REDDIT_TRENDING_ENDPOINT: 'https://www.reddit.com/r/news/hot.json',
27
+ DUCKDUCKGO_ENDPOINT: 'https://api.duckduckgo.com/',
28
+
29
+ // Model configuration
30
+ DEFAULT_MODEL: 'claude-3-5-haiku-20241022',
31
+ OPENAI_MODEL: 'gpt-4o-mini',
32
+
33
+ // Check which AI provider is available
34
+ get availableProvider() {
35
+ if (this.ANTHROPIC_API_KEY) return 'anthropic';
36
+ if (this.OPENAI_API_KEY) return 'openai';
37
+ return null;
38
+ },
39
+
40
+ // Get the appropriate endpoint based on available provider
41
+ get primaryEndpoint() {
42
+ return this.availableProvider === 'anthropic' ? this.ANTHROPIC_ENDPOINT : this.OPENAI_ENDPOINT;
43
+ },
44
+
45
+ // Get the appropriate model based on available provider
46
+ get primaryModel() {
47
+ return this.availableProvider === 'anthropic' ? this.DEFAULT_MODEL : this.OPENAI_MODEL;
48
+ },
49
+ };
50
+
51
+ // Log configuration on startup
52
+ console.log('🤖 AI Agent Machine Configuration:');
53
+ console.log(` Available Provider: ${API_CONFIG.availableProvider || 'None'}`);
54
+ console.log(` Primary Endpoint: ${API_CONFIG.primaryEndpoint}`);
55
+ console.log(` Primary Model: ${API_CONFIG.primaryModel}`);
56
+ console.log(` Using Proxy for AI APIs: ✅ (avoids CORS issues)`);
57
+ console.log(` Using Direct APIs for MCP Search: ✅ (public APIs)`);
58
+ console.log(` Anthropic API Key: ${API_CONFIG.ANTHROPIC_API_KEY ? '✅ Configured' : '❌ Not configured'}`);
59
+ console.log(` OpenAI API Key: ${API_CONFIG.OPENAI_API_KEY ? '✅ Configured' : '❌ Not configured'}`);
60
+ console.log(` News API Key: ${API_CONFIG.NEWS_API_KEY ? '✅ Configured' : '❌ Not configured'}`);
61
+
62
+ // Test API configuration
63
+ if (!API_CONFIG.availableProvider) {
64
+ console.error('🚨 CRITICAL: No AI API provider configured!');
65
+ console.error('🚨 Please set VITE_ANTHROPIC_API_KEY or VITE_OPENAI_API_KEY in your environment variables.');
66
+ } else {
67
+ console.log(`✅ AI API provider configured: ${API_CONFIG.availableProvider}`);
68
+ }
69
+
70
+ // Helper function to get the base URL for proxy calls
71
+ function getProxyBaseUrl(): string {
72
+ // Use Vite dev server proxy to avoid CORS issues
73
+ return CLIENT_URL || 'http://localhost:3011'; // Vite dev server
74
+ }
75
+
76
+ // Log the current proxy configuration
77
+ console.log(`🌐 Proxy Configuration: ${getProxyBaseUrl() || 'Relative paths (production)'}`);
78
+
79
+ // Helper function to make API calls through proxy (avoids CORS)
80
+ async function makeApiCall(endpoint: string, options: RequestInit) {
81
+ try {
82
+ const baseUrl = getProxyBaseUrl();
83
+ const url = `${baseUrl}${endpoint}`;
84
+
85
+ console.log(`🌐 Making API call through proxy to: ${url}`);
86
+ console.log(`📤 Request options:`, {
87
+ method: options.method,
88
+ headers: options.headers,
89
+ body: options.body ? JSON.parse(options.body as string) : undefined,
90
+ });
91
+
92
+ const response = await fetch(url, options);
93
+
94
+ if (!response.ok) {
95
+ const errorData = await response.json().catch(() => ({}));
96
+ console.error('🚨 API Error:', response.status, errorData);
97
+
98
+ let errorMessage = 'API request failed';
99
+ if (errorData.error) {
100
+ if (typeof errorData.error === 'string') {
101
+ errorMessage = errorData.error;
102
+ } else if (errorData.error.message) {
103
+ errorMessage = errorData.error.message;
104
+ } else if (errorData.error.type) {
105
+ errorMessage = `${errorData.error.type}: ${errorData.error.message || 'Unknown error'}`;
106
+ }
107
+ }
108
+
109
+ throw new Error(errorMessage);
110
+ }
111
+
112
+ return response;
113
+ } catch (error) {
114
+ console.error('🚨 API call error:', error);
115
+ throw error;
116
+ }
117
+ }
118
+
119
+ // Helper function to fetch news from NewsAPI directly
120
+ async function fetchNewsFromNewsAPI(query: string) {
121
+ if (!API_CONFIG.NEWS_API_KEY) {
122
+ console.warn('⚠️ News API key not found, skipping NewsAPI');
123
+ return [];
124
+ }
125
+
126
+ try {
127
+ const response = await fetch(
128
+ `${API_CONFIG.NEWS_API_ENDPOINT}?q=${encodeURIComponent(query)}&apiKey=${
129
+ API_CONFIG.NEWS_API_KEY
130
+ }&language=en&sortBy=publishedAt&pageSize=5`,
131
+ );
132
+
133
+ if (!response.ok) {
134
+ console.error('NewsAPI error:', response.status);
135
+ return [];
136
+ }
137
+
138
+ const data = await response.json();
139
+
140
+ if (data.articles) {
141
+ return data.articles.map((article: any) => ({
142
+ title: article.title,
143
+ url: article.url,
144
+ description: article.description || article.title,
145
+ publishedAt: article.publishedAt,
146
+ source: { name: article.source.name },
147
+ }));
148
+ }
149
+ } catch (error) {
150
+ console.error('NewsAPI error:', error);
151
+ }
152
+
153
+ return [];
154
+ }
155
+
156
+ // Helper function to fetch from Reddit API directly
157
+ async function fetchAlternativeNews(query: string) {
158
+ try {
159
+ const response = await fetch(
160
+ `${API_CONFIG.REDDIT_SEARCH_ENDPOINT}?q=${encodeURIComponent(
161
+ query,
162
+ )}&sort=new&t=day&limit=3&restrict_sr=false&type=link`,
163
+ );
164
+
165
+ if (!response.ok) {
166
+ console.error('Reddit API error:', response.status);
167
+ return [];
168
+ }
169
+
170
+ const data = await response.json();
171
+
172
+ if (data?.data?.children) {
173
+ return data.data.children
174
+ .filter((post: any) => post.data.url && !post.data.is_self)
175
+ .slice(0, 3)
176
+ .map((post: any) => ({
177
+ title: post.data.title,
178
+ url: post.data.url,
179
+ description: `Discussion: ${post.data.title} (${post.data.score} upvotes)`,
180
+ publishedAt: new Date(post.data.created_utc * 1000).toISOString(),
181
+ source: { name: `r/${post.data.subreddit}` },
182
+ }));
183
+ }
184
+ } catch (error) {
185
+ console.error('Reddit API error:', error);
186
+ }
187
+
188
+ return [];
189
+ }
190
+
191
+ // Helper function to search DuckDuckGo directly
192
+ async function searchDuckDuckGo(query: string) {
193
+ try {
194
+ const response = await fetch(
195
+ `${API_CONFIG.DUCKDUCKGO_ENDPOINT}?q=${encodeURIComponent(query)}&format=json&no_html=1&skip_disambig=1`,
196
+ );
197
+
198
+ if (!response.ok) {
199
+ console.error('DuckDuckGo API error:', response.status);
200
+ return [];
201
+ }
202
+
203
+ const data = await response.json();
204
+ const results: any[] = [];
205
+
206
+ // Add abstract if available
207
+ if (data.Abstract) {
208
+ results.push({
209
+ title: data.Heading || `About ${query}`,
210
+ url: data.AbstractURL || `https://duckduckgo.com/?q=${encodeURIComponent(query)}`,
211
+ snippet: data.Abstract,
212
+ source: 'DuckDuckGo',
213
+ });
214
+ }
215
+
216
+ // Add related topics
217
+ if (data.RelatedTopics && data.RelatedTopics.length > 0) {
218
+ data.RelatedTopics.slice(0, 3).forEach((topic: any) => {
219
+ if (topic.Text && topic.FirstURL) {
220
+ results.push({
221
+ title: topic.Text.split(' - ')[0] || topic.Text.substring(0, 60),
222
+ url: topic.FirstURL,
223
+ snippet: topic.Text,
224
+ source: 'DuckDuckGo',
225
+ });
226
+ }
227
+ });
228
+ }
229
+
230
+ return results;
231
+ } catch (error) {
232
+ console.error('DuckDuckGo API error:', error);
233
+ return [];
234
+ }
235
+ }
236
+
237
+ // Helper function to get trending news from Reddit directly
238
+ async function getTrendingNews() {
239
+ try {
240
+ const response = await fetch(`${API_CONFIG.REDDIT_TRENDING_ENDPOINT}?limit=5`);
241
+
242
+ if (!response.ok) {
243
+ console.error('Reddit trending error:', response.status);
244
+ return [];
245
+ }
246
+
247
+ const data = await response.json();
248
+
249
+ if (data?.data?.children) {
250
+ return data.data.children
251
+ .filter((post: any) => post.data.url && !post.data.is_self)
252
+ .map((post: any) => ({
253
+ title: post.data.title,
254
+ url: post.data.url,
255
+ description: `Trending: ${post.data.title} (${post.data.score} upvotes, ${post.data.num_comments} comments)`,
256
+ publishedAt: new Date(post.data.created_utc * 1000).toISOString(),
257
+ source: { name: 'r/news' },
258
+ }));
259
+ }
260
+ } catch (error) {
261
+ console.error('Reddit trending error:', error);
262
+ }
263
+
264
+ return [];
265
+ }
266
+
267
+ // Direct MCP search function - no more server dependency
268
+ async function performDirectMCPSearch(query: string, includeNews: boolean = true, includeWeb: boolean = true) {
269
+ try {
270
+ console.log(`🔍 Performing direct MCP search for: "${query}" (news: ${includeNews}, web: ${includeWeb})`);
271
+
272
+ const promises = [];
273
+
274
+ // Fetch news if requested
275
+ if (includeNews) {
276
+ // Try multiple news sources
277
+ promises.push(fetchNewsFromNewsAPI(query), fetchAlternativeNews(query));
278
+
279
+ // For US news specifically, get trending
280
+ if (query.toLowerCase().includes('us news') || query.toLowerCase().includes('american news')) {
281
+ promises.push(getTrendingNews());
282
+ }
283
+ }
284
+
285
+ // Fetch web search if requested
286
+ if (includeWeb) {
287
+ promises.push(searchDuckDuckGo(query));
288
+ }
289
+
290
+ // Execute all searches concurrently
291
+ const results = await Promise.allSettled(promises);
292
+
293
+ // Combine all successful results
294
+ let searchResults: any[] = [];
295
+ let newsArticles: any[] = [];
296
+
297
+ results.forEach((result, index) => {
298
+ if (result.status === 'fulfilled' && Array.isArray(result.value)) {
299
+ if (includeWeb && index === results.length - 1) {
300
+ // Last promise is web search
301
+ searchResults = result.value;
302
+ } else if (includeNews) {
303
+ // Other promises are news sources
304
+ newsArticles = [...newsArticles, ...result.value];
305
+ }
306
+ }
307
+ });
308
+
309
+ // Remove duplicates and limit results
310
+ newsArticles = newsArticles
311
+ .filter((article, index, self) => index === self.findIndex((a: any) => a.title === article.title))
312
+ .slice(0, 8);
313
+
314
+ searchResults = searchResults.slice(0, 5);
315
+
316
+ const response = {
317
+ searchResults: includeWeb ? searchResults : [],
318
+ newsArticles: includeNews ? newsArticles : [],
319
+ summary: `Found ${searchResults.length} web results and ${newsArticles.length} news articles for "${query}"`,
320
+ sources: [
321
+ ...(includeWeb ? ['DuckDuckGo'] : []),
322
+ ...(includeNews ? ['NewsAPI', 'Reddit', 'Alternative Sources'] : []),
323
+ ].filter(Boolean),
324
+ searchTime: Date.now(),
325
+ realData: true, // Flag to indicate this is real data
326
+ };
327
+
328
+ console.log(
329
+ `✅ Direct MCP search completed: ${response.searchResults.length} web + ${response.newsArticles.length} news results`,
330
+ );
331
+
332
+ return response;
333
+ } catch (error) {
334
+ console.error('🚨 Direct MCP search error:', error);
335
+ throw error;
336
+ }
337
+ }
338
+
339
+ // Helper function to detect if a query needs multi-message response
340
+ function needsMultiMessageResponse(query: string): boolean {
341
+ const multiMessageKeywords = [
342
+ 'explain step by step',
343
+ 'break down',
344
+ 'tutorial',
345
+ 'guide',
346
+ 'how to',
347
+ 'teach me',
348
+ 'walk me through',
349
+ 'step-by-step',
350
+ 'detailed explanation',
351
+ 'in detail',
352
+ 'comprehensive',
353
+ 'thorough',
354
+ 'complete guide',
355
+ 'coding',
356
+ 'programming',
357
+ 'development',
358
+ 'build',
359
+ 'create',
360
+ 'multiple',
361
+ 'several',
362
+ 'various',
363
+ 'different ways',
364
+ ];
365
+
366
+ const lowerQuery = query.toLowerCase();
367
+ return multiMessageKeywords.some((keyword) => lowerQuery.includes(keyword)) || query.length > 200; // Long queries often benefit from multi-message responses
368
+ }
369
+
370
+ // Helper function to detect if a query needs real-time data
371
+ function needsRealTimeData(query: string): boolean {
372
+ const realTimeKeywords = [
373
+ 'latest',
374
+ 'recent',
375
+ 'news',
376
+ 'current',
377
+ 'today',
378
+ 'yesterday',
379
+ 'breaking',
380
+ 'update',
381
+ 'now',
382
+ 'price',
383
+ 'stock',
384
+ 'weather',
385
+ 'trending',
386
+ 'popular',
387
+ 'search',
388
+ "what's happening",
389
+ 'tell me about',
390
+ 'find',
391
+ 'look up',
392
+ 'research',
393
+ ];
394
+
395
+ const lowerQuery = query.toLowerCase();
396
+ return realTimeKeywords.some((keyword) => lowerQuery.includes(keyword));
397
+ }
398
+
399
+ // Helper function to format MCP data for the agent
400
+ function formatMCPDataForAgent(mcpData: MCPData): string {
401
+ let context = '';
402
+
403
+ if (mcpData.searchResults && mcpData.searchResults.length > 0) {
404
+ context += 'Current web search results:\n';
405
+ mcpData.searchResults.forEach((result, i) => {
406
+ context += `${i + 1}. **${result.title}**\n ${result.snippet}\n Source: ${result.url}\n\n`;
407
+ });
408
+ }
409
+
410
+ if (mcpData.newsArticles && mcpData.newsArticles.length > 0) {
411
+ context += 'Recent news articles:\n';
412
+ mcpData.newsArticles.forEach((article, i) => {
413
+ context += `${i + 1}. **${article.title}**\n ${article.description}\n Published: ${new Date(
414
+ article.publishedAt,
415
+ ).toLocaleDateString()}\n Source: ${article.source.name}\n\n`;
416
+ });
417
+ }
418
+
419
+ if (context) {
420
+ context =
421
+ 'Here is the current information I have access to:\n\n' +
422
+ context +
423
+ '\nPlease use this information to provide an accurate, up-to-date response.';
424
+ }
425
+
426
+ return context;
427
+ }
428
+
429
+ // Helper function to get the appropriate system prompt based on provider
430
+ function getSystemPrompt(provider: string, includeMCP: boolean = false): string {
431
+ const basePrompt = includeMCP
432
+ ? 'You are a helpful AI assistant that can access real-time information through web search and news APIs. When provided with current information, use it to give accurate, up-to-date responses. Always cite your sources when referencing specific information from search results or news articles. You excel at providing comprehensive analysis and insights based on the latest data available.'
433
+ : 'You are a helpful AI assistant that provides detailed, step-by-step responses. Focus on the specific aspect requested and provide practical, actionable information.';
434
+
435
+ if (provider === 'anthropic') {
436
+ return basePrompt;
437
+ } else if (provider === 'openai') {
438
+ return basePrompt;
439
+ }
440
+
441
+ return basePrompt;
442
+ }
443
+
444
+ // Helper function to get conversation context from regular messages
445
+ function getConversationContext(regularMessages: any[]): string {
446
+ if (!regularMessages || regularMessages.length === 0) return '';
447
+
448
+ // Get the last few messages for context
449
+ const recentMessages = regularMessages.slice(-5);
450
+ let context = 'Recent conversation context:\n';
451
+
452
+ recentMessages.forEach((msg, index) => {
453
+ const author = msg.author?.username || msg.author?.givenName || 'User';
454
+ const content = msg.message?.substring(0, 200) || ''; // Limit content length
455
+ if (content.trim()) {
456
+ context += `${index + 1}. ${author}: ${content}\n`;
457
+ }
458
+ });
459
+
460
+ return context + '\n';
461
+ }
462
+
463
+ export const aiAgentMachine = createMachine(
464
+ {
465
+ /** @xstate-layout N4IgpgJg5mDOIC5QEMCWBBGA7ALgOlQgBswBiAZQFEA5AEQH0BZS889AcUoG0AGAXUSgADgHtYqHKhFZBIAB6IA7ACZleAGwBWdYoAsmngA4AzAE5Fi4wBoQAT0S6AjKbzHHy06aO7Du3eoBfAJs0TDBcAmIyAElqAAUAVQAVegBhAAl0ak5eASQQUXFJaVkFBE1dF0dvXx49Q19NG3sEXUUXHz9lRydFA2dNIJCMbHxCElJUgBlKdAAleko5uYB5OdzZQokpGXyyzU1jPFN9ZXVdHmN9dUNmxGNfPB7NRUNnR0N1ZX0hkFDRvDILDIIi2ABeqCwUFIG3yW2Ku1AZXcykUGlMhk0hh4PEcenU5zurV0R16yiupkODV+-3C+CBIPBkOhXEceWEYm2JT2iBRaPUGKxOLx-kJdkQ33UeA8ZnUxiuulRuhpIzpeAAZmAcABjAAWzNoyBwyFIEGkYAIWAAbiIANYWzU63WMVJxQ3G2Ecoo7Uq87FS8lnE6KardInGc54TTKF48bQVE4VQbBP6qiKOvUGo0msAAJ1zIlzeCERCN6sLAFsNVq9S63dnPQVOQjfQgPjwA8Yg21Q44idc8J8LspcapHMYeMqU7SIkIC9q4OIoabzZabfa8Ng80awHM4KIsLAwI34T6eW3-dKuwKezww+LynLjoZVDxPKiu20VWFZ-PF8zSDzAsixLMtK03cJtxwXd92kI8T2bM8kT9Ooo0sN9HEwrpLiJN4nkMSxTEwgjFHUONFG-AEgMLUg5koJI5gATQQ71uWQhByU0KMfDOao3zfZRbgfFQpVqaNtAJTRiMotVqNzCgaAYZhWA4bh+E2RC2PkCVjC4ioX3UPivFMQSiW+HhjlI-xugFD4Xgo6c03wOTSFiRIUgyLIcnUuFNMRbTWmUIlDN0fDsUxPofCMYwZIiFzplmBYllWdYfK9Ll-LKTjuIMoyBKEloEw0KS3zImUzAclMsBECA4FkGccA01jMsQABadQiVarjPB63revaWKxiiJqMtbTQTjwfijH4vxxtMIkXhcF8JzIoi5XaGLHJ-elgVBCEoRGltz2qJxpUcc5elIyk5X7S5Bx0HE4yIypLEG6snSzY1DqQgKehfPBh3ld9cTjawHycQxJq+Ui3wjEy8U24ZtuLP9YCXKBvq0rK4wsr5DhDLwe10fsYyeO930Mkw9HMN65MxlqEEeO9qbleVun8RQiRDULox0GMYwjTFAiCAIgA */
466
+ id: 'aiAgent',
467
+ types: {} as {
468
+ context: AIAgentContext;
469
+ events: AIAgentEvent;
470
+ },
471
+ initial: 'idle',
472
+ context: {
473
+ messages: [],
474
+ currentInput: '',
475
+ error: null,
476
+ isTyping: false,
477
+ multiMessageMode: false,
478
+ currentMultiStep: 0,
479
+ totalMultiSteps: 0,
480
+ multiMessagePlan: [],
481
+ mcpData: undefined,
482
+ regularMessages: [], // Add regular messages from query
483
+ },
484
+ states: {
485
+ idle: {
486
+ on: {
487
+ SEND_MESSAGE: {
488
+ target: 'analyzing',
489
+ guard: 'hasValidMessage',
490
+ actions: 'addUserMessage',
491
+ },
492
+ FORCE_MULTI_MESSAGE: {
493
+ target: 'analyzing',
494
+ guard: 'hasValidMessage',
495
+ actions: 'addUserMessage',
496
+ },
497
+ AUTO_RESPOND_TO_MESSAGE: {
498
+ target: 'processing',
499
+ guard: 'hasValidMessage',
500
+ actions: 'addAutoResponseMessage',
501
+ },
502
+ INPUT_CHANGE: {
503
+ actions: 'updateInput',
504
+ },
505
+ CLEAR_ERROR: {
506
+ actions: 'clearError',
507
+ },
508
+ UPDATE_REGULAR_MESSAGES: {
509
+ actions: 'updateRegularMessages',
510
+ },
511
+ ANALYZE_EXISTING_CONTEXT: {
512
+ target: 'analyzing',
513
+ guard: 'hasExistingContext',
514
+ actions: 'addContextAnalysisMessage',
515
+ },
516
+ CONTINUE_PROCESSING: {
517
+ // Just stay in idle state to trigger the useEffect again
518
+ actions: 'logContinueProcessing',
519
+ },
520
+ },
521
+ },
522
+ analyzing: {
523
+ entry: 'setTyping',
524
+ always: [
525
+ {
526
+ target: 'planningMultiMessage',
527
+ guard: 'shouldUseMultiMessage',
528
+ },
529
+ {
530
+ target: 'fetchingData',
531
+ guard: 'queryNeedsRealTimeData',
532
+ },
533
+ {
534
+ target: 'processing',
535
+ },
536
+ ],
537
+ },
538
+ planningMultiMessage: {
539
+ entry: 'setMultiMessageMode',
540
+ invoke: {
541
+ id: 'generateMultiMessagePlan',
542
+ src: 'generateMultiMessagePlan',
543
+ input: ({ context }) => ({
544
+ query: context.messages[context.messages.length - 1]?.content || '',
545
+ regularMessages: context.regularMessages,
546
+ }),
547
+ onDone: {
548
+ target: 'multiProcessing',
549
+ actions: 'setMultiMessagePlan',
550
+ },
551
+ onError: {
552
+ target: 'processing', // Fall back to single message
553
+ },
554
+ },
555
+ },
556
+ multiProcessing: {
557
+ invoke: {
558
+ id: 'generateMultiMessage',
559
+ src: 'generateMultiMessage',
560
+ input: ({ context }) => ({
561
+ messages: context.messages,
562
+ plan: context.multiMessagePlan,
563
+ currentStep: context.currentMultiStep,
564
+ mcpData: context.mcpData,
565
+ regularMessages: context.regularMessages,
566
+ }),
567
+ onDone: [
568
+ {
569
+ target: 'waitingBetweenMessages',
570
+ guard: 'hasMoreMultiMessages',
571
+ actions: ['addAIMessage', 'incrementMultiStep'],
572
+ },
573
+ {
574
+ target: 'idle',
575
+ actions: ['addAIMessage', 'resetMultiMessage'],
576
+ },
577
+ ],
578
+ onError: {
579
+ target: 'error',
580
+ actions: 'setError',
581
+ },
582
+ },
583
+ },
584
+ waitingBetweenMessages: {
585
+ entry: 'setTyping',
586
+ after: {
587
+ 1500: 'multiProcessing',
588
+ },
589
+ },
590
+ fetchingData: {
591
+ invoke: {
592
+ id: 'fetchMCPData',
593
+ src: 'fetchMCPData',
594
+ input: ({ context }) => ({
595
+ query: context.messages[context.messages.length - 1]?.content || '',
596
+ }),
597
+ onDone: {
598
+ target: 'processing',
599
+ actions: 'setMCPData',
600
+ },
601
+ onError: {
602
+ target: 'processing',
603
+ },
604
+ },
605
+ },
606
+ processing: {
607
+ entry: () => {
608
+ console.log('🤖 Entering processing state');
609
+ },
610
+ invoke: {
611
+ id: 'generateResponse',
612
+ src: 'generateAIResponse',
613
+ input: ({ context }) => {
614
+ console.log('🤖 generateAIResponse input:', {
615
+ messagesCount: context.messages.length,
616
+ hasMessageToRespondTo: !!context.messageToRespondTo,
617
+ messageToRespondTo: context.messageToRespondTo,
618
+ regularMessagesCount: context.regularMessages?.length || 0,
619
+ });
620
+ return {
621
+ messages: context.messages,
622
+ mcpData: context.mcpData,
623
+ regularMessages: context.regularMessages,
624
+ messageToRespondTo: context.messageToRespondTo,
625
+ };
626
+ },
627
+ onDone: {
628
+ target: 'idle',
629
+ actions: [
630
+ assign(({ context, event }) => {
631
+ console.log('🤖 🎯 onDone transition triggered with event:', event);
632
+ console.log('🤖 🎯 Event type:', typeof event);
633
+ console.log('🤖 🎯 Event content:', event);
634
+ return context;
635
+ }),
636
+ 'addAIMessage',
637
+ ],
638
+ },
639
+ onError: {
640
+ target: 'error',
641
+ actions: 'setError',
642
+ },
643
+ },
644
+ },
645
+ error: {
646
+ on: {
647
+ RETRY: {
648
+ target: 'processing',
649
+ },
650
+ SEND_MESSAGE: {
651
+ target: 'analyzing',
652
+ guard: 'hasValidMessage',
653
+ actions: 'addUserMessage',
654
+ },
655
+ INPUT_CHANGE: {
656
+ actions: 'updateInput',
657
+ },
658
+ CLEAR_ERROR: {
659
+ target: 'idle',
660
+ actions: 'clearError',
661
+ },
662
+ UPDATE_REGULAR_MESSAGES: {
663
+ actions: 'updateRegularMessages',
664
+ },
665
+ },
666
+ },
667
+ },
668
+ },
669
+ {
670
+ guards: {
671
+ hasValidMessage: ({ event }) => {
672
+ console.log('🔍 hasValidMessage guard called with event:', event);
673
+
674
+ if (event.type === 'AUTO_RESPOND_TO_MESSAGE') {
675
+ const isValid = event.message?.trim().length > 0;
676
+ console.log('🔍 AUTO_RESPOND_TO_MESSAGE guard result:', isValid, 'message:', event.message);
677
+ return isValid;
678
+ }
679
+
680
+ if (event.type === 'SEND_MESSAGE' || event.type === 'FORCE_MULTI_MESSAGE') {
681
+ const isValid = event.message?.trim().length > 0;
682
+ console.log(
683
+ '🔍 SEND_MESSAGE/FORCE_MULTI_MESSAGE guard result:',
684
+ isValid,
685
+ 'message:',
686
+ event.message,
687
+ );
688
+ return isValid;
689
+ }
690
+
691
+ console.log('🔍 Unknown event type in hasValidMessage guard:', event.type);
692
+ return false;
693
+ },
694
+ shouldUseMultiMessage: ({ context, event }) => {
695
+ if (event.type === 'FORCE_MULTI_MESSAGE') return true;
696
+
697
+ const lastMessage = context.messages[context.messages.length - 1];
698
+ if (!lastMessage || lastMessage.sender !== 'user') return false;
699
+
700
+ const shouldUse = needsMultiMessageResponse(lastMessage.content);
701
+ console.log('🔄 Should use multi-message:', shouldUse, 'for:', lastMessage.content);
702
+ return shouldUse;
703
+ },
704
+ queryNeedsRealTimeData: ({ context }) => {
705
+ const lastMessage = context.messages[context.messages.length - 1];
706
+ if (!lastMessage || lastMessage.sender !== 'user') return false;
707
+
708
+ const needs = needsRealTimeData(lastMessage.content);
709
+ console.log('🔍 Query needs real-time data:', needs, 'for:', lastMessage.content);
710
+ return needs;
711
+ },
712
+ hasMoreMultiMessages: ({ context }) => {
713
+ return context.currentMultiStep! < context.totalMultiSteps! - 1;
714
+ },
715
+ hasExistingContext: ({ context }) => {
716
+ return context.regularMessages && context.regularMessages.length > 0;
717
+ },
718
+ },
719
+ actions: {
720
+ addUserMessage: assign(({ context, event }) => {
721
+ if (event.type !== 'SEND_MESSAGE' && event.type !== 'FORCE_MULTI_MESSAGE') return context;
722
+ const newMessage: Message = {
723
+ id: Date.now().toString(),
724
+ content: event.message,
725
+ sender: 'user',
726
+ timestamp: new Date(),
727
+ };
728
+ return {
729
+ ...context,
730
+ messages: [...context.messages, newMessage],
731
+ currentInput: '',
732
+ error: null,
733
+ mcpData: undefined,
734
+ messageToRespondTo: undefined, // Clear the message to respond to when adding new user message
735
+ };
736
+ }),
737
+ addAIMessage: assign(({ context, event }) => {
738
+ console.log('🤖 addAIMessage called with event:', event);
739
+
740
+ // Extract the AI response content from the event
741
+ let aiResponseContent = '';
742
+
743
+ // Handle different event formats
744
+ if (event && typeof event === 'object') {
745
+ if ('output' in event) {
746
+ aiResponseContent = (event as any).output;
747
+ } else if ('data' in event) {
748
+ aiResponseContent = (event as any).data;
749
+ } else if (typeof event === 'string') {
750
+ // Direct string response from generateAIResponse actor
751
+ aiResponseContent = event;
752
+ } else {
753
+ console.error('🤖 Could not extract AI response content from event:', event);
754
+ aiResponseContent = 'Sorry, I encountered an error generating the response.';
755
+ }
756
+ } else if (typeof event === 'string') {
757
+ // Direct string response from generateAIResponse actor
758
+ aiResponseContent = event;
759
+ } else {
760
+ console.error('🤖 Could not extract AI response content from event:', event);
761
+ aiResponseContent = 'Sorry, I encountered an error generating the response.';
762
+ }
763
+
764
+ console.log('🤖 AI response content:', aiResponseContent.substring(0, 100) + '...');
765
+
766
+ const newMessage: Message = {
767
+ id: (Date.now() + 1).toString(),
768
+ content: aiResponseContent,
769
+ sender: 'ai',
770
+ timestamp: new Date(),
771
+ };
772
+
773
+ console.log('🤖 Adding AI message to context:', newMessage);
774
+
775
+ return {
776
+ ...context,
777
+ messages: [...context.messages, newMessage],
778
+ isTyping: false,
779
+ error: null,
780
+ messageToRespondTo: undefined, // Clear the message to respond to after processing
781
+ };
782
+ }),
783
+ updateInput: assign(({ context, event }) => {
784
+ if (event.type !== 'INPUT_CHANGE') return context;
785
+ return {
786
+ ...context,
787
+ currentInput: event.value,
788
+ };
789
+ }),
790
+ updateRegularMessages: assign(({ context, event }) => {
791
+ if (event.type !== 'UPDATE_REGULAR_MESSAGES') return context;
792
+ return {
793
+ ...context,
794
+ regularMessages: event.messages || [],
795
+ };
796
+ }),
797
+ setMultiMessageMode: assign(({ context }) => ({
798
+ ...context,
799
+ multiMessageMode: true,
800
+ currentMultiStep: 0,
801
+ })),
802
+ setMultiMessagePlan: assign(({ context, event }) => {
803
+ const plan = (event as unknown as { output: { plan: string[]; totalSteps: number } }).output;
804
+ console.log('📝 Multi-message plan created:', plan.totalSteps, 'steps');
805
+ return {
806
+ ...context,
807
+ multiMessagePlan: plan.plan,
808
+ totalMultiSteps: plan.totalSteps,
809
+ };
810
+ }),
811
+ incrementMultiStep: assign(({ context }) => ({
812
+ ...context,
813
+ currentMultiStep: context.currentMultiStep! + 1,
814
+ })),
815
+ logContinueProcessing: () => {
816
+ console.log('🤖 Continue processing triggered - machine will stay in idle state');
817
+ },
818
+ resetMultiMessage: assign(({ context }) => ({
819
+ ...context,
820
+ multiMessageMode: false,
821
+ currentMultiStep: 0,
822
+ totalMultiSteps: 0,
823
+ multiMessagePlan: [],
824
+ isTyping: false,
825
+ })),
826
+ setTyping: assign(({ context }) => ({
827
+ ...context,
828
+ isTyping: true,
829
+ })),
830
+ setMCPData: assign(({ context, event }) => {
831
+ console.log('📊 MCP data fetched successfully');
832
+ return {
833
+ ...context,
834
+ mcpData: (event as unknown as { output: MCPData }).output,
835
+ };
836
+ }),
837
+ setError: assign(({ context, event }) => {
838
+ console.error('❌ AI processing error:', event);
839
+ let errorMessage = 'An unexpected error occurred';
840
+
841
+ const error = (event as { error: unknown }).error;
842
+ if (error instanceof Error) {
843
+ errorMessage = error.message;
844
+ } else if (typeof error === 'string') {
845
+ errorMessage = error;
846
+ }
847
+
848
+ if (errorMessage.includes('API key')) {
849
+ errorMessage = 'API key not configured. Please set your Anthropic API key.';
850
+ } else if (errorMessage.includes('rate limit')) {
851
+ errorMessage = 'Rate limit exceeded. Please try again in a moment.';
852
+ } else if (errorMessage.includes('network') || errorMessage.includes('fetch')) {
853
+ errorMessage = 'Network error. Please check your connection and try again.';
854
+ }
855
+
856
+ return {
857
+ ...context,
858
+ error: errorMessage,
859
+ isTyping: false,
860
+ };
861
+ }),
862
+ clearError: assign(({ context }) => ({
863
+ ...context,
864
+ error: null,
865
+ })),
866
+ addContextAnalysisMessage: assign(({ context }) => {
867
+ const newMessage: Message = {
868
+ id: (Date.now() + 2).toString(),
869
+ content:
870
+ 'Please analyze the existing conversation and provide insights, continue the discussion, or ask relevant follow-up questions based on the context.',
871
+ sender: 'user',
872
+ timestamp: new Date(),
873
+ };
874
+ return {
875
+ ...context,
876
+ messages: [...context.messages, newMessage],
877
+ currentInput: '',
878
+ error: null,
879
+ };
880
+ }),
881
+ addAutoResponseMessage: assign(({ context, event }) => {
882
+ if (event.type !== 'AUTO_RESPOND_TO_MESSAGE') return context;
883
+
884
+ console.log('🤖 addAutoResponseMessage called with event:', event);
885
+ console.log('🤖 Current context before update:', {
886
+ messagesCount: context.messages.length,
887
+ hasMessageToRespondTo: !!context.messageToRespondTo,
888
+ messageToRespondTo: context.messageToRespondTo,
889
+ });
890
+
891
+ // For auto-responses, we store the message content for AI processing
892
+ // but don't add it as a new user message to avoid duplicates
893
+ const updatedContext = {
894
+ ...context,
895
+ currentInput: '',
896
+ error: null,
897
+ mcpData: undefined,
898
+ // Store the message to respond to without adding it to messages array
899
+ messageToRespondTo: event.message,
900
+ };
901
+
902
+ console.log('🤖 Context updated with messageToRespondTo:', event.message);
903
+ console.log('🤖 Updated context:', {
904
+ messagesCount: updatedContext.messages.length,
905
+ hasMessageToRespondTo: !!updatedContext.messageToRespondTo,
906
+ messageToRespondTo: updatedContext.messageToRespondTo,
907
+ });
908
+
909
+ return updatedContext;
910
+ }),
911
+ },
912
+ actors: {
913
+ generateMultiMessagePlan: fromPromise(
914
+ async ({ input }: { input: { query: string; regularMessages: any[] } }) => {
915
+ try {
916
+ console.log('📋 Generating multi-message plan for:', input.query);
917
+
918
+ // Check if we have an available AI provider
919
+ if (!API_CONFIG.availableProvider) {
920
+ throw new Error(
921
+ 'No AI API key configured. Please set VITE_ANTHROPIC_API_KEY or VITE_OPENAI_API_KEY in your environment.',
922
+ );
923
+ }
924
+
925
+ console.log(`🤖 Using ${API_CONFIG.availableProvider} provider for multi-message planning`);
926
+
927
+ // Add conversation context if available
928
+ let systemPrompt = `You are a planning assistant. Break down complex queries into 3-5 independent messages that can be sent sequentially.
929
+
930
+ Each message should be a complete, standalone response that builds upon the previous ones.
931
+
932
+ Return ONLY a JSON object in this exact format:
933
+ {
934
+ "plan": ["First message topic/focus", "Second message topic/focus", "Third message topic/focus"],
935
+ "totalSteps": 3
936
+ }
937
+
938
+ Keep each step concise but descriptive. Make sure the breakdown makes sense for step-by-step explanations.`;
939
+
940
+ if (input.regularMessages && input.regularMessages.length > 0) {
941
+ const conversationContext = getConversationContext(input.regularMessages);
942
+ systemPrompt = `${systemPrompt}\n\n${conversationContext}`;
943
+ }
944
+
945
+ const response = await makeApiCall(API_CONFIG.primaryEndpoint, {
946
+ method: 'POST',
947
+ headers: {
948
+ 'Content-Type': 'application/json',
949
+ },
950
+ body: JSON.stringify({
951
+ model: API_CONFIG.primaryModel,
952
+ max_tokens: 500,
953
+ system: systemPrompt,
954
+ messages: [
955
+ {
956
+ role: 'user',
957
+ content: `Please create a multi-message plan for this query: "${input.query}"`,
958
+ },
959
+ ],
960
+ }),
961
+ });
962
+
963
+ const data = await response.json();
964
+ const planText = data.content[0]?.text || '';
965
+
966
+ // Parse the JSON response
967
+ try {
968
+ const plan = JSON.parse(planText);
969
+ console.log('✅ Multi-message plan created:', plan);
970
+ return plan;
971
+ } catch {
972
+ // Fallback plan if parsing fails
973
+ return {
974
+ plan: [
975
+ 'Introduction and overview',
976
+ 'Detailed explanation with examples',
977
+ 'Summary and next steps',
978
+ ],
979
+ totalSteps: 3,
980
+ };
981
+ }
982
+ } catch (error) {
983
+ console.error('🚨 Multi-message planning error:', error);
984
+ throw error;
985
+ }
986
+ },
987
+ ),
988
+
989
+ generateMultiMessage: fromPromise(
990
+ async ({
991
+ input,
992
+ }: {
993
+ input: {
994
+ messages: Message[];
995
+ plan?: string[];
996
+ currentStep: number;
997
+ mcpData?: MCPData;
998
+ regularMessages: any[];
999
+ };
1000
+ }) => {
1001
+ console.log(`🔄 Generating multi-message ${input.currentStep + 1}/${input.plan?.length || 1}`);
1002
+
1003
+ try {
1004
+ // Check if we have an available AI provider
1005
+ if (!API_CONFIG.availableProvider) {
1006
+ throw new Error(
1007
+ 'No AI API key configured. Please set VITE_ANTHROPIC_API_KEY or VITE_OPENAI_API_KEY in your environment.',
1008
+ );
1009
+ }
1010
+
1011
+ console.log(`🤖 Using ${API_CONFIG.availableProvider} provider for multi-message generation`);
1012
+ const userMessages = input.messages.filter((msg) => msg.sender === 'user');
1013
+ const latestUserMessage = userMessages[userMessages.length - 1];
1014
+
1015
+ if (!latestUserMessage) {
1016
+ throw new Error('No user message found');
1017
+ }
1018
+
1019
+ const currentStepTopic = input.plan?.[input.currentStep] || 'General response';
1020
+ const stepNumber = input.currentStep + 1;
1021
+ const totalSteps = input.plan?.length || 1;
1022
+
1023
+ let prompt = `User's original question: "${latestUserMessage.content}"
1024
+
1025
+ This is message ${stepNumber} of ${totalSteps} in a multi-message response.
1026
+ Focus specifically on: ${currentStepTopic}
1027
+
1028
+ Guidelines:
1029
+ - Start with a clear indicator like "**Step ${stepNumber}/${totalSteps}: ${currentStepTopic}**"
1030
+ - Provide a complete, standalone response for this specific aspect
1031
+ - Keep it focused and detailed but not overwhelming
1032
+ - Use examples, code snippets, or bullet points where helpful
1033
+ - End with a brief transition to what's coming next (unless it's the final message)`;
1034
+
1035
+ // Add conversation context if available
1036
+ if (input.regularMessages && input.regularMessages.length > 0) {
1037
+ const conversationContext = getConversationContext(input.regularMessages);
1038
+ prompt = `${conversationContext}${prompt}`;
1039
+ }
1040
+
1041
+ // Add MCP context if available
1042
+ if (input.mcpData) {
1043
+ const mcpContext = formatMCPDataForAgent(input.mcpData);
1044
+ if (mcpContext) {
1045
+ prompt = `${mcpContext}\n\n${prompt}`;
1046
+ }
1047
+ }
1048
+
1049
+ const response = await makeApiCall(API_CONFIG.primaryEndpoint, {
1050
+ method: 'POST',
1051
+ headers: {
1052
+ 'Content-Type': 'application/json',
1053
+ },
1054
+ body: JSON.stringify({
1055
+ model: API_CONFIG.primaryModel,
1056
+ max_tokens: 800,
1057
+ system: getSystemPrompt(API_CONFIG.availableProvider || 'anthropic', false),
1058
+ messages: [
1059
+ ...input.messages.slice(-5).map((msg) => ({
1060
+ role: msg.sender === 'user' ? 'user' : 'assistant',
1061
+ content: msg.content,
1062
+ })),
1063
+ {
1064
+ role: 'user',
1065
+ content: prompt,
1066
+ },
1067
+ ],
1068
+ }),
1069
+ });
1070
+
1071
+ const data = await response.json();
1072
+ return data.content[0]?.text || "Sorry, I couldn't generate a response for this step.";
1073
+ } catch (error) {
1074
+ console.error('🚨 Multi-message generation error:', error);
1075
+ throw error;
1076
+ }
1077
+ },
1078
+ ),
1079
+
1080
+ fetchMCPData: fromPromise(async ({ input }: { input: { query: string } }) => {
1081
+ try {
1082
+ console.log('🌐 Fetching MCP data for query:', input.query);
1083
+
1084
+ const response = await performDirectMCPSearch(input.query, true, true);
1085
+ console.log('✅ MCP data fetched:', response.summary);
1086
+
1087
+ return response;
1088
+ } catch (error) {
1089
+ console.error('🚨 MCP fetch error:', error);
1090
+ throw error;
1091
+ }
1092
+ }),
1093
+
1094
+ generateAIResponse: fromPromise(
1095
+ async ({
1096
+ input,
1097
+ }: {
1098
+ input: {
1099
+ messages: Message[];
1100
+ mcpData?: MCPData;
1101
+ regularMessages: any[];
1102
+ messageToRespondTo?: string;
1103
+ };
1104
+ }) => {
1105
+ console.log('🤖 Generating response using server-side AI proxy');
1106
+ console.log('🤖 Input received:', {
1107
+ messagesCount: input.messages.length,
1108
+ hasMCPData: !!input.mcpData,
1109
+ regularMessagesCount: input.regularMessages?.length || 0,
1110
+ messageToRespondTo: input.messageToRespondTo,
1111
+ });
1112
+
1113
+ try {
1114
+ // Check if we have an available AI provider
1115
+ if (!API_CONFIG.availableProvider) {
1116
+ throw new Error(
1117
+ 'No AI API key configured. Please set VITE_ANTHROPIC_API_KEY or VITE_OPENAI_API_KEY in your environment.',
1118
+ );
1119
+ }
1120
+
1121
+ console.log(`🤖 Using ${API_CONFIG.availableProvider} provider for AI response generation`);
1122
+
1123
+ // Get the latest user message or the message to respond to
1124
+ let prompt: string;
1125
+ let userMessages = input.messages.filter((msg) => msg.sender === 'user');
1126
+
1127
+ if (input.messageToRespondTo) {
1128
+ // Use the message to respond to for auto-responses
1129
+ prompt = input.messageToRespondTo;
1130
+ console.log('🤖 Auto-responding to message:', prompt.substring(0, 50) + '...');
1131
+ } else if (userMessages.length > 0) {
1132
+ const latestUserMessage = userMessages[userMessages.length - 1];
1133
+ prompt = latestUserMessage.content;
1134
+ } else {
1135
+ // If no user messages, this might be an auto-response
1136
+ prompt = 'Please provide a helpful response to the conversation context.';
1137
+ }
1138
+
1139
+ // Add conversation context if available
1140
+ if (input.regularMessages && input.regularMessages.length > 0) {
1141
+ const conversationContext = getConversationContext(input.regularMessages);
1142
+ prompt = `${conversationContext}User question: ${prompt}`;
1143
+ console.log(
1144
+ '🚀 Enhanced prompt with conversation context - Recent messages:',
1145
+ input.regularMessages.length,
1146
+ );
1147
+ }
1148
+
1149
+ // Add MCP context if available
1150
+ if (input.mcpData) {
1151
+ const mcpContext = formatMCPDataForAgent(input.mcpData);
1152
+ if (mcpContext) {
1153
+ prompt = `${mcpContext}\n\nUser question: ${prompt}`;
1154
+ console.log(
1155
+ '🚀 Enhanced prompt with MCP data - Web results:',
1156
+ input.mcpData.searchResults?.length || 0,
1157
+ 'News articles:',
1158
+ input.mcpData.newsArticles?.length || 0,
1159
+ );
1160
+ }
1161
+ }
1162
+
1163
+ // Use Vite proxy for AI API calls (avoids CORS issues)
1164
+ const controller = new AbortController();
1165
+ const timeoutId = setTimeout(() => controller.abort(), 15000); // Increased to 15s timeout for better reliability
1166
+
1167
+ try {
1168
+ const response = await makeApiCall(API_CONFIG.primaryEndpoint, {
1169
+ method: 'POST',
1170
+ headers: {
1171
+ 'Content-Type': 'application/json',
1172
+ },
1173
+ body: JSON.stringify({
1174
+ model: API_CONFIG.primaryModel,
1175
+ max_tokens: 300, // Further reduced for faster responses
1176
+ system: getSystemPrompt(API_CONFIG.availableProvider || 'anthropic', true),
1177
+ messages: [
1178
+ // Only include the last 2 messages for faster processing
1179
+ ...input.messages.slice(-2).map((msg) => ({
1180
+ role: msg.sender === 'user' ? 'user' : 'assistant',
1181
+ content: msg.content,
1182
+ })),
1183
+ {
1184
+ role: 'user',
1185
+ content: prompt,
1186
+ },
1187
+ ],
1188
+ }),
1189
+ signal: controller.signal,
1190
+ });
1191
+
1192
+ clearTimeout(timeoutId);
1193
+
1194
+ const data = await response.json();
1195
+ console.log('🤖 AI API response received:', data);
1196
+
1197
+ const aiResponseText =
1198
+ data.content[0]?.text ||
1199
+ data.choices?.[0]?.message?.content ||
1200
+ "Sorry, I couldn't generate a response.";
1201
+ console.log('🤖 Extracted AI response text:', aiResponseText.substring(0, 100) + '...');
1202
+
1203
+ console.log(
1204
+ '🤖 ✅ generateAIResponse actor completing successfully with text length:',
1205
+ aiResponseText.length,
1206
+ );
1207
+ return aiResponseText;
1208
+ } catch (error) {
1209
+ clearTimeout(timeoutId);
1210
+ if (error.name === 'AbortError') {
1211
+ throw new Error('AI response generation timed out. Please try again.');
1212
+ }
1213
+ throw error;
1214
+ }
1215
+ } catch (error) {
1216
+ console.error('🚨 AI generation error:', error);
1217
+
1218
+ if (error instanceof Error) {
1219
+ if (error.message.includes('API key')) {
1220
+ throw new Error(
1221
+ 'AI API key is not configured. Please set your API keys in the environment variables.',
1222
+ );
1223
+ }
1224
+ if (error.message.includes('rate limit')) {
1225
+ throw new Error('Rate limit exceeded. Please try again in a moment.');
1226
+ }
1227
+ if (error.message.includes('network') || error.message.includes('fetch')) {
1228
+ throw new Error(
1229
+ 'Network error. Please check your connection and ensure your Vite dev server is running on localhost:3011.',
1230
+ );
1231
+ }
1232
+ throw error; // Re-throw the Error instance
1233
+ }
1234
+
1235
+ // If error is not an Error instance, create a proper Error with string message
1236
+ const errorMessage =
1237
+ typeof error === 'string'
1238
+ ? error
1239
+ : error && typeof error === 'object' && 'message' in error
1240
+ ? String(error.message)
1241
+ : 'An unexpected error occurred while generating the response';
1242
+ throw new Error(errorMessage);
1243
+ }
1244
+ },
1245
+ ),
1246
+ },
1247
+ },
1248
+ );