File size: 22,097 Bytes
01043cc
 
 
 
 
7d94478
 
01043cc
 
 
 
 
 
 
 
 
 
5d8665e
d6c1381
01043cc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7d94478
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
01043cc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7d94478
 
01043cc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28001dd
01043cc
 
 
 
 
 
 
 
 
 
 
 
 
 
4e064dc
28001dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4e064dc
 
 
01043cc
 
 
 
 
4e064dc
 
 
 
01043cc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4e064dc
 
 
 
01043cc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7d94478
 
 
01043cc
 
 
 
 
 
 
 
 
 
 
 
 
5d8665e
01043cc
 
 
 
 
 
 
 
 
 
7d94478
 
 
01043cc
 
 
 
 
a23237e
01043cc
 
 
 
 
 
 
 
 
 
c1ea8e8
 
 
 
 
 
 
01043cc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20fadb9
01043cc
 
 
 
 
 
 
 
 
 
 
7d94478
 
 
01043cc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
import express from 'express';
import cors from 'cors';
import fs from 'fs';
import path from 'path';
import { createClient } from '@supabase/supabase-js';
import { search } from "./research.js";


const PORT = 7860;
const SUPABASE_URL = process.env.SUPABASE_URL;
const SUPABASE_KEY = process.env.SUPABASE_SERVICE_KEY;
const REMOTE_SERVER_URL = process.env.REMOTE_AI_URL || "http://localhost:11434"; 
const FRONT_URL = process.env.FRONT_URL;
const CRON_REGISTRY_URL = process.env.CRON_REGISTRY_URL || "http://localhost:7861"; 
const CRON_SECRET = process.env.CRON_SECRET || "default_secret";

const SMART_MODEL_ID = "claude"; 
const MIDDLE_MODEL_ID = "haiku";
const FAST_MODEL_ID = "maverick"; //"gpt-5-mini"; 

// The Utility Server that handles the Email Dispatching
const UTILITY_SERVER_URL = process.env.UTILITY_SERVER_URL || "https://lu5zfciin5mk34fowavmhz7dt40pkhhg.lambda-url.us-east-1.on.aws";

if (!SUPABASE_URL || !SUPABASE_KEY) process.exit(1);

const app = express();
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);

app.use(express.json({ limit: '50mb' }));
app.use(cors());

let prompts = {}; 
try {
    prompts = JSON.parse(fs.readFileSync(path.resolve('./prompts.json'), 'utf8'));
} catch (e) { process.exit(1); }

const activeProjects = new Map();

const StateManager = {
    getHistory: async (projectId) => {
        if (activeProjects.has(projectId)) return activeProjects.get(projectId).history;
        const { data: chunks } = await supabase.from('message_chunks').select('*').eq('project_id', projectId).order('chunk_index', { ascending: false }).limit(10);
        const fullHistory = (chunks || []).reverse().flatMap(c => c.payload ||[]);
        activeProjects.set(projectId, { history: fullHistory, isFrozen: false });
        return fullHistory;
    },
    addHistory: async (projectId, role, text) => {
        const newMessage = { role, parts: [{ text }] };
        if (activeProjects.has(projectId)) activeProjects.get(projectId).history.push(newMessage);
        try {
            const { data: latestChunk } = await supabase.from('message_chunks').select('id, chunk_index, payload').eq('project_id', projectId).order('chunk_index', { ascending: false }).limit(1).single();
            const currentPayload = (latestChunk?.payload) ||[];
            if (latestChunk && currentPayload.length < 20) {
                await supabase.from('message_chunks').update({ payload: [...currentPayload, newMessage] }).eq('id', latestChunk.id);
            } else {
                await supabase.from('message_chunks').insert({ project_id: projectId, lead_id: projectId, chunk_index: (latestChunk?.chunk_index ?? -1) + 1, payload: [newMessage] });
            }
        } catch (e) {}
    },
    setFrozen: async (projectId, status) => {
        if (activeProjects.has(projectId)) activeProjects.get(projectId).isFrozen = status;
        await supabase.from('leads').update({ is_frozen: status }).eq('id', projectId);
    },
    isFrozen: async (projectId) => {
        if (activeProjects.has(projectId)) return activeProjects.get(projectId).isFrozen;
        const { data } = await supabase.from('leads').select('is_frozen').eq('id', projectId).single();
        return data?.is_frozen || false;
    }
};


