@messenger-box/tailwind-ui-inbox 10.0.3-alpha.73 → 10.0.3-alpha.74

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 (145) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/components/AIAgent/AIAgent.d.ts +7 -0
  3. package/lib/components/AIAgent/AIAgent.d.ts.map +1 -1
  4. package/lib/components/AIAgent/AIAgent.js +362 -615
  5. package/lib/components/AIAgent/AIAgent.js.map +1 -1
  6. package/lib/components/InboxMessage/InputComponent.d.ts.map +1 -1
  7. package/lib/components/InboxMessage/InputComponent.js +143 -140
  8. package/lib/components/InboxMessage/InputComponent.js.map +1 -1
  9. package/lib/components/InboxMessage/RightSidebarAi.d.ts +23 -0
  10. package/lib/components/InboxMessage/RightSidebarAi.d.ts.map +1 -0
  11. package/lib/components/InboxMessage/RightSidebarAi.js +9 -0
  12. package/lib/components/InboxMessage/RightSidebarAi.js.map +1 -0
  13. package/lib/components/InboxMessage/index.d.ts +1 -0
  14. package/lib/components/InboxMessage/index.d.ts.map +1 -1
  15. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.d.ts +11 -0
  16. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.d.ts.map +1 -0
  17. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.js +194 -0
  18. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.js.map +1 -0
  19. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts +5 -1
  20. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts.map +1 -1
  21. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js +308 -857
  22. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js.map +1 -1
  23. package/lib/components/ModelConfigPanel.d.ts +12 -0
  24. package/lib/components/ModelConfigPanel.d.ts.map +1 -0
  25. package/lib/components/ModelConfigPanel.js +304 -0
  26. package/lib/components/ModelConfigPanel.js.map +1 -0
  27. package/lib/components/filler-components/RightSiderBar.d.ts +24 -0
  28. package/lib/components/filler-components/RightSiderBar.d.ts.map +1 -0
  29. package/lib/components/filler-components/RightSiderBar.js +335 -0
  30. package/lib/components/filler-components/RightSiderBar.js.map +1 -0
  31. package/lib/components/index.d.ts +4 -2
  32. package/lib/components/index.d.ts.map +1 -1
  33. package/lib/components/live-code-editor/hybrid-live-editor.d.ts +20 -0
  34. package/lib/components/live-code-editor/hybrid-live-editor.d.ts.map +1 -0
  35. package/lib/components/live-code-editor/hybrid-live-editor.js +68 -0
  36. package/lib/components/live-code-editor/hybrid-live-editor.js.map +1 -0
  37. package/lib/components/live-code-editor/index.d.ts +4 -0
  38. package/lib/components/live-code-editor/index.d.ts.map +1 -0
  39. package/lib/components/live-code-editor/live-code-editor.d.ts +14 -0
  40. package/lib/components/live-code-editor/live-code-editor.d.ts.map +1 -0
  41. package/lib/components/live-code-editor/live-code-editor.js +207 -0
  42. package/lib/components/live-code-editor/live-code-editor.js.map +1 -0
  43. package/lib/components/slot-fill/chat-message-filler.js +1 -1
  44. package/lib/components/slot-fill/chat-message-filler.js.map +1 -1
  45. package/lib/components/slot-fill/index.d.ts +1 -0
  46. package/lib/components/slot-fill/index.d.ts.map +1 -1
  47. package/lib/components/slot-fill/right-sidebar-filler.d.ts +4 -0
  48. package/lib/components/slot-fill/right-sidebar-filler.d.ts.map +1 -0
  49. package/lib/components/slot-fill/right-sidebar-filler.js +13 -0
  50. package/lib/components/slot-fill/right-sidebar-filler.js.map +1 -0
  51. package/lib/components/ui/button.d.ts +9 -0
  52. package/lib/components/ui/button.d.ts.map +1 -0
  53. package/lib/compute.js +1 -2
  54. package/lib/container/AiInbox.d.ts.map +1 -1
  55. package/lib/container/AiLandingInput.d.ts.map +1 -1
  56. package/lib/container/AiLandingInput.js +46 -119
  57. package/lib/container/AiLandingInput.js.map +1 -1
  58. package/lib/container/Inbox.js +1 -1
  59. package/lib/container/Inbox.js.map +1 -1
  60. package/lib/container/InboxAiMessagesLoader.d.ts +0 -21
  61. package/lib/container/InboxAiMessagesLoader.d.ts.map +1 -1
  62. package/lib/container/InboxAiMessagesLoader.js +18 -32
  63. package/lib/container/InboxAiMessagesLoader.js.map +1 -1
  64. package/lib/container/ServiceInbox.js +1 -1
  65. package/lib/container/ServiceInbox.js.map +1 -1
  66. package/lib/container/ThreadMessages.js +1 -1
  67. package/lib/container/ThreadMessages.js.map +1 -1
  68. package/lib/container/ThreadMessagesInbox.js +1 -1
  69. package/lib/container/ThreadMessagesInbox.js.map +1 -1
  70. package/lib/container/Threads.js +1 -1
  71. package/lib/container/Threads.js.map +1 -1
  72. package/lib/container/index.d.ts +2 -1
  73. package/lib/container/index.d.ts.map +1 -1
  74. package/lib/enums/messenger-slot-fill-name-enum.d.ts +2 -1
  75. package/lib/enums/messenger-slot-fill-name-enum.d.ts.map +1 -1
  76. package/lib/enums/messenger-slot-fill-name-enum.js +1 -0
  77. package/lib/enums/messenger-slot-fill-name-enum.js.map +1 -1
  78. package/lib/hooks/index.d.ts +3 -0
  79. package/lib/hooks/index.d.ts.map +1 -0
  80. package/lib/hooks/use-file-sync.d.ts +16 -0
  81. package/lib/hooks/use-file-sync.d.ts.map +1 -0
  82. package/lib/hooks/use-file-sync.js +63 -0
  83. package/lib/hooks/use-file-sync.js.map +1 -0
  84. package/lib/hooks/usePersistentModelConfig.d.ts +15 -0
  85. package/lib/hooks/usePersistentModelConfig.d.ts.map +1 -0
  86. package/lib/hooks/usePersistentModelConfig.js +46 -0
  87. package/lib/hooks/usePersistentModelConfig.js.map +1 -0
  88. package/lib/index.d.ts +4 -2
  89. package/lib/index.d.ts.map +1 -1
  90. package/lib/index.js +1 -1
  91. package/lib/machines/aiAgentMachine.d.ts.map +1 -1
  92. package/lib/machines/aiAgentMachine.js +64 -21
  93. package/lib/machines/aiAgentMachine.js.map +1 -1
  94. package/lib/machines/aiAgentMachine.simple.d.ts +3 -0
  95. package/lib/machines/aiAgentMachine.simple.d.ts.map +1 -0
  96. package/lib/machines/aiAgentMachine.simple.js +108 -0
  97. package/lib/machines/aiAgentMachine.simple.js.map +1 -0
  98. package/lib/machines/index.d.ts +3 -0
  99. package/lib/machines/index.d.ts.map +1 -0
  100. package/lib/module.d.ts +2 -1
  101. package/lib/module.d.ts.map +1 -1
  102. package/lib/module.js +11 -3
  103. package/lib/module.js.map +1 -1
  104. package/lib/routes.json +1 -2
  105. package/lib/templates/InboxWithAi.d.ts.map +1 -1
  106. package/lib/templates/InboxWithAi.js +129 -70
  107. package/lib/templates/InboxWithAi.js.map +1 -1
  108. package/lib/templates/InboxWithAi.tsx +151 -90
  109. package/lib/utils/utils.d.ts +2 -0
  110. package/lib/utils/utils.d.ts.map +1 -0
  111. package/lib/utils/utils.js +3 -0
  112. package/lib/utils/utils.js.map +1 -0
  113. package/package.json +8 -5
  114. package/src/components/AIAgent/AIAgent.tsx +469 -731
  115. package/src/components/AIAgent/AIAgent.tsx.bk +1365 -0
  116. package/src/components/InboxMessage/InputComponent.tsx +2 -1
  117. package/src/components/InboxMessage/RightSidebarAi.tsx +37 -0
  118. package/src/components/InboxMessage/index.ts +1 -0
  119. package/src/components/InboxMessage/message-widgets/ErrorFixCard.tsx +240 -0
  120. package/src/components/InboxMessage/message-widgets/ModernMessageGroup.tsx +337 -1116
  121. package/src/components/ModelConfigPanel.tsx +334 -0
  122. package/src/components/filler-components/RightSiderBar.tsx +408 -0
  123. package/src/components/index.ts +4 -1
  124. package/src/components/live-code-editor/hybrid-live-editor.tsx +105 -0
  125. package/src/components/live-code-editor/index.ts +3 -0
  126. package/src/components/live-code-editor/live-code-editor.tsx +257 -0
  127. package/src/components/slot-fill/index.ts +1 -0
  128. package/src/components/slot-fill/right-sidebar-filler.tsx +39 -0
  129. package/src/components/ui/button.tsx +32 -0
  130. package/src/container/AiInbox.tsx +26 -3
  131. package/src/container/AiLandingInput.tsx +48 -22
  132. package/src/container/InboxAiMessagesLoader.tsx +17 -31
  133. package/src/container/index.ts +2 -0
  134. package/src/enums/messenger-slot-fill-name-enum.ts +1 -0
  135. package/src/hooks/index.ts +2 -0
  136. package/src/hooks/use-file-sync.ts +91 -0
  137. package/src/hooks/usePersistentModelConfig.ts +63 -0
  138. package/src/index.ts +7 -0
  139. package/src/machines/aiAgentMachine.simple.ts +89 -0
  140. package/src/machines/aiAgentMachine.ts +67 -19
  141. package/src/machines/aiAgentMachine.ts.bk +1296 -0
  142. package/src/machines/index.ts +2 -0
  143. package/src/module.tsx +10 -1
  144. package/src/templates/InboxWithAi.tsx +151 -90
  145. package/src/utils/utils.ts +3 -0
