Spaces:
Running
Running
Update app.js
Browse files
app.js
CHANGED
|
@@ -54,6 +54,11 @@ const StateManager = {
|
|
| 54 |
setFrozen: async (projectId, status) => {
|
| 55 |
if (activeProjects.has(projectId)) activeProjects.get(projectId).isFrozen = status;
|
| 56 |
await supabase.from('leads').update({ is_frozen: status }).eq('id', projectId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
}
|
| 58 |
};
|
| 59 |
|
|
@@ -95,8 +100,6 @@ function extractCommands(text) {
|
|
| 95 |
parse(/<schedule_briefing>([\s\S]*?)<\/schedule_briefing>/gi, 'schedule_briefing');
|
| 96 |
parse(/<freeze_project>([\s\S]*?)<\/freeze_project>/gi, 'freeze_project', false);
|
| 97 |
parse(/<thrust_complete>([\s\S]*?)<\/thrust_complete>/gi, 'thrust_complete', false);
|
| 98 |
-
|
| 99 |
-
// NEW: Parse AI checking off tasks
|
| 100 |
parse(/<complete_task>([\s\S]*?)<\/complete_task>/gi, 'complete_task', false);
|
| 101 |
|
| 102 |
return commands;
|
|
@@ -142,7 +145,6 @@ async function executeCommands(userId, projectId, commands) {
|
|
| 142 |
await registerMorningCron(projectId, cmd.payload.timezone_offset || 0);
|
| 143 |
}
|
| 144 |
|
| 145 |
-
// NEW: AI checks off a specific task
|
| 146 |
if (cmd.type === 'complete_task') {
|
| 147 |
const { data: active } = await supabase.from('thrusts').select('id').eq('lead_id', projectId).eq('status', 'active').single();
|
| 148 |
if (active) {
|
|
@@ -151,7 +153,6 @@ async function executeCommands(userId, projectId, commands) {
|
|
| 151 |
}
|
| 152 |
}
|
| 153 |
|
| 154 |
-
// NEW: AI marks thrust as complete, we delete all its tasks
|
| 155 |
if (cmd.type === 'thrust_complete') {
|
| 156 |
const { data: active } = await supabase.from('thrusts').select('id').eq('lead_id', projectId).eq('status', 'active').single();
|
| 157 |
if (active) {
|
|
@@ -161,6 +162,11 @@ async function executeCommands(userId, projectId, commands) {
|
|
| 161 |
}
|
| 162 |
}
|
| 163 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
if (cmd.type === 'notification' && FRONT_URL) {
|
| 165 |
fetch(`${FRONT_URL}/internal/notify`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user_id: userId, type: 'toast', message: cmd.payload }) }).catch(() => {});
|
| 166 |
}
|
|
@@ -207,7 +213,6 @@ app.post('/process', async (req, res) => {
|
|
| 207 |
let cmds = extractCommands(aiResult.text);
|
| 208 |
let flags = await executeCommands(userId, projectId, cmds);
|
| 209 |
|
| 210 |
-
// Escalation: If fast model marks thrust complete, ask smart model for next thrust immediately
|
| 211 |
if (flags.thrustComplete && task_type === 'log_ingestion') {
|
| 212 |
const escalationPrompt = "The previous thrust is complete based on logs. Generate the next Thrust immediately to keep momentum.";
|
| 213 |
const smartResult = await callAI(history, escalationPrompt, context, prompts.director_system_prompt, projectContext, SMART_MODEL_ID);
|
|
@@ -224,5 +229,40 @@ app.post('/process', async (req, res) => {
|
|
| 224 |
} catch (e) { res.status(500).json({ error: "Processing Error" }); }
|
| 225 |
});
|
| 226 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
app.get('/', async (req, res) => res.status(200).json({ status: "Alive" }));
|
| 228 |
app.listen(PORT, () => console.log(`✅ Core Online: ${PORT}`));
|
|
|
|
| 54 |
setFrozen: async (projectId, status) => {
|
| 55 |
if (activeProjects.has(projectId)) activeProjects.get(projectId).isFrozen = status;
|
| 56 |
await supabase.from('leads').update({ is_frozen: status }).eq('id', projectId);
|
| 57 |
+
},
|
| 58 |
+
isFrozen: async (projectId) => {
|
| 59 |
+
if (activeProjects.has(projectId)) return activeProjects.get(projectId).isFrozen;
|
| 60 |
+
const { data } = await supabase.from('leads').select('is_frozen').eq('id', projectId).single();
|
| 61 |
+
return data?.is_frozen || false;
|
| 62 |
}
|
| 63 |
};
|
| 64 |
|
|
|
|
| 100 |
parse(/<schedule_briefing>([\s\S]*?)<\/schedule_briefing>/gi, 'schedule_briefing');
|
| 101 |
parse(/<freeze_project>([\s\S]*?)<\/freeze_project>/gi, 'freeze_project', false);
|
| 102 |
parse(/<thrust_complete>([\s\S]*?)<\/thrust_complete>/gi, 'thrust_complete', false);
|
|
|
|
|
|
|
| 103 |
parse(/<complete_task>([\s\S]*?)<\/complete_task>/gi, 'complete_task', false);
|
| 104 |
|
| 105 |
return commands;
|
|
|
|
| 145 |
await registerMorningCron(projectId, cmd.payload.timezone_offset || 0);
|
| 146 |
}
|
| 147 |
|
|
|
|
| 148 |
if (cmd.type === 'complete_task') {
|
| 149 |
const { data: active } = await supabase.from('thrusts').select('id').eq('lead_id', projectId).eq('status', 'active').single();
|
| 150 |
if (active) {
|
|
|
|
| 153 |
}
|
| 154 |
}
|
| 155 |
|
|
|
|
| 156 |
if (cmd.type === 'thrust_complete') {
|
| 157 |
const { data: active } = await supabase.from('thrusts').select('id').eq('lead_id', projectId).eq('status', 'active').single();
|
| 158 |
if (active) {
|
|
|
|
| 162 |
}
|
| 163 |
}
|
| 164 |
|
| 165 |
+
if (cmd.type === 'freeze_project') {
|
| 166 |
+
const isFrozen = cmd.payload === 'true';
|
| 167 |
+
await StateManager.setFrozen(projectId, isFrozen);
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
if (cmd.type === 'notification' && FRONT_URL) {
|
| 171 |
fetch(`${FRONT_URL}/internal/notify`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user_id: userId, type: 'toast', message: cmd.payload }) }).catch(() => {});
|
| 172 |
}
|
|
|
|
| 213 |
let cmds = extractCommands(aiResult.text);
|
| 214 |
let flags = await executeCommands(userId, projectId, cmds);
|
| 215 |
|
|
|
|
| 216 |
if (flags.thrustComplete && task_type === 'log_ingestion') {
|
| 217 |
const escalationPrompt = "The previous thrust is complete based on logs. Generate the next Thrust immediately to keep momentum.";
|
| 218 |
const smartResult = await callAI(history, escalationPrompt, context, prompts.director_system_prompt, projectContext, SMART_MODEL_ID);
|
|
|
|
| 229 |
} catch (e) { res.status(500).json({ error: "Processing Error" }); }
|
| 230 |
});
|
| 231 |
|
| 232 |
+
// --- AUTOMATED MORNING BRIEFING ---
|
| 233 |
+
app.post('/automated-briefing', async (req, res) => {
|
| 234 |
+
const { projectId } = req.body;
|
| 235 |
+
|
| 236 |
+
try {
|
| 237 |
+
const isFrozen = await StateManager.isFrozen(projectId);
|
| 238 |
+
const { data: lastThrust } = await supabase.from('thrusts').select('created_at').eq('lead_id', projectId).order('created_at', { ascending: false }).limit(1).single();
|
| 239 |
+
|
| 240 |
+
const lastThrustDate = lastThrust ? new Date(lastThrust.created_at).getDate() : 0;
|
| 241 |
+
const todayDate = new Date().getDate();
|
| 242 |
+
|
| 243 |
+
if (isFrozen) return res.json({ status: "skipped_frozen" });
|
| 244 |
+
if (lastThrustDate === todayDate) return res.json({ status: "skipped_exists" });
|
| 245 |
+
|
| 246 |
+
// TONE DIRECTIVE INJECTED INTO PROMPT
|
| 247 |
+
const prompt = "It 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>.";
|
| 248 |
+
|
| 249 |
+
const { data: lead } = await supabase.from('leads').select('*').eq('id', projectId).single();
|
| 250 |
+
const { data: timeline } = await supabase.from('timeline_events').select('*').eq('lead_id', projectId).order('created_at', { ascending: false }).limit(5);
|
| 251 |
+
|
| 252 |
+
const projectContext = `[PRD]: ${lead.requirements_doc}\n[RECENT TIMELINE]: ${JSON.stringify(timeline)}`;
|
| 253 |
+
const history = await StateManager.getHistory(projectId);
|
| 254 |
+
|
| 255 |
+
const aiResult = await callAI(history, prompt, {}, prompts.director_system_prompt, projectContext, SMART_MODEL_ID);
|
| 256 |
+
|
| 257 |
+
await StateManager.addHistory(projectId, 'model', aiResult.text);
|
| 258 |
+
const cmds = extractCommands(aiResult.text);
|
| 259 |
+
await executeCommands(lead.user_id, projectId, cmds);
|
| 260 |
+
|
| 261 |
+
await StateManager.setFrozen(projectId, true);
|
| 262 |
+
res.json({ success: true });
|
| 263 |
+
|
| 264 |
+
} catch (e) { res.status(500).json({ error: e.message }); }
|
| 265 |
+
});
|
| 266 |
+
|
| 267 |
app.get('/', async (req, res) => res.status(200).json({ status: "Alive" }));
|
| 268 |
app.listen(PORT, () => console.log(`✅ Core Online: ${PORT}`));
|