async function callAIWithResearch(history, input, context, images, systemPrompt, projectContext, modelId) {
  const firstPass = await callAI(history, input, context, images, systemPrompt, projectContext, modelId);

  // Check if the AI requested any research
  const researchMatches = [...firstPass.text.matchAll(/<deep_research>([\s\S]*?)<\/deep_research>/gi)];
  if (researchMatches.length === 0) return firstPass;

  // Resolve all research requests in parallel
  const resolved = await Promise.all(
    researchMatches.map(async (match) => {
      try {
        const payload = JSON.parse(match[1].trim());
        const { result } = await search({
          query:   payload.query,
          urgent:  payload.urgent  ?? false,
          deep:    payload.deep    ?? false,
          supabase,
        });
        return `[RESEARCH: "${payload.query}"]\n${result}`;
      } catch (e) {
        console.error("[Research] Failed to resolve query:", e.message);
        return null;
      }
    })
  );

  const researchBlock = resolved.filter(Boolean).join("\n\n────\n\n");
  if (!researchBlock) return firstPass;

  // Second pass: inject research results and let the AI continue
  const augmentedInput =
    `${input}\n\n` +
    `[RESEARCH RESULTS β€” use these to complete your response accurately]:\n${researchBlock}`;

  console.log(`[Research] Re-running AI with ${researchMatches.length} research result(s) injected.`);
  return callAI(history, augmentedInput, context, images, systemPrompt, projectContext, modelId);
}

const callAI = async (history, input, contextData, images, systemPrompt, projectContext, modelId) => {
    let contextStr = "";
    try { contextStr = JSON.stringify(contextData, null, 2); } catch {}
    const recentHistory = history.slice(-10).map(m => `${m.role === 'model' ? 'Assistant' : 'User'}: ${m.parts?.[0]?.text || ""}`).join('\n');
    const fullPrompt = `System: ${systemPrompt}\n\n${projectContext}\n\n[HISTORY]:\n${recentHistory}\n\n[CONTEXT]: ${contextStr}\n\nUser: ${input}\nAssistant:`;

    try {
        const response = await fetch(`${REMOTE_SERVER_URL}/api/generate`, {
            method: 'POST', headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ model: modelId, prompt: fullPrompt, system_prompt: systemPrompt, images: images ||[] })
        });
        const result = await response.json();
        return { text: result.data || result.text || "", usage: result.usage };
    } catch (e) { return { text: "<notification>AI Unreachable</notification>", usage: {} }; }
};

function extractCommands(text) {
    const commands =[];
    const parse = (regex, type, isJson = true) => {
        let match;
        regex.lastIndex = 0;
        while ((match = regex.exec(text)) !== null) {
            let rawPayload = match[1].trim();
            try {
                commands.push({ type, payload: isJson ? JSON.parse(rawPayload) : rawPayload });
            } catch (e) { 
                if (type === 'create_thrust') commands.push({ type, payload: { title: "System Thrust", markdown_content: rawPayload, tasks:[] } });
            }
        }
    };

    parse(/<thrust_create>([\s\S]*?)<\/thrust_create>/gi, 'create_thrust');
    parse(/<timeline_log>([\s\S]*?)<\/timeline_log>/gi, 'log_timeline');
    parse(/<notification>([\s\S]*?)<\/notification>/gi, 'notification', false);
    parse(/<update_requirements>([\s\S]*?)<\/update_requirements>/gi, 'update_requirements', false);
    parse(/<schedule_briefing>([\s\S]*?)<\/schedule_briefing>/gi, 'schedule_briefing');
    parse(/<freeze_project>([\s\S]*?)<\/freeze_project>/gi, 'freeze_project', false); 
    parse(/<thrust_complete>([\s\S]*?)<\/thrust_complete>/gi, 'thrust_complete', false); 
    parse(/<complete_task>([\s\S]*?)<\/complete_task>/gi, 'complete_task', false); 
    
    // NEW: Extract AI queries sent to local MCPs
    parse(/<mcp_query>([\s\S]*?)<\/mcp_query>/gi, 'mcp_query');
    parse(/<deep_research>([\s\S]*?)<\/deep_research>/gi, 'deep_research');
  
    return commands;
}