@@ -0,0 +1,91 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ import { useUpdateSandboxFileMutation, useFileUpdatedSubscription } from 'common/graphql';
3
+
4
+ export interface FileSyncHookProps {
5
+ projectId: string;
6
+ messageId?: string;
7
+ }
8
+
9
+ export interface FileSyncResult {
10
+ updateFile: (filePath: string, content: string) => Promise<void>;
11
+ isUpdating: boolean;
12
+ error: string | null;
13
+ lastUpdate: {
14
+ filePath: string;
15
+ operation: string;
16
+ timestamp: string;
17
+ } | null;
18
+ }
19
+
20
+ export const useFileSync = ({ projectId, messageId }: FileSyncHookProps): FileSyncResult => {
21
+ const [isUpdating, setIsUpdating] = useState(false);
22
+ const [error, setError] = useState<string | null>(null);
23
+ const [lastUpdate, setLastUpdate] = useState<{
24
+ filePath: string;
25
+ operation: string;
26
+ timestamp: string;
27
+ } | null>(null);
28
+
29
+ // GraphQL mutations
30
+ const [updateSandboxFileMutation] = useUpdateSandboxFileMutation();
31
+
32
+ // Real-time file updates subscription
33
+ const { data: fileUpdateData } = useFileUpdatedSubscription({
34
+ variables: { projectId },
35
+ skip: !projectId,
36
+ });
37
+
38
+ // Handle real-time file updates
39
+ useEffect(() => {
40
+ if (fileUpdateData?.fileUpdated) {
41
+ const update = fileUpdateData.fileUpdated;
42
+ setLastUpdate({
43
+ filePath: update.filePath,
44
+ operation: update.operation,
45
+ timestamp: update.timestamp,
46
+ });
47
+ }
48
+ }, [fileUpdateData]);
49
+
50
+ const updateFile = useCallback(
51
+ async (filePath: string, content: string) => {
52
+ if (!messageId) {
53
+ throw new Error('Fragment ID is required for file operations');
54
+ }
55
+
56
+ console.log('useFileSync.updateFile - Using fragmentId:', messageId);
57
+
58
+ try {
59
+ setIsUpdating(true);
60
+ setError(null);
61
+
62
+ const response = await updateSandboxFileMutation({
63
+ variables: {
64
+ projectId,
65
+ messageId,
66
+ filePath,
67
+ content,
68
+ },
69
+ });
70
+
71
+ if (!response.data?.updateSandboxFile.success) {
72
+ throw new Error(response.data?.updateSandboxFile.message || 'Failed to update file');
73
+ }
74
+ } catch (err) {
75
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
76
+ setError(errorMessage);
77
+ throw err;
78
+ } finally {
79
+ setIsUpdating(false);
80
+ }
81
+ },
82
+ [updateSandboxFileMutation, projectId, messageId],
83
+ );
84
+
85
+ return {
86
+ updateFile,
87
+ isUpdating,
88
+ error,
89
+ lastUpdate,
90
+ };
91
+ };
@@ -0,0 +1,63 @@
1
+ import { useCallback, useEffect, useMemo, useState } from 'react';
2
+
3
+ export type ProviderId = 'openai' | 'anthropic' | 'gemini';
4
+
5
+ export interface ModelConfig {
6
+ provider: ProviderId;
7
+ model: string;
8
+ apiKey: string;
9
+ template: 'react-vite' | 'nextjs' | 'vue' | 'vite-react';
10
+ }
11
+
12
+ const STORAGE_KEY = 'mbx:model-config';
13
+
14
+ function loadFromStorage(): ModelConfig | null {
15
+ try {
16
+ const raw = typeof window !== 'undefined' ? window.localStorage.getItem(STORAGE_KEY) : null;
17
+ if (!raw) return null;
18
+ const parsed = JSON.parse(raw);
19
+ return parsed as ModelConfig;
20
+ } catch {
21
+ return null;
22
+ }
23
+ }
24
+
25
+ function saveToStorage(config: ModelConfig) {
26
+ try {
27
+ if (typeof window !== 'undefined') {
28
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(config));
29
+ }
30
+ } catch {
31
+ // ignore
32
+ }
33
+ }
34
+
35
+ const DEFAULTS: ModelConfig = {
36
+ provider: 'anthropic',
37
+ model: 'claude-3-5-sonnet-20241022',
38
+ apiKey: '',
39
+ template: 'react-vite',
40
+ };
41
+
42
+ export function usePersistentModelConfig() {
43
+ const [modelConfig, setModelConfig] = useState<ModelConfig>(() => loadFromStorage() || DEFAULTS);
44
+
45
+ useEffect(() => {
46
+ saveToStorage(modelConfig);
47
+ }, [modelConfig]);
48
+
49
+ const updateModelConfig = useCallback((newConfig: ModelConfig) => {
50
+ setModelConfig(newConfig);
51
+ }, []);
52
+
53
+ const hasApiKey = useMemo(() => Boolean(modelConfig.apiKey && modelConfig.apiKey.trim().length > 0), [modelConfig]);
54
+
55
+ const getValidatedConfig = useCallback(() => {
56
+ if (!hasApiKey) return null;
57
+ return modelConfig;
58
+ }, [hasApiKey, modelConfig]);
59
+
60
+ return { modelConfig, updateModelConfig, getValidatedConfig, hasApiKey };
61
+ }
62
+
63
+ export default usePersistentModelConfig;
package/src/index.ts CHANGED
@@ -9,6 +9,7 @@ export {
9
9
  InboxWithAiLoader,
10
10
  InboxAiMessagesLoader,
11
11
  InboxContainer,
12
+ AiLandingInput,
12
13
  } from './container';
