hedgequantx 2.5.12 → 2.5.14
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.
- package/package.json +1 -1
- package/src/app.js +9 -3
- package/src/menus/ai-agent.js +297 -50
- package/src/menus/dashboard.js +9 -13
- package/src/pages/algo/index.js +230 -35
- package/src/services/ai/index.js +304 -61
- package/src/services/ai/supervisor.js +713 -0
- package/src/services/ai/token-scanner.js +27 -4
package/src/services/ai/index.js
CHANGED
|
@@ -1,22 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AI Service Manager
|
|
3
|
-
* Manages AI provider connections
|
|
3
|
+
* Manages multiple AI provider connections
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const { getProviders, getProvider } = require('./providers');
|
|
7
|
-
const {
|
|
7
|
+
const { storage } = require('../session');
|
|
8
|
+
const AISupervisor = require('./supervisor');
|
|
8
9
|
|
|
9
|
-
// In-memory cache of
|
|
10
|
-
let
|
|
10
|
+
// In-memory cache of connections
|
|
11
|
+
let connectionsCache = null;
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Get AI settings from storage
|
|
14
15
|
*/
|
|
15
16
|
const getAISettings = () => {
|
|
16
17
|
try {
|
|
17
|
-
|
|
18
|
+
const sessions = storage.load();
|
|
19
|
+
const aiSettings = sessions.find(s => s.type === 'ai') || { type: 'ai', agents: [] };
|
|
20
|
+
|
|
21
|
+
// Migrate old single-agent format to multi-agent
|
|
22
|
+
if (aiSettings.provider && !aiSettings.agents) {
|
|
23
|
+
return {
|
|
24
|
+
type: 'ai',
|
|
25
|
+
agents: [{
|
|
26
|
+
id: generateAgentId(),
|
|
27
|
+
provider: aiSettings.provider,
|
|
28
|
+
option: aiSettings.option,
|
|
29
|
+
credentials: aiSettings.credentials,
|
|
30
|
+
model: aiSettings.model,
|
|
31
|
+
name: getProvider(aiSettings.provider)?.name || 'AI Agent',
|
|
32
|
+
createdAt: Date.now()
|
|
33
|
+
}],
|
|
34
|
+
activeAgentId: null
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return aiSettings;
|
|
18
38
|
} catch {
|
|
19
|
-
return {};
|
|
39
|
+
return { type: 'ai', agents: [] };
|
|
20
40
|
}
|
|
21
41
|
};
|
|
22
42
|
|
|
@@ -25,42 +45,139 @@ const getAISettings = () => {
|
|
|
25
45
|
*/
|
|
26
46
|
const saveAISettings = (aiSettings) => {
|
|
27
47
|
try {
|
|
28
|
-
|
|
48
|
+
const sessions = storage.load();
|
|
49
|
+
const otherSessions = sessions.filter(s => s.type !== 'ai');
|
|
50
|
+
|
|
51
|
+
aiSettings.type = 'ai';
|
|
52
|
+
otherSessions.push(aiSettings);
|
|
53
|
+
|
|
54
|
+
storage.save(otherSessions);
|
|
55
|
+
connectionsCache = null; // Invalidate cache
|
|
29
56
|
} catch (e) {
|
|
30
57
|
// Silent fail
|
|
31
58
|
}
|
|
32
59
|
};
|
|
33
60
|
|
|
34
61
|
/**
|
|
35
|
-
*
|
|
62
|
+
* Generate unique agent ID
|
|
63
|
+
*/
|
|
64
|
+
const generateAgentId = () => {
|
|
65
|
+
return 'agent_' + Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get all connected agents
|
|
70
|
+
*/
|
|
71
|
+
const getAgents = () => {
|
|
72
|
+
const aiSettings = getAISettings();
|
|
73
|
+
const agents = aiSettings.agents || [];
|
|
74
|
+
|
|
75
|
+
return agents.map(agent => {
|
|
76
|
+
const provider = getProvider(agent.provider);
|
|
77
|
+
return {
|
|
78
|
+
id: agent.id,
|
|
79
|
+
name: agent.name || provider?.name || 'Unknown',
|
|
80
|
+
provider: provider,
|
|
81
|
+
providerId: agent.provider,
|
|
82
|
+
option: agent.option,
|
|
83
|
+
model: agent.model || provider?.defaultModel,
|
|
84
|
+
createdAt: agent.createdAt,
|
|
85
|
+
isActive: agent.id === aiSettings.activeAgentId
|
|
86
|
+
};
|
|
87
|
+
}).filter(a => a.provider); // Filter out invalid providers
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get agent count
|
|
92
|
+
*/
|
|
93
|
+
const getAgentCount = () => {
|
|
94
|
+
const aiSettings = getAISettings();
|
|
95
|
+
return (aiSettings.agents || []).length;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check if any AI is connected
|
|
36
100
|
*/
|
|
37
101
|
const isConnected = () => {
|
|
102
|
+
return getAgentCount() > 0;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get active agent (or first agent if none active)
|
|
107
|
+
*/
|
|
108
|
+
const getActiveAgent = () => {
|
|
38
109
|
const aiSettings = getAISettings();
|
|
39
|
-
|
|
110
|
+
const agents = aiSettings.agents || [];
|
|
111
|
+
|
|
112
|
+
if (agents.length === 0) return null;
|
|
113
|
+
|
|
114
|
+
// Find active agent or use first one
|
|
115
|
+
const activeId = aiSettings.activeAgentId;
|
|
116
|
+
const agent = activeId
|
|
117
|
+
? agents.find(a => a.id === activeId)
|
|
118
|
+
: agents[0];
|
|
119
|
+
|
|
120
|
+
if (!agent) return null;
|
|
121
|
+
|
|
122
|
+
const provider = getProvider(agent.provider);
|
|
123
|
+
if (!provider) return null;
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
id: agent.id,
|
|
127
|
+
name: agent.name || provider.name,
|
|
128
|
+
provider: provider,
|
|
129
|
+
providerId: agent.provider,
|
|
130
|
+
option: agent.option,
|
|
131
|
+
model: agent.model || provider.defaultModel,
|
|
132
|
+
credentials: agent.credentials,
|
|
133
|
+
connected: true
|
|
134
|
+
};
|
|
40
135
|
};
|
|
41
136
|
|
|
42
137
|
/**
|
|
43
|
-
* Get
|
|
138
|
+
* Get agent by ID
|
|
44
139
|
*/
|
|
45
|
-
const
|
|
140
|
+
const getAgent = (agentId) => {
|
|
46
141
|
const aiSettings = getAISettings();
|
|
47
|
-
|
|
142
|
+
const agents = aiSettings.agents || [];
|
|
143
|
+
const agent = agents.find(a => a.id === agentId);
|
|
48
144
|
|
|
49
|
-
|
|
145
|
+
if (!agent) return null;
|
|
146
|
+
|
|
147
|
+
const provider = getProvider(agent.provider);
|
|
50
148
|
if (!provider) return null;
|
|
51
149
|
|
|
52
150
|
return {
|
|
151
|
+
id: agent.id,
|
|
152
|
+
name: agent.name || provider.name,
|
|
53
153
|
provider: provider,
|
|
54
|
-
|
|
55
|
-
|
|
154
|
+
providerId: agent.provider,
|
|
155
|
+
option: agent.option,
|
|
156
|
+
model: agent.model || provider.defaultModel,
|
|
157
|
+
credentials: agent.credentials,
|
|
56
158
|
connected: true
|
|
57
159
|
};
|
|
58
160
|
};
|
|
59
161
|
|
|
60
162
|
/**
|
|
61
|
-
*
|
|
163
|
+
* Set active agent
|
|
62
164
|
*/
|
|
63
|
-
const
|
|
165
|
+
const setActiveAgent = (agentId) => {
|
|
166
|
+
const aiSettings = getAISettings();
|
|
167
|
+
const agents = aiSettings.agents || [];
|
|
168
|
+
|
|
169
|
+
if (!agents.find(a => a.id === agentId)) {
|
|
170
|
+
throw new Error('Agent not found');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
aiSettings.activeAgentId = agentId;
|
|
174
|
+
saveAISettings(aiSettings);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Add a new agent (connect to provider)
|
|
179
|
+
*/
|
|
180
|
+
const addAgent = async (providerId, optionId, credentials, model = null, customName = null) => {
|
|
64
181
|
const provider = getProvider(providerId);
|
|
65
182
|
if (!provider) {
|
|
66
183
|
throw new Error('Invalid provider');
|
|
@@ -71,34 +188,153 @@ const connect = async (providerId, optionId, credentials, model = null) => {
|
|
|
71
188
|
throw new Error('Invalid option');
|
|
72
189
|
}
|
|
73
190
|
|
|
74
|
-
|
|
75
|
-
|
|
191
|
+
const aiSettings = getAISettings();
|
|
192
|
+
if (!aiSettings.agents) {
|
|
193
|
+
aiSettings.agents = [];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Create new agent
|
|
197
|
+
const agentId = generateAgentId();
|
|
198
|
+
const newAgent = {
|
|
199
|
+
id: agentId,
|
|
76
200
|
provider: providerId,
|
|
77
201
|
option: optionId,
|
|
78
202
|
credentials: credentials,
|
|
79
|
-
model: model || provider.defaultModel
|
|
203
|
+
model: model || provider.defaultModel,
|
|
204
|
+
name: customName || provider.name,
|
|
205
|
+
createdAt: Date.now()
|
|
80
206
|
};
|
|
81
207
|
|
|
208
|
+
aiSettings.agents.push(newAgent);
|
|
209
|
+
|
|
210
|
+
// Set as active if first agent
|
|
211
|
+
if (aiSettings.agents.length === 1) {
|
|
212
|
+
aiSettings.activeAgentId = agentId;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
saveAISettings(aiSettings);
|
|
216
|
+
|
|
217
|
+
// Start AI supervision automatically
|
|
218
|
+
const agent = getAgent(agentId);
|
|
219
|
+
if (agent) {
|
|
220
|
+
// Mock algo target - in real implementation, this would be HQX Ultra Scalping
|
|
221
|
+
const mockAlgo = {
|
|
222
|
+
name: 'HQX Ultra Scalping',
|
|
223
|
+
status: 'ready',
|
|
224
|
+
active: false
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// Check if other agents are already supervising
|
|
228
|
+
const allAgents = getAgents();
|
|
229
|
+
const activeSupervisionCount = allAgents.filter(a => a.id !== agentId && a.isActive).length;
|
|
230
|
+
|
|
231
|
+
if (activeSupervisionCount === 0) {
|
|
232
|
+
// First agent - start single supervision
|
|
233
|
+
AISupervisor.start(agentId, mockAlgo);
|
|
234
|
+
console.log(`\n🤖 AI supervision started for ${agent.name}`);
|
|
235
|
+
console.log(` Monitoring: HQX Ultra Scalping`);
|
|
236
|
+
console.log(` Mode: Single agent supervision`);
|
|
237
|
+
} else {
|
|
238
|
+
// Additional agent - switch to consensus mode
|
|
239
|
+
console.log(`\n🤖 ${agent.name} added to supervision`);
|
|
240
|
+
console.log(` Switching to multi-agent consensus mode`);
|
|
241
|
+
console.log(` Total agents: ${allAgents.length}`);
|
|
242
|
+
|
|
243
|
+
// Start consensus mode would be handled by supervisor
|
|
244
|
+
AISupervisor.start(agentId, mockAlgo);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return agent;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Remove an agent
|
|
253
|
+
*/
|
|
254
|
+
const removeAgent = (agentId) => {
|
|
255
|
+
const aiSettings = getAISettings();
|
|
256
|
+
const agents = aiSettings.agents || [];
|
|
257
|
+
|
|
258
|
+
const index = agents.findIndex(a => a.id === agentId);
|
|
259
|
+
if (index === -1) {
|
|
260
|
+
throw new Error('Agent not found');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
agents.splice(index, 1);
|
|
264
|
+
aiSettings.agents = agents;
|
|
265
|
+
|
|
266
|
+
// Stop AI supervision for this agent
|
|
267
|
+
AISupervisor.stop(agentId);
|
|
268
|
+
|
|
269
|
+
// If removed agent was active, set new active
|
|
270
|
+
if (aiSettings.activeAgentId === agentId) {
|
|
271
|
+
aiSettings.activeAgentId = agents.length > 0 ? agents[0].id : null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
saveAISettings(aiSettings);
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Update agent settings
|
|
279
|
+
*/
|
|
280
|
+
const updateAgent = (agentId, updates) => {
|
|
281
|
+
const aiSettings = getAISettings();
|
|
282
|
+
const agents = aiSettings.agents || [];
|
|
283
|
+
|
|
284
|
+
const agent = agents.find(a => a.id === agentId);
|
|
285
|
+
if (!agent) {
|
|
286
|
+
throw new Error('Agent not found');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Apply updates
|
|
290
|
+
if (updates.name) agent.name = updates.name;
|
|
291
|
+
if (updates.model) agent.model = updates.model;
|
|
292
|
+
if (updates.credentials) agent.credentials = updates.credentials;
|
|
293
|
+
|
|
82
294
|
saveAISettings(aiSettings);
|
|
83
|
-
|
|
295
|
+
return getAgent(agentId);
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Disconnect all agents
|
|
300
|
+
*/
|
|
301
|
+
const disconnectAll = () => {
|
|
302
|
+
// Stop all AI supervision sessions
|
|
303
|
+
AISupervisor.stopAll();
|
|
84
304
|
|
|
85
|
-
|
|
305
|
+
saveAISettings({ agents: [] });
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Legacy: Get current connection (returns active agent)
|
|
310
|
+
* @deprecated Use getActiveAgent() instead
|
|
311
|
+
*/
|
|
312
|
+
const getConnection = () => {
|
|
313
|
+
return getActiveAgent();
|
|
86
314
|
};
|
|
87
315
|
|
|
88
316
|
/**
|
|
89
|
-
*
|
|
317
|
+
* Legacy: Connect to a provider (adds new agent)
|
|
318
|
+
* @deprecated Use addAgent() instead
|
|
319
|
+
*/
|
|
320
|
+
const connect = async (providerId, optionId, credentials, model = null) => {
|
|
321
|
+
return addAgent(providerId, optionId, credentials, model);
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Legacy: Disconnect (removes all agents)
|
|
326
|
+
* @deprecated Use removeAgent() or disconnectAll() instead
|
|
90
327
|
*/
|
|
91
328
|
const disconnect = () => {
|
|
92
|
-
|
|
93
|
-
currentConnection = null;
|
|
329
|
+
disconnectAll();
|
|
94
330
|
};
|
|
95
331
|
|
|
96
332
|
/**
|
|
97
|
-
* Get credentials
|
|
333
|
+
* Get credentials for active agent
|
|
98
334
|
*/
|
|
99
335
|
const getCredentials = () => {
|
|
100
|
-
const
|
|
101
|
-
return
|
|
336
|
+
const agent = getActiveAgent();
|
|
337
|
+
return agent?.credentials || null;
|
|
102
338
|
};
|
|
103
339
|
|
|
104
340
|
/**
|
|
@@ -156,43 +392,22 @@ const validateAnthropic = async (credentials) => {
|
|
|
156
392
|
const isOAuthToken = token && token.startsWith('sk-ant-oat');
|
|
157
393
|
|
|
158
394
|
if (isOAuthToken) {
|
|
159
|
-
// OAuth tokens
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (response.ok) {
|
|
170
|
-
return { valid: true, tokenType: 'oauth', subscriptionType: credentials.subscriptionType || 'max' };
|
|
395
|
+
// OAuth tokens (from Claude Max/Pro subscription) cannot be validated via public API
|
|
396
|
+
// They use a different authentication flow through claude.ai
|
|
397
|
+
// Trust them if they have the correct format (sk-ant-oatXX-...)
|
|
398
|
+
if (token.length > 50 && /^sk-ant-oat\d{2}-[a-zA-Z0-9_-]+$/.test(token)) {
|
|
399
|
+
return {
|
|
400
|
+
valid: true,
|
|
401
|
+
tokenType: 'oauth',
|
|
402
|
+
subscriptionType: credentials.subscriptionType || 'max',
|
|
403
|
+
trusted: credentials.fromKeychain || false
|
|
404
|
+
};
|
|
171
405
|
}
|
|
172
406
|
|
|
173
|
-
|
|
174
|
-
const altResponse = await fetch('https://claude.ai/api/auth/session', {
|
|
175
|
-
method: 'GET',
|
|
176
|
-
headers: {
|
|
177
|
-
'Authorization': `Bearer ${token}`,
|
|
178
|
-
'Cookie': `sessionKey=${token}`
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
if (altResponse.ok) {
|
|
183
|
-
return { valid: true, tokenType: 'oauth', subscriptionType: credentials.subscriptionType || 'max' };
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// For OAuth tokens from Keychain, trust them without validation
|
|
187
|
-
// since the Keychain already verified the user
|
|
188
|
-
if (credentials.fromKeychain) {
|
|
189
|
-
return { valid: true, tokenType: 'oauth', subscriptionType: credentials.subscriptionType || 'max', trusted: true };
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return { valid: false, error: 'OAuth token expired or invalid' };
|
|
407
|
+
return { valid: false, error: 'Invalid OAuth token format' };
|
|
193
408
|
}
|
|
194
409
|
|
|
195
|
-
// Standard API key validation
|
|
410
|
+
// Standard API key validation (sk-ant-api...)
|
|
196
411
|
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
197
412
|
method: 'POST',
|
|
198
413
|
headers: {
|
|
@@ -214,6 +429,16 @@ const validateAnthropic = async (credentials) => {
|
|
|
214
429
|
const error = await response.json();
|
|
215
430
|
return { valid: false, error: error.error?.message || 'Invalid API key' };
|
|
216
431
|
} catch (e) {
|
|
432
|
+
// Network error - if it's an OAuth token, still accept it (can't validate anyway)
|
|
433
|
+
const token = credentials.apiKey || credentials.sessionKey || credentials.accessToken;
|
|
434
|
+
if (token && token.startsWith('sk-ant-oat') && token.length > 50) {
|
|
435
|
+
return {
|
|
436
|
+
valid: true,
|
|
437
|
+
tokenType: 'oauth',
|
|
438
|
+
subscriptionType: credentials.subscriptionType || 'max',
|
|
439
|
+
warning: 'Could not validate online (network error), but token format is valid'
|
|
440
|
+
};
|
|
441
|
+
}
|
|
217
442
|
return { valid: false, error: e.message };
|
|
218
443
|
}
|
|
219
444
|
};
|
|
@@ -401,14 +626,32 @@ const validateOpenAICompatible = async (provider, credentials) => {
|
|
|
401
626
|
};
|
|
402
627
|
|
|
403
628
|
module.exports = {
|
|
629
|
+
// Provider info
|
|
404
630
|
getProviders,
|
|
405
631
|
getProvider,
|
|
632
|
+
|
|
633
|
+
// Multi-agent API
|
|
634
|
+
getAgents,
|
|
635
|
+
getAgentCount,
|
|
636
|
+
getAgent,
|
|
637
|
+
getActiveAgent,
|
|
638
|
+
setActiveAgent,
|
|
639
|
+
addAgent,
|
|
640
|
+
removeAgent,
|
|
641
|
+
updateAgent,
|
|
642
|
+
disconnectAll,
|
|
643
|
+
|
|
644
|
+
// Legacy API (for backwards compatibility)
|
|
406
645
|
isConnected,
|
|
407
646
|
getConnection,
|
|
408
647
|
connect,
|
|
409
648
|
disconnect,
|
|
410
649
|
getCredentials,
|
|
650
|
+
|
|
651
|
+
// Validation
|
|
411
652
|
validateConnection,
|
|
653
|
+
|
|
654
|
+
// Settings
|
|
412
655
|
getAISettings,
|
|
413
656
|
saveAISettings
|
|
414
657
|
};
|