// --- BULLETPROOF CRON MATH ---
async function registerMorningCron(projectId, offset) {
    const now = new Date();
    const target = new Date(now);
    
    const targetUtcMinutes = (5 * 60) - (offset * 60); 
    target.setUTCHours(0, targetUtcMinutes, 0, 0); 
    
    if (target <= now) target.setDate(target.getDate() + 1); 

    const delayMs = target.getTime() - now.getTime();
    console.log(`⏰ Briefing Scheduled for ${projectId}. Local 6AM occurs in ${(delayMs/1000/60/60).toFixed(2)} hours.`);

    fetch(`${CRON_REGISTRY_URL}/register`, {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ 
            secret: CRON_SECRET, 
            jobId: `briefing_${projectId}`, 
            intervalMs: 86400000, 
            initialDelay: delayMs, 
            webhookUrl: `https://everydaytok-thrust-core-server.hf.space/automated-briefing`, 
            leadId: projectId, 
            payload: { projectId, timezoneOffset: offset } 
        })
    }).catch(()=>{});
}

// --- EMAIL DISPATCHER HELPER ---
async function dispatchEmail(userId, projectId, projectName, briefingId, markdownContent) {
    console.log(`πŸ“§ Dispatching email to Utility Server for Project: ${projectName}`);
    fetch(`${UTILITY_SERVER_URL}/api/email/send-briefing`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ userId, projectId, projectName, briefingId, markdownContent })
    }).catch(e => console.error("Email Dispatch Error:", e.message));
}

async function executeCommands(userId, projectId, commands) {
    let flags = { shouldReload: false, thrustComplete: false, newThrustId: null, newThrustMarkdown: null };

    for (const cmd of commands) {
        try {
            /* if (cmd.type === 'create_thrust') {
                await supabase.from('thrusts').delete().eq('lead_id', projectId);

                const { data: thrust } = await supabase.from('thrusts').insert({ lead_id: projectId, title: cmd.payload.title, markdown_content: cmd.payload.markdown_content, status: 'active' }).select().single();
                if (thrust && cmd.payload.tasks && cmd.payload.tasks.length > 0) {
                    const tasks = cmd.payload.tasks.map(t => ({ thrust_id: thrust.id, title: t }));
                    await supabase.from('thrust_tasks').insert(tasks);
                }
                
                flags.shouldReload = true;

                if (thrust) {
                    flags.newThrustId = thrust.id;
                    flags.newThrustMarkdown = cmd.payload.markdown_content;
                }

                if (FRONT_URL) {
                    fetch(`${FRONT_URL}/internal/notify`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user_id: userId, type: 'toast', message: `πŸš€ New Thrust Generated: ${cmd.payload.title}` }) }).catch(() => {});
                }
            } */

          if (cmd.type === 'create_thrust') {
                await supabase.from('thrusts').delete().eq('lead_id', projectId);

                const { data: thrust } = await supabase.from('thrusts').insert({ lead_id: projectId, title: cmd.payload.title, markdown_content: cmd.payload.markdown_content, status: 'active' }).select().single();
                
                if (thrust && cmd.payload.tasks && cmd.payload.tasks.length > 0) {
                    // NEW: Safely map objects to include dynamic AI options
                    const tasks = cmd.payload.tasks.map(t => {
                        if (typeof t === 'string') {
                            return { thrust_id: thrust.id, title: t, options:[], placeholder: "" };
                        } else {
                            return {
                                thrust_id: thrust.id, 
                                title: t.title, 
                                options: t.options ||[], 
                                placeholder: t.placeholder || ""
                            };
                        }
                    });
                    await supabase.from('thrust_tasks').insert(tasks);
                }
                
                flags.shouldReload = true;

                if (thrust) {
                    flags.newThrustId = thrust.id;
                    flags.newThrustMarkdown = cmd.payload.markdown_content;
                }

                if (FRONT_URL) {
                    fetch(`${FRONT_URL}/internal/notify`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user_id: userId, type: 'toast', message: `πŸš€ New Thrust Generated: ${cmd.payload.title}` }) }).catch(() => {});
                }
            }

            if (cmd.type === 'log_timeline') {
                await supabase.from('timeline_events').insert({ lead_id: projectId, title: cmd.payload.title, description: cmd.payload.description, type: (cmd.payload.type || 'system').toLowerCase() });
                flags.shouldReload = true;

                if (FRONT_URL) {
                    fetch(`${FRONT_URL}/internal/notify`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user_id: userId, type: 'toast', message: `πŸ•’ Timeline Updated: ${cmd.payload.title}` }) }).catch(() => {});
                }
            }

            if (cmd.type === 'update_requirements') {
                await supabase.from('leads').update({ requirements_doc: cmd.payload }).eq('id', projectId);
                flags.shouldReload = true;
            }

            if (cmd.type === 'schedule_briefing') {
                await registerMorningCron(projectId, cmd.payload.timezone_offset || 0);
            }

            if (cmd.type === 'complete_task') {
                const { data: active } = await supabase.from('thrusts').select('id').eq('lead_id', projectId).eq('status', 'active').single();
                if (active) {
                    await supabase.from('thrust_tasks').update({ status: 'done' }).eq('thrust_id', active.id).ilike('title', `%${cmd.payload}%`);
                    flags.shouldReload = true;
                    
                    if (FRONT_URL) {
                        fetch(`${FRONT_URL}/internal/notify`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user_id: userId, type: 'toast', message: `βœ… AI Verified Task: ${cmd.payload}` }) }).catch(() => {});
                    }
                }
            }

            if (cmd.type === 'thrust_complete') {
                const { data: active } = await supabase.from('thrusts').select('id').eq('lead_id', projectId).eq('status', 'active').single();
                if (active) {
                    await supabase.from('thrusts').update({ status: 'completed' }).eq('id', active.id);
                    await supabase.from('thrust_tasks').delete().eq('thrust_id', active.id);
                    flags.thrustComplete = true;
                }
            }

            if (cmd.type === 'freeze_project') {
                const isFrozen = cmd.payload === 'true';
                await StateManager.setFrozen(projectId, isFrozen);
            }

            if (cmd.type === 'notification' && FRONT_URL) {
                fetch(`${FRONT_URL}/internal/notify`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user_id: userId, type: 'toast', message: cmd.payload }) }).catch(() => {});
            }

            // NEW: Forward AI MCP queries to the frontend gateway so it can push via websocket
            if (cmd.type === 'mcp_query' && FRONT_URL) {
                fetch(`${FRONT_URL}/internal/mcp_query`, { 
                    method: 'POST', 
                    headers: { 'Content-Type': 'application/json' }, 
                    body: JSON.stringify({ user_id: userId, lead_id: projectId, payload: cmd.payload }) 
                }).catch(() => {});
            }

        } catch (e) {}
    }
    return flags;
}