13
14
  export {
14
15
  MessageSliceRenderer,
@@ -24,7 +25,13 @@ export {
24
25
  UserModalContent,
25
26
  ServiceInboxItem,
26
27
  AIAgent,
28
+ ModelConfigPanel,
29
+ RightSidebarAi,
30
+ InputComponent,
31
+ RightSidebarFillDefault,
27
32
  } from './components';
28
33
  export { InboxWithAi } from './templates';
29
34
  export type { CardMessageAttachmentsInterface, AlertMessageAttachmentsInterface } from './interfaces';
30
35
  export * from './enums';
36
+ export * from './machines';
37
+ export * from './hooks';
@@ -0,0 +1,89 @@
1
+ import { createMachine, assign } from 'xstate';
2
+ import { AIAgentContext, AIAgentEvent, Message } from './types';
3
+
4
+ export const aiAgentMachine = createMachine(
5
+ {
6
+ id: 'aiAgentSimple',
7
+ types: {} as {
8
+ context: AIAgentContext;
9
+ events: AIAgentEvent;
10
+ },
11
+ initial: 'idle',
12
+ context: {
13
+ messages: [],
14
+ currentInput: '',
15
+ error: null,
16
+ isTyping: false,
17
+ regularMessages: [],
18
+ },
19
+ states: {
20
+ idle: {
21
+ on: {
22
+ SEND_MESSAGE: {
23
+ target: 'idle',
24
+ guard: 'hasValidMessage',
25
+ actions: 'addUserMessage',
26
+ },
27
+ INPUT_CHANGE: {
28
+ actions: 'updateInput',
29
+ },
30
+ CLEAR_ERROR: {
31
+ actions: 'clearError',
32
+ },
33
+ UPDATE_REGULAR_MESSAGES: {
34
+ actions: 'updateRegularMessages',
35
+ },
36
+ // Tolerate legacy events as no-ops
37
+ AUTO_RESPOND_TO_MESSAGE: {},
38
+ RETRY: {},
39
+ CONTINUE_PROCESSING: {},
40
+ UPDATE: {
41
+ actions: 'mergeUpdate',
42
+ },
43
+ },
44
+ },
45
+ },
46
+ },
47
+ {
48
+ guards: {
49
+ hasValidMessage: ({ event }) => {
50
+ if (event.type === 'SEND_MESSAGE') {
51
+ return Boolean(event.message && event.message.trim().length > 0);
52
+ }
53
+ return false;
54
+ },
55
+ },
56
+ actions: {
57
+ addUserMessage: assign(({ context, event }) => {
58
+ if (event.type !== 'SEND_MESSAGE') return context;
59
+ const newMessage: Message = {
60
+ id: Date.now().toString(),
61
+ content: event.message,
62
+ sender: 'user',
63
+ timestamp: new Date(),
64
+ };
65
+ return {
66
+ ...context,
67
+ messages: [...context.messages, newMessage],
68
+ currentInput: '',
69
+ error: null,
70
+ };
71
+ }),
72
+ updateInput: assign(({ context, event }) => {
73
+ if (event.type !== 'INPUT_CHANGE') return context;
74
+ return { ...context, currentInput: event.value };
75
+ }),
76
+ updateRegularMessages: assign(({ context, event }) => {
77
+ if (event.type !== 'UPDATE_REGULAR_MESSAGES') return context;
78
+ return { ...context, regularMessages: event.messages || [] };
79
+ }),
80
+ clearError: assign(({ context }) => ({ ...context, error: null })),
81
+ mergeUpdate: assign(({ context, event }) => {
82
+ // Supports events like { type: 'UPDATE', value: { messages: [] } }
83
+ if (event.type !== 'UPDATE') return context as any;
84
+ const value = (event as any).value || {};
85
+ return { ...context, ...value } as any;
86
+ }),
87
+ },
88
+ },
89
+ );
@@ -1,19 +1,33 @@
1
1
  import { createMachine, assign, fromPromise } from 'xstate';
2
2
  import { AIAgentContext, AIAgentEvent, Message, MCPData } from './types';
3
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;
4
+ const { CLIENT_URL, NEWS_API_KEY } = config;
6
5
  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
6
  NEWS_API_KEY: NEWS_API_KEY,
10
7
  };
