samarthya-bot 1.0.2

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 (50) hide show
  1. package/README.md +92 -0
  2. package/backend/.env.example +23 -0
  3. package/backend/bin/samarthya.js +384 -0
  4. package/backend/config/constants.js +71 -0
  5. package/backend/config/db.js +13 -0
  6. package/backend/controllers/auditController.js +86 -0
  7. package/backend/controllers/authController.js +154 -0
  8. package/backend/controllers/chatController.js +158 -0
  9. package/backend/controllers/fileController.js +268 -0
  10. package/backend/controllers/platformController.js +54 -0
  11. package/backend/controllers/screenController.js +91 -0
  12. package/backend/controllers/telegramController.js +120 -0
  13. package/backend/controllers/toolsController.js +56 -0
  14. package/backend/controllers/whatsappController.js +214 -0
  15. package/backend/fix_toolRegistry.js +25 -0
  16. package/backend/middleware/auth.js +28 -0
  17. package/backend/models/AuditLog.js +28 -0
  18. package/backend/models/BackgroundJob.js +13 -0
  19. package/backend/models/Conversation.js +40 -0
  20. package/backend/models/Memory.js +17 -0
  21. package/backend/models/User.js +24 -0
  22. package/backend/package-lock.json +3766 -0
  23. package/backend/package.json +41 -0
  24. package/backend/public/assets/index-Ckf0GO1B.css +1 -0
  25. package/backend/public/assets/index-Do4jNsZS.js +19 -0
  26. package/backend/public/assets/index-Ui-pyZvK.js +25 -0
  27. package/backend/public/favicon.svg +17 -0
  28. package/backend/public/index.html +18 -0
  29. package/backend/public/manifest.json +16 -0
  30. package/backend/routes/audit.js +9 -0
  31. package/backend/routes/auth.js +11 -0
  32. package/backend/routes/chat.js +11 -0
  33. package/backend/routes/files.js +14 -0
  34. package/backend/routes/platform.js +18 -0
  35. package/backend/routes/screen.js +10 -0
  36. package/backend/routes/telegram.js +8 -0
  37. package/backend/routes/tools.js +9 -0
  38. package/backend/routes/whatsapp.js +11 -0
  39. package/backend/server.js +134 -0
  40. package/backend/services/background/backgroundService.js +81 -0
  41. package/backend/services/llm/llmService.js +444 -0
  42. package/backend/services/memory/memoryService.js +159 -0
  43. package/backend/services/planner/plannerService.js +182 -0
  44. package/backend/services/security/securityService.js +166 -0
  45. package/backend/services/telegram/telegramService.js +49 -0
  46. package/backend/services/tools/toolRegistry.js +879 -0
  47. package/backend/services/whatsapp/whatsappService.js +254 -0
  48. package/backend/test_email.js +29 -0
  49. package/backend/test_parser.js +10 -0
  50. package/package.json +49 -0