app.post('/init-project', async (req, res) => {
    const { userId, name, description, localPath, timezoneOffset = 0 } = req.body;
    const { data: lead } = await supabase.from('leads').insert({ user_id: userId, name, description, local_path: localPath, status: 'active', requirements_doc: "Init..." }).select().single();
    res.json({ success: true, leadId: lead.id });

    setImmediate(async () => {
        try {
            const initInput = `PROJECT: ${name}\nDESC: ${description}\nUSER TIMEZONE OFFSET: ${timezoneOffset}\nTask: Init PRD, First Thrust, Schedule Morning Briefing.`;
           // const aiResult = await callAI([], initInput, {},[], prompts.init_system_prompt, "", SMART_MODEL_ID);
            const aiResult = await callAIWithResearch([], initInput, {},[], prompts.init_system_prompt, "", SMART_MODEL_ID);
            
            aiResult.text += `\n<notification>Project '${name}' initialized successfully!</notification>`;
            await StateManager.addHistory(lead.id, 'user', initInput);
            await StateManager.addHistory(lead.id, 'model', aiResult.text);
            const cmds = extractCommands(aiResult.text);
            await executeCommands(userId, lead.id, cmds);
        } catch (err) {}
    });
});

app.post('/process', async (req, res) => {
    const { userId, projectId, prompt, context, images, task_type = 'chat' } = req.body;
    if (task_type === 'chat') await StateManager.setFrozen(projectId, false);

    let selectedModel = (task_type === 'log_ingestion') ? FAST_MODEL_ID : MIDDLE_MODEL_ID; // SMART_MODEL_ID;
    let sysPrompt = (task_type === 'log_ingestion') ? prompts.log_analyst_prompt : prompts.director_system_prompt;

    try {
        const { data: lead } = await supabase.from('leads').select('requirements_doc').eq('id', projectId).single();
        const { data: activeThrust } = await supabase.from('thrusts').select('title, tasks:thrust_tasks(title, status)').eq('lead_id', projectId).eq('status', 'active').order('created_at', { ascending: false }).limit(1).single();
        const { data: timeline } = await supabase.from('timeline_events').select('title, type, description, created_at').eq('lead_id', projectId).order('created_at', { ascending: false }).limit(10); 

        const projectContext = `[PRD]: ${lead?.requirements_doc?.substring(0, 3000)}...\n[CURRENT THRUST]: ${activeThrust ? JSON.stringify(activeThrust) : "None"}\n[RECENT TIMELINE]: ${JSON.stringify(timeline ||[])}`;
        const history = await StateManager.getHistory(projectId);
        
        // let aiResult = await callAI(history, prompt, context, images, sysPrompt, projectContext, selectedModel);
       let aiResult = await callAIWithResearch(history, prompt, context, images, sysPrompt, projectContext, selectedModel);
       
        let cmds = extractCommands(aiResult.text);
        let flags = await executeCommands(userId, projectId, cmds);

        if (flags.thrustComplete && task_type === 'log_ingestion') {
            const escalationPrompt = "The previous thrust is complete based on logs. Generate the next Thrust immediately to keep momentum.";
            const smartResult = await callAI(history, escalationPrompt, context,[], prompts.director_system_prompt, projectContext, MIDDLE_MODEL_ID); // SMART_MODEL_ID);
            aiResult.text += `\n\n[DIRECTOR INTERVENTION]:\n${smartResult.text}`;
            const smartCmds = extractCommands(smartResult.text);
            await executeCommands(userId, projectId, smartCmds);
            
            await StateManager.addHistory(projectId, 'model', aiResult.text);
        } else {
            await StateManager.addHistory(projectId, 'model', aiResult.text);
        }

        const cleanText = aiResult.text.replace(/<[^>]+>[\s\S]*?<\/[^>]+>/g, '').trim();
      
      const isFrozen = await StateManager.isFrozen(projectId);
        if (isFrozen) {
          await StateManager.setFrozen(projectId, true);
        };
      
      res.json({ text: cleanText, should_reload: flags.shouldReload });
    } catch (e) { res.status(500).json({ error: "Processing Error" }); }
});