11
8
 
12
9
  // API configuration - using environment variables from dev.env
10
+ // Read persisted model configuration saved by usePersistentModelConfig
11
+ function getUserModelConfig(): { provider?: 'openai' | 'anthropic' | 'gemini'; model?: string; apiKey?: string } {
12
+ try {
13
+ if (typeof window === 'undefined') return {};
14
+ const raw = window.localStorage.getItem('mbx:model-config');
15
+ if (!raw) return {};
16
+ const parsed = JSON.parse(raw);
17
+ return {
18
+ provider: parsed?.provider,
19
+ model: parsed?.model,
20
+ apiKey: parsed?.apiKey,
21
+ };
22
+ } catch {
23
+ return {};
24
+ }
25
+ }
26
+
27
+ const USER_MODEL_CONFIG = getUserModelConfig();
28
+
13
29
  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,
30
+ // Only non-AI keys from env (public APIs allowed)
17
31
  NEWS_API_KEY: env?.NEWS_API_KEY,
18
32
 
19
33
  // Configurable proxy endpoints
@@ -30,39 +44,56 @@ const API_CONFIG = {
30
44
  DEFAULT_MODEL: 'claude-3-5-haiku-20241022',
31
45
  OPENAI_MODEL: 'gpt-4o-mini',
32
46
 
33
- // Check which AI provider is available
47
+ // User-provided model configuration (from localStorage)
48
+ get userProvider() {
49
+ return USER_MODEL_CONFIG?.provider;
50
+ },
51
+ get userModel() {
52
+ return USER_MODEL_CONFIG?.model;
53
+ },
54
+ get userApiKey() {
55
+ return USER_MODEL_CONFIG?.apiKey;
56
+ },
57
+
58
+ // Check which AI provider is available (ONLY from user config)
34
59
  get availableProvider() {
35
- if (this.ANTHROPIC_API_KEY) return 'anthropic';
36
- if (this.OPENAI_API_KEY) return 'openai';
60
+ if (this.userApiKey && this.userProvider) {
61
+ if (this.userProvider === 'anthropic') return 'anthropic';
62
+ if (this.userProvider === 'openai') return 'openai';
63
+ }
37
64
  return null;
38
65
  },
39
66
 
40
67
  // Get the appropriate endpoint based on available provider
41
68
  get primaryEndpoint() {
42
- return this.availableProvider === 'anthropic' ? this.ANTHROPIC_ENDPOINT : this.OPENAI_ENDPOINT;
69
+ if (this.availableProvider === 'anthropic') return this.ANTHROPIC_ENDPOINT;
70
+ if (this.availableProvider === 'openai') return this.OPENAI_ENDPOINT;
71
+ return '';
43
72
  },
44
73
 
45
74
  // Get the appropriate model based on available provider
46
75
  get primaryModel() {
47
- return this.availableProvider === 'anthropic' ? this.DEFAULT_MODEL : this.OPENAI_MODEL;
76
+ if (this.userModel) return this.userModel;
77
+ if (this.availableProvider === 'anthropic') return this.DEFAULT_MODEL;
78
+ if (this.availableProvider === 'openai') return this.OPENAI_MODEL;
79
+ return this.DEFAULT_MODEL;
48
80
  },
49
81
  };
