Spaces:
Sleeping
Sleeping
Update app.js
Browse files
app.js
CHANGED
|
@@ -19,6 +19,7 @@ const validateRequest = (req, res, next) => {
|
|
| 19 |
next();
|
| 20 |
};
|
| 21 |
|
|
|
|
| 22 |
function extractWorkerPrompt(text) {
|
| 23 |
const match = text.match(/WORKER_PROMPT:\s*(.*)/s);
|
| 24 |
return match ? match[1].trim() : null;
|
|
@@ -31,18 +32,23 @@ function formatContext({ hierarchyContext, scriptContext, logContext }) {
|
|
| 31 |
return out;
|
| 32 |
}
|
| 33 |
|
| 34 |
-
// Helper to check for Consultant requests
|
| 35 |
function extractPMQuestion(text) {
|
| 36 |
-
const match = text.match(/\[ASK_PM:\s*(.*?)\]/s);
|
| 37 |
return match ? match[1].trim() : null;
|
| 38 |
}
|
| 39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
/**
|
| 41 |
* 1. NEW PROJECT
|
| 42 |
*/
|
| 43 |
app.post('/new/project', validateRequest, async (req, res) => {
|
| 44 |
const { userId, projectId, description } = req.body;
|
| 45 |
-
console.log(`[${projectId}] Init: ${description}`);
|
| 46 |
|
| 47 |
try {
|
| 48 |
const pmHistory = [];
|
|
@@ -75,7 +81,9 @@ app.post('/new/project', validateRequest, async (req, res) => {
|
|
| 75 |
failureCount: 0
|
| 76 |
});
|
| 77 |
|
| 78 |
-
|
|
|
|
|
|
|
| 79 |
res.json({ success: true, message: "Project Initialized", gddPreview: gddResponse.substring(0, 200) });
|
| 80 |
|
| 81 |
} catch (err) {
|
|
@@ -85,7 +93,7 @@ app.post('/new/project', validateRequest, async (req, res) => {
|
|
| 85 |
});
|
| 86 |
|
| 87 |
/**
|
| 88 |
-
* 2. FEEDBACK LOOP
|
| 89 |
*/
|
| 90 |
app.post('/project/feedback', async (req, res) => {
|
| 91 |
const { projectId, prompt, hierarchyContext, scriptContext, logContext, taskComplete, images } = req.body;
|
|
@@ -122,13 +130,13 @@ app.post('/project/feedback', async (req, res) => {
|
|
| 122 |
failureCount: 0
|
| 123 |
});
|
| 124 |
|
| 125 |
-
StateManager.queueCommand(projectId, "
|
| 126 |
-
|
| 127 |
|
| 128 |
return res.json({ success: true, message: "Next Task Assigned" });
|
| 129 |
}
|
| 130 |
|
| 131 |
-
// B.
|
| 132 |
let isFailure = false;
|
| 133 |
if (logContext?.logs) {
|
| 134 |
const errs = ["Error", "Exception", "failed", "Stack Begin", "Infinite yield"];
|
|
@@ -138,7 +146,7 @@ app.post('/project/feedback', async (req, res) => {
|
|
| 138 |
}
|
| 139 |
}
|
| 140 |
|
| 141 |
-
//
|
| 142 |
if (project.failureCount > 3) {
|
| 143 |
console.log(`[${projectId}] 🚨 Escalating to PM...`);
|
| 144 |
const pmPrompt = sysPrompts.pm_guidance_prompt.replace('{{LOGS}}', logContext?.logs);
|
|
@@ -154,8 +162,7 @@ app.post('/project/feedback', async (req, res) => {
|
|
| 154 |
resetHistory.push({ role: 'model', parts: [{ text: workerResp }] });
|
| 155 |
|
| 156 |
await StateManager.updateProject(projectId, { workerHistory: resetHistory, failureCount: 0 });
|
| 157 |
-
|
| 158 |
-
StateManager.queueCommand(projectId, workerResp);
|
| 159 |
return res.json({ success: true, message: "Worker Terminated." });
|
| 160 |
} else {
|
| 161 |
const injection = `[PM GUIDANCE]: ${pmVerdict} \n\nApply this fix now.`;
|
|
@@ -165,63 +172,50 @@ app.post('/project/feedback', async (req, res) => {
|
|
| 165 |
project.workerHistory.push({ role: 'model', parts: [{ text: workerResp }] });
|
| 166 |
|
| 167 |
await StateManager.updateProject(projectId, { workerHistory: project.workerHistory, failureCount: 1 });
|
| 168 |
-
await
|
| 169 |
return res.json({ success: true, message: "PM Guidance Applied." });
|
| 170 |
}
|
| 171 |
}
|
| 172 |
|
| 173 |
-
//
|
| 174 |
try {
|
| 175 |
const fullInput = `USER: ${prompt || "Automatic Feedback"}` + formatContext({ hierarchyContext, scriptContext, logContext });
|
| 176 |
|
| 177 |
-
// 1. Get Initial Worker Response
|
| 178 |
let response = await AIEngine.callWorker(project.workerHistory, fullInput, images || []);
|
| 179 |
|
| 180 |
-
//
|
| 181 |
const pmQuestion = extractPMQuestion(response);
|
| 182 |
-
|
| 183 |
if (pmQuestion) {
|
| 184 |
console.log(`[${projectId}] 🙋 Worker asking PM: "${pmQuestion}"`);
|
| 185 |
-
|
| 186 |
-
// a. Ask PM
|
| 187 |
-
// We give the PM the worker's question AND a snippet of context so PM knows why
|
| 188 |
-
const pmConsultPrompt = `[WORKER CONSULTATION]: The Worker needs clarification:\n"${pmQuestion}"\n\nProvide a brief, technical answer to unblock them. Do not change the task scope unless necessary.`;
|
| 189 |
const pmAnswer = await AIEngine.callPM(project.pmHistory, pmConsultPrompt);
|
| 190 |
|
| 191 |
-
// Update PM history
|
| 192 |
project.pmHistory.push({ role: 'user', parts: [{ text: pmConsultPrompt }] });
|
| 193 |
project.pmHistory.push({ role: 'model', parts: [{ text: pmAnswer }] });
|
| 194 |
|
| 195 |
-
|
| 196 |
-
const injectionMsg = `[PM RESPONSE]: ${pmAnswer}\n\nProceed with this information.`;
|
| 197 |
|
| 198 |
-
//
|
| 199 |
project.workerHistory.push({ role: 'user', parts: [{ text: fullInput }] });
|
| 200 |
-
project.workerHistory.push({ role: 'model', parts: [{ text: response }] });
|
| 201 |
|
| 202 |
-
//
|
| 203 |
-
console.log(`[${projectId}] 🗣️ PM Answered. Resuming Worker...`);
|
| 204 |
response = await AIEngine.callWorker(project.workerHistory, injectionMsg, []);
|
| 205 |
-
|
| 206 |
-
// Push the answer transaction
|
| 207 |
project.workerHistory.push({ role: 'user', parts: [{ text: injectionMsg }] });
|
| 208 |
project.workerHistory.push({ role: 'model', parts: [{ text: response }] });
|
| 209 |
-
|
| 210 |
} else {
|
| 211 |
-
// Standard flow if no question
|
| 212 |
project.workerHistory.push({ role: 'user', parts: [{ text: fullInput }] });
|
| 213 |
project.workerHistory.push({ role: 'model', parts: [{ text: response }] });
|
| 214 |
}
|
| 215 |
|
| 216 |
-
// 3. Save & Queue Result
|
| 217 |
await StateManager.updateProject(projectId, {
|
| 218 |
workerHistory: project.workerHistory,
|
| 219 |
-
pmHistory: project.pmHistory,
|
| 220 |
failureCount: project.failureCount
|
| 221 |
});
|
| 222 |
|
| 223 |
-
//
|
| 224 |
-
await
|
| 225 |
res.json({ success: true });
|
| 226 |
|
| 227 |
} catch (err) {
|
|
@@ -231,14 +225,12 @@ app.post('/project/feedback', async (req, res) => {
|
|
| 231 |
});
|
| 232 |
|
| 233 |
/**
|
| 234 |
-
*
|
| 235 |
*/
|
| 236 |
app.post('/human/override', validateRequest, async (req, res) => {
|
| 237 |
const { projectId, instruction, pruneHistory } = req.body;
|
| 238 |
const project = await StateManager.getProject(projectId);
|
| 239 |
|
| 240 |
-
if (!project) return res.status(404).json({ error: "Project not found" });
|
| 241 |
-
|
| 242 |
const overrideMsg = `[SYSTEM OVERRIDE]: ${instruction}`;
|
| 243 |
|
| 244 |
if (pruneHistory && project.workerHistory.length >= 2) {
|
|
@@ -251,30 +243,12 @@ app.post('/human/override', validateRequest, async (req, res) => {
|
|
| 251 |
project.workerHistory.push({ role: 'model', parts: [{ text: response }] });
|
| 252 |
|
| 253 |
await StateManager.updateProject(projectId, { workerHistory: project.workerHistory });
|
| 254 |
-
await
|
| 255 |
-
res.json({ success: true
|
| 256 |
});
|
| 257 |
|
| 258 |
/**
|
| 259 |
-
*
|
| 260 |
-
*/
|
| 261 |
-
app.post('/worker/kill', validateRequest, async (req, res) => {
|
| 262 |
-
const { projectId } = req.body;
|
| 263 |
-
const rebootPrompt = "[SYSTEM]: Worker process restarted. Attempt the task again from scratch.";
|
| 264 |
-
const response = await AIEngine.callWorker([{ role: 'user', parts: [{ text: rebootPrompt }] }], rebootPrompt, []);
|
| 265 |
-
|
| 266 |
-
await StateManager.updateProject(projectId, {
|
| 267 |
-
workerHistory: [{ role: 'user', parts: [{ text: rebootPrompt }] }],
|
| 268 |
-
workerHistory: [{ role: 'model', parts: [{ text: response }] }],
|
| 269 |
-
failureCount: 0
|
| 270 |
-
});
|
| 271 |
-
|
| 272 |
-
StateManager.queueCommand(projectId, response);
|
| 273 |
-
res.json({ success: true, message: "Worker Killed & Restarted" });
|
| 274 |
-
});
|
| 275 |
-
|
| 276 |
-
/**
|
| 277 |
-
* 5. PING
|
| 278 |
*/
|
| 279 |
app.post('/project/ping', async (req, res) => {
|
| 280 |
const { projectId } = req.body;
|
|
@@ -284,6 +258,7 @@ app.post('/project/ping', async (req, res) => {
|
|
| 284 |
if (command.payload === "CLEAR_CONSOLE") {
|
| 285 |
res.json({ action: "CLEAR_LOGS" });
|
| 286 |
} else {
|
|
|
|
| 287 |
res.json({
|
| 288 |
action: command.type,
|
| 289 |
target: command.payload,
|
|
@@ -295,6 +270,32 @@ app.post('/project/ping', async (req, res) => {
|
|
| 295 |
}
|
| 296 |
});
|
| 297 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
app.listen(PORT, () => {
|
| 299 |
console.log(`AI Backend Running on ${PORT}`);
|
| 300 |
});
|
|
|
|
| 19 |
next();
|
| 20 |
};
|
| 21 |
|
| 22 |
+
// --- HELPERS ---
|
| 23 |
function extractWorkerPrompt(text) {
|
| 24 |
const match = text.match(/WORKER_PROMPT:\s*(.*)/s);
|
| 25 |
return match ? match[1].trim() : null;
|
|
|
|
| 32 |
return out;
|
| 33 |
}
|
| 34 |
|
|
|
|
| 35 |
function extractPMQuestion(text) {
|
| 36 |
+
const match = text.match(/\[ASK_PM:\s*(.*?)\]/s);
|
| 37 |
return match ? match[1].trim() : null;
|
| 38 |
}
|
| 39 |
|
| 40 |
+
function extractImagePrompt(text) {
|
| 41 |
+
const match = text.match(/\[GENERATE_IMAGE:\s*(.*?)\]/s);
|
| 42 |
+
return match ? match[1].trim() : null;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
// --- ENDPOINTS ---
|
| 46 |
+
|
| 47 |
/**
|
| 48 |
* 1. NEW PROJECT
|
| 49 |
*/
|
| 50 |
app.post('/new/project', validateRequest, async (req, res) => {
|
| 51 |
const { userId, projectId, description } = req.body;
|
|
|
|
| 52 |
|
| 53 |
try {
|
| 54 |
const pmHistory = [];
|
|
|
|
| 81 |
failureCount: 0
|
| 82 |
});
|
| 83 |
|
| 84 |
+
// Queue Initial Response
|
| 85 |
+
await processAndQueueResponse(projectId, workerResponse);
|
| 86 |
+
|
| 87 |
res.json({ success: true, message: "Project Initialized", gddPreview: gddResponse.substring(0, 200) });
|
| 88 |
|
| 89 |
} catch (err) {
|
|
|
|
| 93 |
});
|
| 94 |
|
| 95 |
/**
|
| 96 |
+
* 2. FEEDBACK LOOP
|
| 97 |
*/
|
| 98 |
app.post('/project/feedback', async (req, res) => {
|
| 99 |
const { projectId, prompt, hierarchyContext, scriptContext, logContext, taskComplete, images } = req.body;
|
|
|
|
| 130 |
failureCount: 0
|
| 131 |
});
|
| 132 |
|
| 133 |
+
StateManager.queueCommand(projectId, { type: "EXECUTE", payload: "warn('Starting Next Task')" }); // Reset console
|
| 134 |
+
await processAndQueueResponse(projectId, workerResponse);
|
| 135 |
|
| 136 |
return res.json({ success: true, message: "Next Task Assigned" });
|
| 137 |
}
|
| 138 |
|
| 139 |
+
// B. FAILURE DETECTION
|
| 140 |
let isFailure = false;
|
| 141 |
if (logContext?.logs) {
|
| 142 |
const errs = ["Error", "Exception", "failed", "Stack Begin", "Infinite yield"];
|
|
|
|
| 146 |
}
|
| 147 |
}
|
| 148 |
|
| 149 |
+
// C. ESCALATION
|
| 150 |
if (project.failureCount > 3) {
|
| 151 |
console.log(`[${projectId}] 🚨 Escalating to PM...`);
|
| 152 |
const pmPrompt = sysPrompts.pm_guidance_prompt.replace('{{LOGS}}', logContext?.logs);
|
|
|
|
| 162 |
resetHistory.push({ role: 'model', parts: [{ text: workerResp }] });
|
| 163 |
|
| 164 |
await StateManager.updateProject(projectId, { workerHistory: resetHistory, failureCount: 0 });
|
| 165 |
+
await processAndQueueResponse(projectId, workerResp);
|
|
|
|
| 166 |
return res.json({ success: true, message: "Worker Terminated." });
|
| 167 |
} else {
|
| 168 |
const injection = `[PM GUIDANCE]: ${pmVerdict} \n\nApply this fix now.`;
|
|
|
|
| 172 |
project.workerHistory.push({ role: 'model', parts: [{ text: workerResp }] });
|
| 173 |
|
| 174 |
await StateManager.updateProject(projectId, { workerHistory: project.workerHistory, failureCount: 1 });
|
| 175 |
+
await processAndQueueResponse(projectId, workerResp);
|
| 176 |
return res.json({ success: true, message: "PM Guidance Applied." });
|
| 177 |
}
|
| 178 |
}
|
| 179 |
|
| 180 |
+
// D. STANDARD WORKER FLOW
|
| 181 |
try {
|
| 182 |
const fullInput = `USER: ${prompt || "Automatic Feedback"}` + formatContext({ hierarchyContext, scriptContext, logContext });
|
| 183 |
|
|
|
|
| 184 |
let response = await AIEngine.callWorker(project.workerHistory, fullInput, images || []);
|
| 185 |
|
| 186 |
+
// CHECK: PM CONSULTATION
|
| 187 |
const pmQuestion = extractPMQuestion(response);
|
|
|
|
| 188 |
if (pmQuestion) {
|
| 189 |
console.log(`[${projectId}] 🙋 Worker asking PM: "${pmQuestion}"`);
|
| 190 |
+
const pmConsultPrompt = `[WORKER CONSULTATION]: The Worker asks: "${pmQuestion}"\nProvide a technical answer.`;
|
|
|
|
|
|
|
|
|
|
| 191 |
const pmAnswer = await AIEngine.callPM(project.pmHistory, pmConsultPrompt);
|
| 192 |
|
|
|
|
| 193 |
project.pmHistory.push({ role: 'user', parts: [{ text: pmConsultPrompt }] });
|
| 194 |
project.pmHistory.push({ role: 'model', parts: [{ text: pmAnswer }] });
|
| 195 |
|
| 196 |
+
const injectionMsg = `[PM RESPONSE]: ${pmAnswer}`;
|
|
|
|
| 197 |
|
| 198 |
+
// Push question-answer to history
|
| 199 |
project.workerHistory.push({ role: 'user', parts: [{ text: fullInput }] });
|
| 200 |
+
project.workerHistory.push({ role: 'model', parts: [{ text: response }] });
|
| 201 |
|
| 202 |
+
// Resume Worker
|
|
|
|
| 203 |
response = await AIEngine.callWorker(project.workerHistory, injectionMsg, []);
|
|
|
|
|
|
|
| 204 |
project.workerHistory.push({ role: 'user', parts: [{ text: injectionMsg }] });
|
| 205 |
project.workerHistory.push({ role: 'model', parts: [{ text: response }] });
|
|
|
|
| 206 |
} else {
|
|
|
|
| 207 |
project.workerHistory.push({ role: 'user', parts: [{ text: fullInput }] });
|
| 208 |
project.workerHistory.push({ role: 'model', parts: [{ text: response }] });
|
| 209 |
}
|
| 210 |
|
|
|
|
| 211 |
await StateManager.updateProject(projectId, {
|
| 212 |
workerHistory: project.workerHistory,
|
| 213 |
+
pmHistory: project.pmHistory,
|
| 214 |
failureCount: project.failureCount
|
| 215 |
});
|
| 216 |
|
| 217 |
+
// Process response for Code OR Images
|
| 218 |
+
await processAndQueueResponse(projectId, response);
|
| 219 |
res.json({ success: true });
|
| 220 |
|
| 221 |
} catch (err) {
|
|
|
|
| 225 |
});
|
| 226 |
|
| 227 |
/**
|
| 228 |
+
* HUMAN OVERRIDE
|
| 229 |
*/
|
| 230 |
app.post('/human/override', validateRequest, async (req, res) => {
|
| 231 |
const { projectId, instruction, pruneHistory } = req.body;
|
| 232 |
const project = await StateManager.getProject(projectId);
|
| 233 |
|
|
|
|
|
|
|
| 234 |
const overrideMsg = `[SYSTEM OVERRIDE]: ${instruction}`;
|
| 235 |
|
| 236 |
if (pruneHistory && project.workerHistory.length >= 2) {
|
|
|
|
| 243 |
project.workerHistory.push({ role: 'model', parts: [{ text: response }] });
|
| 244 |
|
| 245 |
await StateManager.updateProject(projectId, { workerHistory: project.workerHistory });
|
| 246 |
+
await processAndQueueResponse(projectId, response);
|
| 247 |
+
res.json({ success: true });
|
| 248 |
});
|
| 249 |
|
| 250 |
/**
|
| 251 |
+
* PING (Plugin Polling)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
*/
|
| 253 |
app.post('/project/ping', async (req, res) => {
|
| 254 |
const { projectId } = req.body;
|
|
|
|
| 258 |
if (command.payload === "CLEAR_CONSOLE") {
|
| 259 |
res.json({ action: "CLEAR_LOGS" });
|
| 260 |
} else {
|
| 261 |
+
// Can be: EXECUTE, READ_SCRIPT, READ_HIERARCHY, CREATE_ASSET
|
| 262 |
res.json({
|
| 263 |
action: command.type,
|
| 264 |
target: command.payload,
|
|
|
|
| 270 |
}
|
| 271 |
});
|
| 272 |
|
| 273 |
+
|
| 274 |
+
// --- CORE LOGIC: PROCESS RESPONSE & HANDLE IMAGES ---
|
| 275 |
+
async function processAndQueueResponse(projectId, rawResponse) {
|
| 276 |
+
// 1. Check for Image Generation Tag
|
| 277 |
+
const imgPrompt = extractImagePrompt(rawResponse);
|
| 278 |
+
if (imgPrompt) {
|
| 279 |
+
console.log(`[${projectId}] 🎨 Generating Image for: ${imgPrompt}`);
|
| 280 |
+
const base64Image = await AIEngine.generateImage(imgPrompt);
|
| 281 |
+
|
| 282 |
+
if (base64Image) {
|
| 283 |
+
// Queue the creation command for the plugin
|
| 284 |
+
await StateManager.queueCommand(projectId, {
|
| 285 |
+
type: "CREATE_ASSET",
|
| 286 |
+
payload: base64Image // Plugin receives Base64
|
| 287 |
+
});
|
| 288 |
+
console.log(`[${projectId}] Image Asset Queued.`);
|
| 289 |
+
} else {
|
| 290 |
+
console.error(`[${projectId}] Image Generation Failed.`);
|
| 291 |
+
}
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
// 2. Queue standard commands (Code, Reads, etc.)
|
| 295 |
+
// We pass the RAW response to StateManager to parse code blocks
|
| 296 |
+
await StateManager.queueCommand(projectId, rawResponse);
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
app.listen(PORT, () => {
|
| 300 |
console.log(`AI Backend Running on ${PORT}`);
|
| 301 |
});
|