@@ -0,0 +1,91 @@
1
+ const llmService = require('../services/llm/llmService');
2
+ const securityService = require('../services/security/securityService');
3
+ const AuditLog = require('../models/AuditLog');
4
+
5
+ /**
6
+ * Analyze a screenshot via Gemini Vision
7
+ * POST /api/screen/analyze
8
+ * Body: { image: base64string, prompt?: string }
9
+ */
10
+ exports.analyzeScreen = async (req, res) => {
11
+ try {
12
+ const { image, prompt } = req.body;
13
+
14
+ if (!image) {
15
+ return res.status(400).json({ error: 'Screenshot image required (base64)' });
16
+ }
17
+
18
+ // Remove data URL prefix if present
19
+ let base64Image = image;
20
+ if (base64Image.startsWith('data:image')) {
21
+ base64Image = base64Image.split(',')[1];
22
+ }
23
+
24
+ // Size check (max ~10MB base64)
25
+ if (base64Image.length > 14_000_000) {
26
+ return res.status(400).json({ error: 'Image too large. Max 10MB.' });
27
+ }
28
+
29
+ const user = req.user;
30
+
31
+ // Audit log
32
+ await AuditLog.create({
33
+ userId: user._id,
34
+ action: 'Screen analysis requested',
35
+ category: 'browser',
36
+ details: {
37
+ toolName: 'screen_understanding',
38
+ prompt: prompt || 'Auto-analyze',
39
+ imageSize: `${Math.round(base64Image.length / 1024)}KB`,
40
+ riskLevel: 'medium'
41
+ },
42
+ status: 'success',
43
+ riskLevel: 'medium'
44
+ });
45
+
46
+ // Call Gemini Vision
47
+ const analysis = await llmService.analyzeScreen(
48
+ base64Image,
49
+ prompt || 'Analyze this screenshot. Identify what app/website this is, what the user is trying to do, and suggest the next steps.',
50
+ user
51
+ );
52
+
53
+ // Mask any sensitive data in the analysis
54
+ analysis.content = securityService.maskSensitiveData(analysis.content);
55
+
56
+ res.json({
57
+ success: true,
58
+ analysis: {
59
+ content: analysis.content,
60
+ model: analysis.model,
61
+ tokensUsed: analysis.tokensUsed || 0
62
+ }
63
+ });
64
+
65
+ } catch (error) {
66
+ console.error('Screen analyze error:', error);
67
+ res.status(500).json({
68
+ error: 'Screen analysis failed',
69
+ message: error.message
70
+ });
71
+ }
72
+ };
73
+
74
+ /**
75
+ * List supported screen types this AI can understand
76
+ * GET /api/screen/supported
77
+ */
78
+ exports.getSupportedScreens = (req, res) => {
79
+ res.json({
80
+ supported: [
81
+ { id: 'irctc', name: 'IRCTC', nameHi: 'आईआरसीटीसी', desc: 'Train booking, PNR status, tatkal' },
82
+ { id: 'gst', name: 'GST Portal', nameHi: 'जीएसटी पोर्टल', desc: 'GSTR filing, returns, challan' },
83
+ { id: 'digilocker', name: 'DigiLocker', nameHi: 'डिजीलॉकर', desc: 'Document download, Aadhaar, PAN' },
84
+ { id: 'upi', name: 'UPI Apps', nameHi: 'यूपीआई ऐप्स', desc: 'GPay, PhonePe, Paytm transactions' },
85
+ { id: 'banking', name: 'Net Banking', nameHi: 'नेट बैंकिंग', desc: 'Balance check, transfers, statements' },
86
+ { id: 'ecommerce', name: 'E-Commerce', nameHi: 'ई-कॉमर्स', desc: 'Amazon, Flipkart orders & tracking' },
87
+ { id: 'govt', name: 'Govt Portals', nameHi: 'सरकारी पोर्टल', desc: 'Income tax, passport, PF' },
88
+ { id: 'any', name: 'Any Website', nameHi: 'कोई भी वेबसाइट', desc: 'General form filling assistance' },
89
+ ]
90
+ });
91
+ };
@@ -0,0 +1,120 @@
1
+ const telegramService = require('../services/telegram/telegramService');
2
+ const llmService = require('../services/llm/llmService');
3
+ const securityService = require('../services/security/securityService');
4
+ const plannerService = require('../services/planner/plannerService');
5
+ const User = require('../models/User');
6
+ const Conversation = require('../models/Conversation');
7
+ const Memory = require('../models/Memory');
8
+
9
+ /**
10
+ * Handle incoming Telegram messages (POST webhook)
11
+ */
12
+ exports.handleMessage = async (req, res) => {
13
+ // Always respond 200 OK fast
14
+ res.status(200).send('OK');
15
+
16
+ try {
17
+ const update = req.body;
18
+ // Check if message and text exist
19
+ if (!update.message || !update.message.text) return;
20
+
21
+ const chatId = update.message.chat.id;
22
+ const text = update.message.text;
23
+ const fromName = update.message.from?.first_name || 'User';
24
+ const username = update.message.from?.username || chatId;
25
+
26
+ console.log(`📱 Telegram from ${fromName} (${chatId}): ${text.substring(0, 50)}`);
27
+
28
+ // Find or create user by chatId
29
+ let user = await User.findOne({ email: `tg_${chatId}@samarthya.local` });
30
+ if (!user) {
31
+ user = await User.create({
32
+ name: fromName,
33
+ email: `tg_${chatId}@samarthya.local`,
34
+ password: 'telegram_user',
35
+ language: 'hinglish',
36
+ workType: 'personal',
37
+ activePack: 'personal',
38
+ source: 'telegram'
39
+ });
40
+
41
+ // Welcome message for new Telegram users
42
+ await telegramService.sendMessage(chatId,
43
+ '🙏 Namaste! Main *SamarthyaBot* hoon.\n\n' +
44
+ 'Main aapka personal AI assistant hoon.\n\n' +
45
+ '💡 Try karo:\n' +
46
+ '• "Bhai, 5 log ka movie ticket ka bill split karo"\n' +
47
+ '• "Kal meeting create karo"\n' +
48
+ '• "Summarize this long text..."\n\n' +
49
+ '🔐 Aapka data yahan puri tarah safe hai.'
50
+ );
51
+ return;
52
+ }
53
+
54
+ // Security check
55
+ const securityReport = securityService.scanForSensitiveData(text);
56
+ if (securityReport.found) {
57
+ await telegramService.sendMessage(chatId,
58
+ `⚠️ *Sensitive Data Detected*: ${securityReport.types.join(', ')}\n` +
59
+ 'Maine automatically isko mask kar diya hai.'
60
+ );
61
+ }
62
+
63
+ // Context (Memories)
64
+ const memories = await Memory.find({ userId: user._id })
65
+ .sort({ importance: -1 })
66
+ .limit(10)
67
+ .lean();
68
+
69
+ // Previous Conversation
70
+ let conversation = await Conversation.findOne({
71
+ userId: user._id,
72
+ source: 'telegram',
73
+ updatedAt: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }
74
+ }).sort({ updatedAt: -1 });
75
+
76
+ const previousMessages = conversation?.messages?.slice(-6)?.map(m => ({
77
+ role: m.role,
78
+ content: m.content
79
+ })) || [];
80
+
81
+ // Progress handler
82
+ const onProgress = async (updateText) => {
83
+ // Send the agent's thought/plan as an intermediate message
84
+ if (updateText && updateText.trim().length > 0) {
85
+ await telegramService.sendMessage(chatId, `🧠 *Agent Progress:*${updateText}`);
86
+ }
87
+ };
88
+
89
+ // Process through the full Agentic Planner (OS Tools, Web, Mem)
90
+ const result = await plannerService.processMessage(user, previousMessages, text, onProgress);
91
+
92
+ let replyText = result.response;
93
+ // Truncate if too large (~4096 limit in Telegram)
94
+ if (replyText.length > 4000) {
95
+ replyText = replyText.substring(0, 3990) + '\n\n... (truncated)';
96
+ }
97
+
98
+ await telegramService.sendMessage(chatId, replyText);
99
+
100
+ // Save conversation
101
+ if (!conversation) {
102
+ conversation = new Conversation({ userId: user._id, title: text.substring(0, 20), source: 'telegram', messages: [] });
103
+ }
104
+ conversation.messages.push({ role: 'user', content: text });
105
+ conversation.messages.push({
106
+ role: 'assistant',
107
+ content: result.response,
108
+ toolCalls: result.toolCalls,
109
+ language: result.language,
110
+ metadata: {
111
+ tokensUsed: result.tokensUsed,
112
+ model: result.model
113
+ }
114
+ });
115
+ await conversation.save();
116
+
117
+ } catch (error) {
118
+ console.error('Telegram handler error:', error);
119
+ }
120
+ };
@@ -0,0 +1,56 @@
1
+ const toolRegistry = require('../services/tools/toolRegistry');
2
+ const { TOOL_PACKS } = require('../config/constants');
3
+
4
+ // Get all available tools
5
+ exports.getTools = async (req, res) => {
6
+ try {
7
+ const tools = toolRegistry.getAllTools().map(t => ({
8
+ name: t.name,
9
+ description: t.description,
10
+ descriptionHi: t.descriptionHi,
11
+ riskLevel: t.riskLevel,
12
+ category: t.category,
13
+ parameters: t.parameters
14
+ }));
15
+
16
+ res.json({ success: true, tools });
17
+ } catch (error) {
18
+ res.status(500).json({ success: false, message: error.message });
19
+ }
20
+ };
21
+
22
+ // Get tool packs
23
+ exports.getToolPacks = async (req, res) => {
24
+ try {
25
+ const packs = Object.entries(TOOL_PACKS).map(([key, pack]) => ({
26
+ id: key,
27
+ name: pack.name,
28
+ nameHi: pack.nameHi,
29
+ description: pack.description,
30
+ toolCount: pack.tools.length,
31
+ tools: pack.tools
32
+ }));
33
+
34
+ res.json({ success: true, packs });
35
+ } catch (error) {
36
+ res.status(500).json({ success: false, message: error.message });
37
+ }
38
+ };
39
+
40
+ // Get tools for a specific pack
41
+ exports.getPackTools = async (req, res) => {
42
+ try {
43
+ const { packId } = req.params;
44
+ const tools = toolRegistry.getToolsForPack(packId).map(t => ({
45
+ name: t.name,
46
+ description: t.description,
47
+ descriptionHi: t.descriptionHi,
48
+ riskLevel: t.riskLevel,
49
+ category: t.category
50
+ }));
51
+
52
+ res.json({ success: true, tools, pack: TOOL_PACKS[packId] });
53
+ } catch (error) {
54
+ res.status(500).json({ success: false, message: error.message });
55
+ }
56
+ };
@@ -0,0 +1,214 @@
1
+ const whatsappService = require('../services/whatsapp/whatsappService');
2
+ const llmService = require('../services/llm/llmService');
3
+ const securityService = require('../services/security/securityService');
4
+ const User = require('../models/User');
5
+ const Conversation = require('../models/Conversation');
6
+ const Memory = require('../models/Memory');
7
+
8
+ /**
9
+ * WhatsApp Webhook Verification (GET)
10
+ * Meta sends this to verify your webhook URL
11
+ */
12
+ exports.verifyWebhook = (req, res) => {
13
+ const mode = req.query['hub.mode'];
14
+ const token = req.query['hub.verify_token'];
15
+ const challenge = req.query['hub.challenge'];
16
+
17
+ if (mode === 'subscribe' && token === whatsappService.verifyToken) {
18
+ console.log('✅ WhatsApp webhook verified');
19
+ return res.status(200).send(challenge);
20
+ }
21
+
22
+ console.error('❌ WhatsApp webhook verification failed');
23
+ return res.status(403).send('Forbidden');
24
+ };
25
+
26
+ /**
27
+ * WhatsApp Incoming Message Handler (POST)
28
+ * Processes incoming messages and sends AI responses
29
+ */
30
+ exports.handleMessage = async (req, res) => {
31
+ // Always respond 200 quickly (WhatsApp requires fast ACK)
32
+ res.status(200).send('EVENT_RECEIVED');
33
+
34
+ try {
35
+ const parsed = whatsappService.parseWebhookMessage(req.body);
36
+ if (!parsed) return;
37
+
38
+ console.log(`📱 WhatsApp from ${parsed.contactName} (${parsed.from}): ${parsed.text || parsed.type}`);
39
+
40
+ // Mark as read
41
+ await whatsappService.markAsRead(parsed.messageId);
42
+
43
+ // Find or create user by phone
44
+ let user = await User.findOne({ phone: parsed.from });
45
+ if (!user) {
46
+ user = await User.create({
47
+ name: parsed.contactName,
48
+ phone: parsed.from,
49
+ email: `wa_${parsed.from}@samarthya.local`,
50
+ password: 'whatsapp_user',
51
+ language: 'hinglish',
52
+ workType: 'personal',
53
+ activePack: 'personal',
54
+ source: 'whatsapp'
55
+ });
56
+
57
+ // Welcome message for new WhatsApp users
58
+ await whatsappService.sendMessage(parsed.from,
59
+ '🙏 Namaste! Main SamarthyaBot hoon.\n\n' +
60
+ 'Main aapka personal AI assistant hoon. Hindi, Hinglish ya English mein baat karo!\n\n' +
61
+ '💡 Try karo:\n' +
62
+ '• "GST ki deadline kab hai?"\n' +
63
+ '• "500 * 18% calculate karo"\n' +
64
+ '• "Reminder set karo"\n' +
65
+ '• Screenshot bhejo — main samajh lunga!\n\n' +
66
+ '🔐 Aapka data safe hai. PAN/Aadhaar auto-mask hota hai.'
67
+ );
68
+ return;
69
+ }
70
+
71
+ // Handle image messages → Screen Understanding
72
+ if (parsed.type === 'image' && parsed.mediaId) {
73
+ await handleScreenshot(parsed, user);
74
+ return;
75
+ }
76
+
77
+ // Handle text messages → AI pipeline
78
+ if (parsed.text) {
79
+ await handleTextMessage(parsed, user);
80
+ return;
81
+ }
82
+
83
+ // Handle audio → notify not supported yet
84
+ if (parsed.type === 'audio') {
85
+ await whatsappService.sendMessage(parsed.from,
86
+ '🎤 Voice notes ka support jald aa raha hai! Abhi ke liye text mein likho ya screenshot bhejo.'
87
+ );
88
+ }
89
+
90
+ } catch (error) {
91
+ console.error('WhatsApp handler error:', error);
92
+ }
93
+ };
94
+
95
+ /**
96
+ * Process text messages through AI pipeline
97
+ */
98
+ async function handleTextMessage(parsed, user) {
99
+ try {
100
+ // Security scan
101
+ const securityReport = securityService.scanForSensitiveData(parsed.text);
102
+ if (securityReport.found) {
103
+ await whatsappService.sendMessage(parsed.from,
104
+ `⚠️ Sensitive data detected: ${securityReport.types.join(', ')}\n` +
105
+ '🔐 Main isko mask kar dunga. Dhyan rakhein!'
106
+ );
107
+ }
108
+
109
+ // Get user memories for context
110
+ const memories = await Memory.find({ userId: user._id })
111
+ .sort({ importance: -1 })
112
+ .limit(10)
113
+ .lean();
114
+
115
+ // Get/create conversation
116
+ let conversation = await Conversation.findOne({
117
+ userId: user._id,
118
+ source: 'whatsapp',
119
+ updatedAt: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) } // last 24h
120
+ }).sort({ updatedAt: -1 });
121
+
122
+ const previousMessages = conversation?.messages?.slice(-6)?.map(m => ({
123
+ role: m.role,
124
+ content: m.content
125
+ })) || [];
126
+
127
+ // Build prompt and call LLM
128
+ const systemPrompt = llmService.buildSystemPrompt(user, [], memories);
129
+ const messages = [
130
+ ...previousMessages,
131
+ { role: 'user', content: parsed.text }
132
+ ];
133
+
134
+ const response = await llmService.chat(messages, systemPrompt, user);
135
+
136
+ // Truncate for WhatsApp (4096 char limit)
137
+ let replyText = response.content;
138
+ if (replyText.length > 4000) {
139
+ replyText = replyText.substring(0, 3990) + '\n\n... (truncated)';
140
+ }
141
+
142
+ // Mask sensitive data in response
143
+ replyText = securityService.maskSensitiveData(replyText);
144
+
145
+ // Save conversation
146
+ if (!conversation) {
147
+ conversation = await Conversation.create({
148
+ userId: user._id,
149
+ title: parsed.text.substring(0, 50),
150
+ source: 'whatsapp',
151
+ messages: []
152
+ });
153
+ }
154
+
155
+ conversation.messages.push(
156
+ { role: 'user', content: parsed.text },
157
+ { role: 'assistant', content: replyText }
158
+ );
159
+ await conversation.save();
160
+
161
+ // Send reply
162
+ await whatsappService.sendMessage(parsed.from, replyText);
163
+
164
+ } catch (error) {
165
+ console.error('Text message handler error:', error);
166
+ await whatsappService.sendMessage(parsed.from,
167
+ '❌ Kuch error aa gaya. Thodi der baad try karo.'
168
+ );
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Handle screenshot messages → Gemini Vision (Screen Understanding)
174
+ */
175
+ async function handleScreenshot(parsed, user) {
176
+ try {
177
+ await whatsappService.sendMessage(parsed.from,
178
+ '👁️ Screenshot received! Analyzing...'
179
+ );
180
+
181
+ // Download image from WhatsApp
182
+ const media = await whatsappService.downloadMedia(parsed.mediaId);
183
+ if (!media) {
184
+ await whatsappService.sendMessage(parsed.from,
185
+ '❌ Image download fail ho gaya. Dubara bhejo.'
186
+ );
187
+ return;
188
+ }
189
+
190
+ // Convert to base64 for Gemini Vision
191
+ const base64Image = media.buffer.toString('base64');
192
+ const prompt = parsed.caption ||
193
+ 'Is screenshot mein kya hai? Mujhe batao aur next step suggest karo.';
194
+
195
+ // Call Gemini Vision
196
+ const analysis = await llmService.analyzeScreen(base64Image, prompt, user);
197
+
198
+ // Truncate for WhatsApp
199
+ let replyText = analysis.content;
200
+ if (replyText.length > 4000) {
201
+ replyText = replyText.substring(0, 3990) + '\n\n... (truncated)';
202
+ }
203
+
204
+ replyText = securityService.maskSensitiveData(replyText);
205
+
206
+ await whatsappService.sendMessage(parsed.from, replyText);
207
+
208
+ } catch (error) {
209
+ console.error('Screenshot handler error:', error);
210
+ await whatsappService.sendMessage(parsed.from,
211
+ '❌ Screen analysis mein error aa gaya. Check karo ki Gemini API key set hai .env mein.'
212
+ );
213
+ }
214
+ }
@@ -0,0 +1,25 @@
1
+ const fs = require('fs');
2
+
3
+ let content = fs.readFileSync('services/tools/toolRegistry.js', 'utf8');
4
+
5
+ // 1. Replace SAFE_DIR definition
6
+ content = content.replace(
7
+ /const SAFE_DIR = path\.join\(os\.homedir\(\), 'SamarthyaBot_Files'\);\n\n\/\/ Ensure safe directory exists on startup\n\(\(async \(\) => {\n try { await fs\.mkdir\(SAFE_DIR, { recursive: true } \); } catch \(e\) { \/\* ignore \*\/ }\n}\)\(\);/g,
8
+ `const BASE_DIR = path.join(os.homedir(), 'SamarthyaBot_Files');\n\nasync function getSafeDir(user) {\n let dir = BASE_DIR;\n if (user && (user._id || user.id)) {\n dir = path.join(BASE_DIR, String(user._id || user.id));\n }\n try { await fs.mkdir(dir, { recursive: true }); } catch(e) {}\n return dir;\n}`
9
+ );
10
+
11
+ // 2. update execute function signatures specifically
12
+ content = content.replace(/execute: async \(args\) => {/g, 'execute: async (args, userContext) => {');
13
+ content = content.replace(/execute: async \(\) => {/g, 'execute: async (args, userContext) => {');
14
+
15
+ // 3. inject SAFE_DIR into the functions that need it
16
+ const fileFuncs = ['file_read', 'file_write', 'file_list', 'note_take', 'reminder_set', 'reminders_get', 'gst_reminder', 'calendar_schedule'];
17
+ content = content.replace(/execute: async \(args, userContext\) => {\n try {/g,
18
+ 'execute: async (args, userContext) => {\n try {\n const SAFE_DIR = await getSafeDir(userContext);');
19
+
20
+ // 4. Update executeTool and execute calls
21
+ content = content.replace(/async executeTool\(name, args\) {/g, 'async executeTool(name, args, user) {');
22
+ content = content.replace(/const result = await tool\.execute\(args\);/g, 'const result = await tool.execute(args, user);');
23
+
24
+ fs.writeFileSync('services/tools/toolRegistry.js', content);
25
+ console.log('Fixed toolRegistry.js');
@@ -0,0 +1,28 @@
1
+ const User = require('../models/User');
2
+
3
+ const auth = async (req, res, next) => {
4
+ try {
5
+ // Local OS Mode: Bypass JWT Authentication
6
+ // We automatically find or create a default "Local Admin" user.
7
+ let user = await User.findOne({ email: 'admin@samarthya.local' });
8
+
9
+ if (!user) {
10
+ user = new User({
11
+ name: 'System Admin',
12
+ email: 'admin@samarthya.local',
13
+ password: 'no_password_needed_for_local',
14
+ role: 'admin',
15
+ activePack: 'developer'
16
+ });
17
+ await user.save();
18
+ }
19
+
20
+ req.user = user;
21
+ next();
22
+ } catch (error) {
23
+ console.error("Auth Middleware Error:", error);
24
+ res.status(500).json({ success: false, message: 'Local Auth Error', error: error.message });
25
+ }
26
+ };
27
+
28
+ module.exports = auth;
@@ -0,0 +1,28 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const auditLogSchema = new mongoose.Schema({
4
+ userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
5
+ conversationId: { type: mongoose.Schema.Types.ObjectId, ref: 'Conversation' },
6
+ action: { type: String, required: true },
7
+ category: {
8
+ type: String,
9
+ enum: ['file', 'email', 'browser', 'calendar', 'search', 'system', 'security', 'tool'],
10
+ required: true
11
+ },
12
+ details: {
13
+ toolName: String,
14
+ input: mongoose.Schema.Types.Mixed,
15
+ output: mongoose.Schema.Types.Mixed,
16
+ riskLevel: { type: String, enum: ['low', 'medium', 'high', 'critical'], default: 'low' },
17
+ sensitiveDataFound: [String],
18
+ permissionGranted: Boolean
19
+ },
20
+ status: { type: String, enum: ['success', 'failed', 'blocked', 'rolled_back'], default: 'success' },
21
+ rollbackData: mongoose.Schema.Types.Mixed,
22
+ canRollback: { type: Boolean, default: false }
23
+ }, { timestamps: true });
24
+
25
+ auditLogSchema.index({ userId: 1, createdAt: -1 });
26
+ auditLogSchema.index({ category: 1 });
27
+
28
+ module.exports = mongoose.model('AuditLog', auditLogSchema);
@@ -0,0 +1,13 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const backgroundJobSchema = new mongoose.Schema({
4
+ userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
5
+ taskName: { type: String, required: true },
6
+ prompt: { type: String, required: true },
7
+ intervalMinutes: { type: Number, required: true }, // 0 means run once
8
+ lastRunAt: { type: Date },
9
+ nextRunAt: { type: Date, required: true },
10
+ isActive: { type: Boolean, default: true },
11
+ });
12
+
13
+ module.exports = mongoose.model('BackgroundJob', backgroundJobSchema);
@@ -0,0 +1,40 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const messageSchema = new mongoose.Schema({
4
+ role: { type: String, enum: ['user', 'assistant', 'system', 'tool'], required: true },
5
+ content: { type: String, required: true },
6
+ language: { type: String, enum: ['hindi', 'hinglish', 'english'], default: 'english' },
7
+ toolCalls: [{
8
+ toolName: String,
9
+ arguments: mongoose.Schema.Types.Mixed,
10
+ result: mongoose.Schema.Types.Mixed,
11
+ status: { type: String, enum: ['pending', 'running', 'completed', 'failed', 'blocked'], default: 'pending' },
12
+ riskLevel: { type: String, enum: ['low', 'medium', 'high', 'critical'], default: 'low' },
13
+ notificationParams: mongoose.Schema.Types.Mixed,
14
+ executedAt: Date
15
+ }],
16
+ metadata: {
17
+ tokensUsed: Number,
18
+ responseTime: Number,
19
+ model: String,
20
+ sensitiveDataDetected: [String]
21
+ }
22
+ }, { timestamps: true });
23
+
24
+ const conversationSchema = new mongoose.Schema({
25
+ userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
26
+ title: { type: String, default: 'New Conversation' },
27
+ messages: [messageSchema],
28
+ context: {
29
+ activePack: String,
30
+ language: String,
31
+ taskType: String
32
+ },
33
+ isActive: { type: Boolean, default: true },
34
+ isPinned: { type: Boolean, default: false },
35
+ source: { type: String, enum: ['web', 'whatsapp', 'telegram'], default: 'web' }
36
+ }, { timestamps: true });
37
+
38
+ conversationSchema.index({ userId: 1, updatedAt: -1 });
39
+
40
+ module.exports = mongoose.model('Conversation', conversationSchema);
@@ -0,0 +1,17 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const memorySchema = new mongoose.Schema({
4
+ userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
5
+ type: { type: String, enum: ['preference', 'fact', 'task', 'context', 'reminder'], required: true },
6
+ key: { type: String, required: true },
7
+ value: { type: mongoose.Schema.Types.Mixed, required: true },
8
+ importance: { type: Number, default: 5, min: 1, max: 10 },
9
+ expiresAt: Date,
10
+ source: { type: String, default: 'conversation' },
11
+ tags: [String]
12
+ }, { timestamps: true });
13
+
14
+ memorySchema.index({ userId: 1, type: 1 });
15
+ memorySchema.index({ userId: 1, key: 1 }, { unique: true });
16
+
17
+ module.exports = mongoose.model('Memory', memorySchema);
@@ -0,0 +1,24 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const userSchema = new mongoose.Schema({
4
+ name: { type: String, required: true },
5
+ email: { type: String, required: true, unique: true },
6
+ password: { type: String, required: true },
7
+ language: { type: String, enum: ['hindi', 'hinglish', 'english'], default: 'hinglish' },
8
+ city: { type: String, default: '' },
9
+ timezone: { type: String, default: 'Asia/Kolkata' },
10
+ workType: { type: String, enum: ['student', 'business', 'developer', 'personal', 'other'], default: 'personal' },
11
+ activePack: { type: String, enum: ['student', 'business', 'developer', 'personal'], default: 'personal' },
12
+ permissions: {
13
+ fileAccess: { type: String, enum: ['allow_once', 'allow_always', 'never', 'ask'], default: 'ask' },
14
+ emailAccess: { type: String, enum: ['allow_once', 'allow_always', 'never', 'ask'], default: 'ask' },
15
+ browserAccess: { type: String, enum: ['allow_once', 'allow_always', 'never', 'ask'], default: 'ask' },
16
+ calendarAccess: { type: String, enum: ['allow_once', 'allow_always', 'never', 'ask'], default: 'ask' },
17
+ },
18
+ avatar: { type: String, default: '' },
19
+ phone: { type: String, default: '', index: true },
20
+ source: { type: String, enum: ['web', 'whatsapp', 'telegram'], default: 'web' },
21
+ isActive: { type: Boolean, default: true }
22
+ }, { timestamps: true });
23
+
24
+ module.exports = mongoose.model('User', userSchema);