50
82
 
51
83
  // Log configuration on startup
52
84
  console.log('🤖 AI Agent Machine Configuration:');
53
- console.log(` Available Provider: ${API_CONFIG.availableProvider || 'None'}`);
85
+ console.log(` Available Provider (user): ${API_CONFIG.availableProvider || 'None'}`);
54
86
  console.log(` Primary Endpoint: ${API_CONFIG.primaryEndpoint}`);
55
87
  console.log(` Primary Model: ${API_CONFIG.primaryModel}`);
56
88
  console.log(` Using Proxy for AI APIs: ✅ (avoids CORS issues)`);
57
89
  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'}`);
90
+ console.log(` User API Key: ${API_CONFIG.userApiKey ? '✅ Configured' : '❌ Not configured'}`);
60
91
  console.log(` News API Key: ${API_CONFIG.NEWS_API_KEY ? '✅ Configured' : '❌ Not configured'}`);
61
92
 
62
93
  // Test API configuration
63
94
  if (!API_CONFIG.availableProvider) {
64
95
  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.');
96
+ console.error('🚨 Please add your API key in Model Settings.');
66
97
  } else {
67
98
  console.log(`✅ AI API provider configured: ${API_CONFIG.availableProvider}`);
68
99
  }
@@ -80,7 +111,7 @@ console.log(`🌐 Proxy Configuration: ${getProxyBaseUrl() || 'Relative paths (p
80
111
  async function makeApiCall(endpoint: string, options: RequestInit) {
81
112
  try {
82
113
  const baseUrl = getProxyBaseUrl();
83
- const url = `${baseUrl}${endpoint}`;
114
+ const url = endpoint ? `${baseUrl}${endpoint}` : '';
84
115
 
85
116
  console.log(`🌐 Making API call through proxy to: ${url}`);
86
117
  console.log(`📤 Request options:`, {
@@ -89,7 +120,24 @@ async function makeApiCall(endpoint: string, options: RequestInit) {
89
120
  body: options.body ? JSON.parse(options.body as string) : undefined,
90
121
  });
91
122
 
92
- const response = await fetch(url, options);
123
+ // Do not call if no endpoint (no provider configured)
124
+ if (!endpoint) {
125
+ throw new Error('AI provider is not configured. Please add your API key in Model Settings.');
126
+ }
127
+
128
+ // Inject API key headers from user config if available
129
+ const headers = new Headers(options.headers || {});
130
+ if (API_CONFIG.userApiKey) {
131
+ if (API_CONFIG.availableProvider === 'anthropic') {
132
+ headers.set('x-api-key', API_CONFIG.userApiKey);
133
+ headers.set('anthropic-version', '2023-06-01');
134
+ headers.set('anthropic-dangerous-direct-browser-access', 'true');
135
+ } else if (API_CONFIG.availableProvider === 'openai') {
136
+ headers.set('Authorization', `Bearer ${API_CONFIG.userApiKey}`);
137
+ }
138
+ }
139
+
140
+ const response = await fetch(url, { ...options, headers });
93
141
 
94
142
  if (!response.ok) {
95
143
  const errorData = await response.json().catch(() => ({}));
@@ -846,7 +894,7 @@ export const aiAgentMachine = createMachine(
846
894
  }
847
895
 
848
896
  if (errorMessage.includes('API key')) {
849
- errorMessage = 'API key not configured. Please set your Anthropic API key.';
897
+ errorMessage = 'API key not configured. Please add your API key in Model Settings.';
850
898
  } else if (errorMessage.includes('rate limit')) {
851
899
  errorMessage = 'Rate limit exceeded. Please try again in a moment.';
852
900
  } else if (errorMessage.includes('network') || errorMessage.includes('fetch')) {