app.post('/automated-briefing', async (req, res) => {
    const { projectId } = req.body;

    try {
        const isFrozen = await StateManager.isFrozen(projectId);
        const { data: lastThrust } = await supabase.from('thrusts').select('created_at').eq('lead_id', projectId).order('created_at', { ascending: false }).limit(1).single();

        const lastThrustDate = lastThrust ? new Date(lastThrust.created_at).getDate() : 0;
        const today = new Date();
        const todayDate = today.getDate();

        if (isFrozen) return res.json({ status: "skipped_frozen" });
        if (lastThrustDate === todayDate) return res.json({ status: "skipped_exists" });

        const currentDateString = today.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
        
        const prompt = `[SYSTEM TIME: ${currentDateString} at 5:00 AM]\nIt is morning. Generate today's Morning Briefing (New Thrust). Look at the RECENT TIMELINE to see what was accomplished yesterday. Adopt a highly conversational, proactive tone in the markdown (e.g., 'Morning! You finished X yesterday. Today, the priority is Y.'). If the project has been idle for days, use <freeze_project>true</freeze_project>.`;
        
        const { data: lead } = await supabase.from('leads').select('*').eq('id', projectId).single();
        const { data: timeline } = await supabase.from('timeline_events').select('*').eq('lead_id', projectId).order('created_at', { ascending: false }).limit(5);

        const projectContext = `[PRD]: ${lead.requirements_doc}\n[RECENT TIMELINE]: ${JSON.stringify(timeline)}`;
        const history = await StateManager.getHistory(projectId);
        
      //  const aiResult = await callAI(history, prompt, {}, [], prompts.director_system_prompt, projectContext, SMART_MODEL_ID);
        const aiResult = await callAIWithResearch(history, prompt, {}, [], prompts.director_system_prompt, projectContext, SMART_MODEL_ID);
      
        await StateManager.addHistory(projectId, 'model', aiResult.text);
        const cmds = extractCommands(aiResult.text);
        const flags = await executeCommands(lead.user_id, projectId, cmds);

        // πŸ‘‰ EMAIL IS DISPATCHED ONLY HERE
        if (flags.newThrustId) {
            await dispatchEmail(lead.user_id, projectId, lead.name, flags.newThrustId, flags.newThrustMarkdown);
        }

        await StateManager.setFrozen(projectId, true); 
        res.json({ success: true });

    } catch (e) { res.status(500).json({ error: e.message }); }
});

app.get('/', async (req, res) => res.status(200).json({ status: "Alive" }));
app.listen(PORT, () => console.log(`βœ… Core Online: ${PORT}`));