Spaces:
Sleeping
Sleeping
Update app.js
Browse files
app.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
-
|
| 2 |
// App.js
|
| 3 |
|
| 4 |
import express from 'express';
|
|
@@ -82,9 +81,9 @@ async function deductUserCredits(userId, amount, type = 'basic') {
|
|
| 82 |
const current = current_credits || 0;
|
| 83 |
return Math.max(0, current - amount);
|
| 84 |
});
|
| 85 |
-
console.log(` Deducted ${amount} ${type} credits from User ${userId}`);
|
| 86 |
} catch (err) {
|
| 87 |
-
console.error(` Failed to deduct ${amount} ${type} from ${userId}:`, err);
|
| 88 |
}
|
| 89 |
}
|
| 90 |
|
|
@@ -101,33 +100,36 @@ const validateRequest = (req, res, next) => {
|
|
| 101 |
next();
|
| 102 |
};
|
| 103 |
|
| 104 |
-
// --- REGEX HELPERS ---
|
| 105 |
function extractWorkerPrompt(text) {
|
| 106 |
const match = text.match(/WORKER_PROMPT:\s*(.*)/s);
|
| 107 |
-
return match ? match.trim() : null;
|
| 108 |
}
|
| 109 |
|
| 110 |
function formatContext({ hierarchyContext, scriptContext, logContext }) {
|
| 111 |
let out = "";
|
| 112 |
-
if (scriptContext) out += `\n: ${scriptContext.targetName}\n: ${scriptContext.scriptSource}`;
|
| 113 |
-
if (logContext) out += `\n: ${logContext.logs}`;
|
| 114 |
-
if (hierarchyContext) out += `\n: ${hierarchyContext}`;
|
| 115 |
return out;
|
| 116 |
}
|
| 117 |
|
| 118 |
function extractPMQuestion(text) {
|
| 119 |
-
|
| 120 |
-
|
|
|
|
| 121 |
}
|
| 122 |
|
| 123 |
function extractImagePrompt(text) {
|
| 124 |
-
|
| 125 |
-
|
|
|
|
| 126 |
}
|
| 127 |
|
| 128 |
function extractRouteToPM(text) {
|
| 129 |
-
|
| 130 |
-
|
|
|
|
| 131 |
}
|
| 132 |
|
| 133 |
// --- ADMIN ENDPOINTS ---
|
|
@@ -148,7 +150,7 @@ app.post('/onboarding/analyze', validateRequest, async (req, res) => {
|
|
| 148 |
|
| 149 |
try {
|
| 150 |
await checkMinimumCredits(userId, 'basic');
|
| 151 |
-
console.log(` Analyzing idea...`);
|
| 152 |
|
| 153 |
const result = await AIEngine.generateEntryQuestions(description);
|
| 154 |
const usage = result.usage?.totalTokenCount || 0;
|
|
@@ -182,7 +184,7 @@ app.post('/onboarding/create', validateRequest, async (req, res) => {
|
|
| 182 |
const randomHex = (n = 6) => crypto.randomBytes(Math.ceil(n/2)).toString("hex").slice(0, n);
|
| 183 |
const projectId = `proj_${Date.now()}_${randomHex(7)}`;
|
| 184 |
|
| 185 |
-
console.log(` Grading Project ${projectId}...`);
|
| 186 |
|
| 187 |
const gradingResult = await AIEngine.gradeProject(description, answers);
|
| 188 |
basicTokens += (gradingResult.usage?.totalTokenCount || 0);
|
|
@@ -203,9 +205,9 @@ app.post('/onboarding/create', validateRequest, async (req, res) => {
|
|
| 203 |
thumbnail: thumbnailUrl,
|
| 204 |
createdAt: Date.now(),
|
| 205 |
status: "Idle",
|
| 206 |
-
workerHistory:[],
|
| 207 |
pmHistory: [],
|
| 208 |
-
commandQueue:[],
|
| 209 |
failureCount: 0
|
| 210 |
};
|
| 211 |
await StateManager.updateProject(projectId, memoryObject);
|
|
@@ -215,14 +217,14 @@ app.post('/onboarding/create', validateRequest, async (req, res) => {
|
|
| 215 |
await firestore.collection('projects').doc(projectId).set({
|
| 216 |
id: projectId,
|
| 217 |
userId: userId,
|
| 218 |
-
assets: thumbnailUrl ? :[],
|
| 219 |
createdAt: admin.firestore.FieldValue.serverTimestamp()
|
| 220 |
});
|
| 221 |
}
|
| 222 |
|
| 223 |
if (db && !isFailure) {
|
| 224 |
const updates = {};
|
| 225 |
-
updates = {
|
| 226 |
id: projectId,
|
| 227 |
userId,
|
| 228 |
title: gradingResult.title || "Untitled",
|
|
@@ -232,11 +234,11 @@ app.post('/onboarding/create', validateRequest, async (req, res) => {
|
|
| 232 |
createdAt: Date.now(),
|
| 233 |
status: "Idle"
|
| 234 |
};
|
| 235 |
-
if (thumbnailUrl) updates = { url: thumbnailUrl };
|
| 236 |
-
updates = {
|
| 237 |
workerHistory: [],
|
| 238 |
-
pmHistory:[],
|
| 239 |
-
commandQueue:[],
|
| 240 |
failureCount: 0
|
| 241 |
};
|
| 242 |
await db.ref().update(updates);
|
|
@@ -265,13 +267,13 @@ app.post('/onboarding/create', validateRequest, async (req, res) => {
|
|
| 265 |
// --- CORE ENDPOINTS ---
|
| 266 |
|
| 267 |
async function runBackgroundInitialization(projectId, userId, description) {
|
| 268 |
-
console.log(` Starting initialization for ${projectId}`);
|
| 269 |
|
| 270 |
let diamondUsage = 0;
|
| 271 |
let basicUsage = 0;
|
| 272 |
|
| 273 |
try {
|
| 274 |
-
const pmHistory =[];
|
| 275 |
|
| 276 |
// 1. Generate GDD (PM -> Diamond)
|
| 277 |
const gddPrompt = `Create a comprehensive GDD for: ${description}`;
|
|
@@ -280,8 +282,8 @@ async function runBackgroundInitialization(projectId, userId, description) {
|
|
| 280 |
diamondUsage += (gddResult.usage?.totalTokenCount || 0);
|
| 281 |
const gddText = gddResult.text;
|
| 282 |
|
| 283 |
-
pmHistory.push({ role: 'user', parts: });
|
| 284 |
-
pmHistory.push({ role: 'model', parts: });
|
| 285 |
|
| 286 |
// 2. Generate First Task (PM -> Diamond)
|
| 287 |
const taskPrompt = "Based on the GDD, generate the first technical milestone.\nOutput format:\nTASK_NAME: <Name>\nWORKER_PROMPT: <Specific, isolated instructions for the worker>";
|
|
@@ -290,21 +292,21 @@ async function runBackgroundInitialization(projectId, userId, description) {
|
|
| 290 |
diamondUsage += (taskResult.usage?.totalTokenCount || 0);
|
| 291 |
const taskText = taskResult.text;
|
| 292 |
|
| 293 |
-
pmHistory.push({ role: 'user', parts: });
|
| 294 |
-
pmHistory.push({ role: 'model', parts: });
|
| 295 |
|
| 296 |
// 3. Initialize Worker (Worker -> Basic)
|
| 297 |
const initialWorkerInstruction = extractWorkerPrompt(taskText) || `Initialize structure for: ${description}`;
|
| 298 |
-
const workerHistory =[];
|
| 299 |
const initialWorkerPrompt = `CONTEXT: New Project. \nINSTRUCTION: ${initialWorkerInstruction}`;
|
| 300 |
|
| 301 |
-
const workerResult = await AIEngine.callWorker(workerHistory, initialWorkerPrompt,[]);
|
| 302 |
|
| 303 |
basicUsage += (workerResult.usage?.totalTokenCount || 0);
|
| 304 |
const workerText = workerResult.text;
|
| 305 |
|
| 306 |
-
workerHistory.push({ role: 'user', parts: });
|
| 307 |
-
workerHistory.push({ role: 'model', parts: });
|
| 308 |
|
| 309 |
// 4. Update Memory
|
| 310 |
await StateManager.updateProject(projectId, {
|
|
@@ -328,10 +330,10 @@ async function runBackgroundInitialization(projectId, userId, description) {
|
|
| 328 |
if (diamondUsage > 0) await deductUserCredits(userId, diamondUsage, 'diamond');
|
| 329 |
if (basicUsage > 0) await deductUserCredits(userId, basicUsage, 'basic');
|
| 330 |
|
| 331 |
-
console.log(` Init complete. Diamond: ${diamondUsage}, Basic: ${basicUsage}`);
|
| 332 |
|
| 333 |
} catch (err) {
|
| 334 |
-
console.error(` Init Error for ${projectId}:`, err);
|
| 335 |
if(db) await db.ref(`projects/${projectId}/info/status`).set("error");
|
| 336 |
}
|
| 337 |
}
|
|
@@ -378,15 +380,15 @@ app.post('/project/feedback', async (req, res) => {
|
|
| 378 |
|
| 379 |
// SCENARIO 1: TASK COMPLETE (Worker asks PM for next job)
|
| 380 |
if (taskComplete) {
|
| 381 |
-
console.log(` ✅ TASK COMPLETE.`);
|
| 382 |
const summary = `Worker completed the previous task. Logs: ${logContext?.logs || "Clean"}. \nGenerate the NEXT task using 'WORKER_PROMPT:' format.`;
|
| 383 |
|
| 384 |
const pmResult = await AIEngine.callPM(project.pmHistory, summary);
|
| 385 |
diamondUsage += (pmResult.usage?.totalTokenCount || 0);
|
| 386 |
const pmText = pmResult.text;
|
| 387 |
|
| 388 |
-
project.pmHistory.push({ role: 'user', parts: });
|
| 389 |
-
project.pmHistory.push({ role: 'model', parts: });
|
| 390 |
|
| 391 |
const nextInstruction = extractWorkerPrompt(pmText);
|
| 392 |
if (!nextInstruction) {
|
|
@@ -397,12 +399,12 @@ app.post('/project/feedback', async (req, res) => {
|
|
| 397 |
}
|
| 398 |
|
| 399 |
const newPrompt = `New Objective: ${nextInstruction}`;
|
| 400 |
-
const workerResult = await AIEngine.callWorker(project.workerHistory, newPrompt,[]);
|
| 401 |
basicUsage += (workerResult.usage?.totalTokenCount || 0);
|
| 402 |
const workerText = workerResult.text;
|
| 403 |
|
| 404 |
-
project.workerHistory.push({ role: 'user', parts: });
|
| 405 |
-
project.workerHistory.push({ role: 'model', parts: });
|
| 406 |
|
| 407 |
await StateManager.updateProject(projectId, {
|
| 408 |
pmHistory: project.pmHistory,
|
|
@@ -428,17 +430,17 @@ app.post('/project/feedback', async (req, res) => {
|
|
| 428 |
diamondUsage += (pmResult.usage?.totalTokenCount || 0);
|
| 429 |
const pmVerdict = pmResult.text;
|
| 430 |
|
| 431 |
-
if (pmVerdict.includes("")) {
|
| 432 |
-
const fixInstruction = pmVerdict.replace("", "").trim();
|
| 433 |
-
const resetPrompt = `: Previous worker terminated. \nNew Objective: ${fixInstruction}`;
|
| 434 |
|
| 435 |
// Clear History on terminate
|
| 436 |
-
const resetHistory =[];
|
| 437 |
-
const workerResult = await AIEngine.callWorker(resetHistory, resetPrompt,[]);
|
| 438 |
basicUsage += (workerResult.usage?.totalTokenCount || 0);
|
| 439 |
|
| 440 |
-
resetHistory.push({ role: 'user', parts: });
|
| 441 |
-
resetHistory.push({ role: 'model', parts: });
|
| 442 |
|
| 443 |
await StateManager.updateProject(projectId, { workerHistory: resetHistory, failureCount: 0 });
|
| 444 |
StateManager.queueCommand(projectId, { type: "EXECUTE", payload: "print('SYSTEM: Worker Reset')" });
|
|
@@ -448,12 +450,12 @@ app.post('/project/feedback', async (req, res) => {
|
|
| 448 |
if (basicUsage > 0) await deductUserCredits(userId, basicUsage, 'basic');
|
| 449 |
return res.json({ success: true, message: "Worker Terminated." });
|
| 450 |
} else {
|
| 451 |
-
const injection = `: ${pmVerdict} \n\nApply this fix now.`;
|
| 452 |
-
const workerResult = await AIEngine.callWorker(project.workerHistory, injection,[]);
|
| 453 |
basicUsage += (workerResult.usage?.totalTokenCount || 0);
|
| 454 |
|
| 455 |
-
project.workerHistory.push({ role: 'user', parts: });
|
| 456 |
-
project.workerHistory.push({ role: 'model', parts: });
|
| 457 |
|
| 458 |
await StateManager.updateProject(projectId, { workerHistory: project.workerHistory, failureCount: 1 });
|
| 459 |
await processAndQueueResponse(projectId, workerResult.text, userId);
|
|
@@ -467,7 +469,7 @@ app.post('/project/feedback', async (req, res) => {
|
|
| 467 |
// SCENARIO 3: STANDARD INTERACTION (Worker is the Frontline)
|
| 468 |
const fullInput = `USER: ${prompt || "Automatic Feedback"}` + formatContext({ hierarchyContext, scriptContext, logContext });
|
| 469 |
|
| 470 |
-
let workerResult = await AIEngine.callWorker(project.workerHistory, fullInput, images ||[]);
|
| 471 |
basicUsage += (workerResult.usage?.totalTokenCount || 0);
|
| 472 |
let responseText = workerResult.text;
|
| 473 |
|
|
@@ -478,13 +480,13 @@ app.post('/project/feedback', async (req, res) => {
|
|
| 478 |
console.log(` 🔀 Task routed to PM: ${routeTask}`);
|
| 479 |
|
| 480 |
// PM generates Backend Code (Diamond)
|
| 481 |
-
const pmPrompt = `: ${routeTask}\nWrite the core server/backend logic yourself. Then use WORKER_PROMPT: <task> to delegate the UI/Client aspects to the Worker.`;
|
| 482 |
const pmResult = await AIEngine.callPM(project.pmHistory, pmPrompt);
|
| 483 |
diamondUsage += (pmResult.usage?.totalTokenCount || 0);
|
| 484 |
const pmText = pmResult.text;
|
| 485 |
|
| 486 |
-
project.pmHistory.push({ role: 'user', parts: });
|
| 487 |
-
project.pmHistory.push({ role: 'model', parts: });
|
| 488 |
|
| 489 |
// Queue PM's Backend Code to Studio
|
| 490 |
await processAndQueueResponse(projectId, pmText, userId);
|
|
@@ -492,18 +494,18 @@ app.post('/project/feedback', async (req, res) => {
|
|
| 492 |
// Did the PM give the Worker UI/Client homework?
|
| 493 |
const nextInstruction = extractWorkerPrompt(pmText);
|
| 494 |
if (nextInstruction) {
|
| 495 |
-
const delegationPrompt = `: The PM has handled the backend logic. Now execute this UI/Client task: ${nextInstruction}`;
|
| 496 |
|
| 497 |
-
const workerDelegationResult = await AIEngine.callWorker(project.workerHistory, delegationPrompt,[]);
|
| 498 |
basicUsage += (workerDelegationResult.usage?.totalTokenCount || 0);
|
| 499 |
responseText = workerDelegationResult.text;
|
| 500 |
|
| 501 |
-
project.workerHistory.push({ role: 'user', parts: });
|
| 502 |
-
project.workerHistory.push({ role: 'model', parts: });
|
| 503 |
} else {
|
| 504 |
responseText = "System Note: The PM completed the core task without delegating client logic.";
|
| 505 |
-
project.workerHistory.push({ role: 'user', parts: });
|
| 506 |
-
project.workerHistory.push({ role: 'model', parts: });
|
| 507 |
}
|
| 508 |
}
|
| 509 |
// INTERCEPTOR B: Worker asks PM a question
|
|
@@ -512,29 +514,29 @@ app.post('/project/feedback', async (req, res) => {
|
|
| 512 |
if (pmQuestion) {
|
| 513 |
console.log(` ❓ Worker asked PM: ${pmQuestion}`);
|
| 514 |
|
| 515 |
-
const pmConsultPrompt = `: The Worker asks: "${pmQuestion}"\nProvide a technical answer to unblock them.`;
|
| 516 |
const pmResult = await AIEngine.callPM(project.pmHistory, pmConsultPrompt);
|
| 517 |
diamondUsage += (pmResult.usage?.totalTokenCount || 0);
|
| 518 |
const pmAnswer = pmResult.text;
|
| 519 |
|
| 520 |
-
project.pmHistory.push({ role: 'user', parts: });
|
| 521 |
-
project.pmHistory.push({ role: 'model', parts: });
|
| 522 |
|
| 523 |
-
const injectionMsg = `: ${pmAnswer}`;
|
| 524 |
-
project.workerHistory.push({ role: 'user', parts: });
|
| 525 |
-
project.workerHistory.push({ role: 'model', parts: });
|
| 526 |
|
| 527 |
// Re-call Worker
|
| 528 |
-
workerResult = await AIEngine.callWorker(project.workerHistory, injectionMsg,[]);
|
| 529 |
basicUsage += (workerResult.usage?.totalTokenCount || 0);
|
| 530 |
responseText = workerResult.text;
|
| 531 |
|
| 532 |
-
project.workerHistory.push({ role: 'user', parts: });
|
| 533 |
-
project.workerHistory.push({ role: 'model', parts: });
|
| 534 |
} else {
|
| 535 |
// Standard Worker Execution
|
| 536 |
-
project.workerHistory.push({ role: 'user', parts: });
|
| 537 |
-
project.workerHistory.push({ role: 'model', parts: });
|
| 538 |
}
|
| 539 |
}
|
| 540 |
|
|
@@ -600,18 +602,18 @@ app.post('/human/override', validateRequest, async (req, res) => {
|
|
| 600 |
await checkMinimumCredits(userId, 'basic');
|
| 601 |
|
| 602 |
const project = await StateManager.getProject(projectId);
|
| 603 |
-
const overrideMsg = `: ${instruction}`;
|
| 604 |
|
| 605 |
if (pruneHistory && project.workerHistory.length >= 2) {
|
| 606 |
project.workerHistory.pop();
|
| 607 |
project.workerHistory.pop();
|
| 608 |
}
|
| 609 |
|
| 610 |
-
const workerResult = await AIEngine.callWorker(project.workerHistory, overrideMsg,[]);
|
| 611 |
basicUsage += (workerResult.usage?.totalTokenCount || 0);
|
| 612 |
|
| 613 |
-
project.workerHistory.push({ role: 'user', parts: });
|
| 614 |
-
project.workerHistory.push({ role: 'model', parts: });
|
| 615 |
|
| 616 |
await StateManager.updateProject(projectId, { workerHistory: project.workerHistory });
|
| 617 |
await processAndQueueResponse(projectId, workerResult.text, userId);
|
|
|
|
|
|
|
| 1 |
// App.js
|
| 2 |
|
| 3 |
import express from 'express';
|
|
|
|
| 81 |
const current = current_credits || 0;
|
| 82 |
return Math.max(0, current - amount);
|
| 83 |
});
|
| 84 |
+
console.log(`[Credits] Deducted ${amount} ${type} credits from User ${userId}`);
|
| 85 |
} catch (err) {
|
| 86 |
+
console.error(`[Credits] Failed to deduct ${amount} ${type} from ${userId}:`, err);
|
| 87 |
}
|
| 88 |
}
|
| 89 |
|
|
|
|
| 100 |
next();
|
| 101 |
};
|
| 102 |
|
| 103 |
+
// --- REGEX HELPERS (FIXED) ---
|
| 104 |
function extractWorkerPrompt(text) {
|
| 105 |
const match = text.match(/WORKER_PROMPT:\s*(.*)/s);
|
| 106 |
+
return match ? match[1].trim() : null;
|
| 107 |
}
|
| 108 |
|
| 109 |
function formatContext({ hierarchyContext, scriptContext, logContext }) {
|
| 110 |
let out = "";
|
| 111 |
+
if (scriptContext) out += `\n[TARGET SCRIPT]: ${scriptContext.targetName}\n[SOURCE PREVIEW]: ${scriptContext.scriptSource}`;
|
| 112 |
+
if (logContext) out += `\n[LAST LOGS]: ${logContext.logs}`;
|
| 113 |
+
if (hierarchyContext) out += `\n[Hierarchy Context]: ${hierarchyContext}`;
|
| 114 |
return out;
|
| 115 |
}
|
| 116 |
|
| 117 |
function extractPMQuestion(text) {
|
| 118 |
+
// Looks for [ASK_PM: ...]
|
| 119 |
+
const match = text.match(/\[ASK_PM:\s*(.*?)\]/s);
|
| 120 |
+
return match ? match[1].trim() : null;
|
| 121 |
}
|
| 122 |
|
| 123 |
function extractImagePrompt(text) {
|
| 124 |
+
// Looks for [GENERATE_IMAGE: ...]
|
| 125 |
+
const match = text.match(/\[GENERATE_IMAGE:\s*(.*?)\]/s);
|
| 126 |
+
return match ? match[1].trim() : null;
|
| 127 |
}
|
| 128 |
|
| 129 |
function extractRouteToPM(text) {
|
| 130 |
+
// Looks for [ROUTE_TO_PM: ...]
|
| 131 |
+
const match = text.match(/\[ROUTE_TO_PM:\s*(.*?)\]/s);
|
| 132 |
+
return match ? match[1].trim() : null;
|
| 133 |
}
|
| 134 |
|
| 135 |
// --- ADMIN ENDPOINTS ---
|
|
|
|
| 150 |
|
| 151 |
try {
|
| 152 |
await checkMinimumCredits(userId, 'basic');
|
| 153 |
+
console.log(`[Onboarding] Analyzing idea...`);
|
| 154 |
|
| 155 |
const result = await AIEngine.generateEntryQuestions(description);
|
| 156 |
const usage = result.usage?.totalTokenCount || 0;
|
|
|
|
| 184 |
const randomHex = (n = 6) => crypto.randomBytes(Math.ceil(n/2)).toString("hex").slice(0, n);
|
| 185 |
const projectId = `proj_${Date.now()}_${randomHex(7)}`;
|
| 186 |
|
| 187 |
+
console.log(`[Onboarding] Grading Project ${projectId}...`);
|
| 188 |
|
| 189 |
const gradingResult = await AIEngine.gradeProject(description, answers);
|
| 190 |
basicTokens += (gradingResult.usage?.totalTokenCount || 0);
|
|
|
|
| 205 |
thumbnail: thumbnailUrl,
|
| 206 |
createdAt: Date.now(),
|
| 207 |
status: "Idle",
|
| 208 |
+
workerHistory: [],
|
| 209 |
pmHistory: [],
|
| 210 |
+
commandQueue: [],
|
| 211 |
failureCount: 0
|
| 212 |
};
|
| 213 |
await StateManager.updateProject(projectId, memoryObject);
|
|
|
|
| 217 |
await firestore.collection('projects').doc(projectId).set({
|
| 218 |
id: projectId,
|
| 219 |
userId: userId,
|
| 220 |
+
assets: thumbnailUrl ? [thumbnailUrl] : [],
|
| 221 |
createdAt: admin.firestore.FieldValue.serverTimestamp()
|
| 222 |
});
|
| 223 |
}
|
| 224 |
|
| 225 |
if (db && !isFailure) {
|
| 226 |
const updates = {};
|
| 227 |
+
updates[`projects/${projectId}/info`] = {
|
| 228 |
id: projectId,
|
| 229 |
userId,
|
| 230 |
title: gradingResult.title || "Untitled",
|
|
|
|
| 234 |
createdAt: Date.now(),
|
| 235 |
status: "Idle"
|
| 236 |
};
|
| 237 |
+
if (thumbnailUrl) updates[`projects/${projectId}/thumbnail`] = { url: thumbnailUrl };
|
| 238 |
+
updates[`projects/${projectId}/state`] = {
|
| 239 |
workerHistory: [],
|
| 240 |
+
pmHistory: [],
|
| 241 |
+
commandQueue: [],
|
| 242 |
failureCount: 0
|
| 243 |
};
|
| 244 |
await db.ref().update(updates);
|
|
|
|
| 267 |
// --- CORE ENDPOINTS ---
|
| 268 |
|
| 269 |
async function runBackgroundInitialization(projectId, userId, description) {
|
| 270 |
+
console.log(`[Background] Starting initialization for ${projectId}`);
|
| 271 |
|
| 272 |
let diamondUsage = 0;
|
| 273 |
let basicUsage = 0;
|
| 274 |
|
| 275 |
try {
|
| 276 |
+
const pmHistory = [];
|
| 277 |
|
| 278 |
// 1. Generate GDD (PM -> Diamond)
|
| 279 |
const gddPrompt = `Create a comprehensive GDD for: ${description}`;
|
|
|
|
| 282 |
diamondUsage += (gddResult.usage?.totalTokenCount || 0);
|
| 283 |
const gddText = gddResult.text;
|
| 284 |
|
| 285 |
+
pmHistory.push({ role: 'user', parts: [{ text: gddPrompt }] });
|
| 286 |
+
pmHistory.push({ role: 'model', parts: [{ text: gddText }] });
|
| 287 |
|
| 288 |
// 2. Generate First Task (PM -> Diamond)
|
| 289 |
const taskPrompt = "Based on the GDD, generate the first technical milestone.\nOutput format:\nTASK_NAME: <Name>\nWORKER_PROMPT: <Specific, isolated instructions for the worker>";
|
|
|
|
| 292 |
diamondUsage += (taskResult.usage?.totalTokenCount || 0);
|
| 293 |
const taskText = taskResult.text;
|
| 294 |
|
| 295 |
+
pmHistory.push({ role: 'user', parts: [{ text: taskPrompt }] });
|
| 296 |
+
pmHistory.push({ role: 'model', parts: [{ text: taskText }] });
|
| 297 |
|
| 298 |
// 3. Initialize Worker (Worker -> Basic)
|
| 299 |
const initialWorkerInstruction = extractWorkerPrompt(taskText) || `Initialize structure for: ${description}`;
|
| 300 |
+
const workerHistory = [];
|
| 301 |
const initialWorkerPrompt = `CONTEXT: New Project. \nINSTRUCTION: ${initialWorkerInstruction}`;
|
| 302 |
|
| 303 |
+
const workerResult = await AIEngine.callWorker(workerHistory, initialWorkerPrompt, []);
|
| 304 |
|
| 305 |
basicUsage += (workerResult.usage?.totalTokenCount || 0);
|
| 306 |
const workerText = workerResult.text;
|
| 307 |
|
| 308 |
+
workerHistory.push({ role: 'user', parts: [{ text: initialWorkerPrompt }] });
|
| 309 |
+
workerHistory.push({ role: 'model', parts: [{ text: workerText }] });
|
| 310 |
|
| 311 |
// 4. Update Memory
|
| 312 |
await StateManager.updateProject(projectId, {
|
|
|
|
| 330 |
if (diamondUsage > 0) await deductUserCredits(userId, diamondUsage, 'diamond');
|
| 331 |
if (basicUsage > 0) await deductUserCredits(userId, basicUsage, 'basic');
|
| 332 |
|
| 333 |
+
console.log(`[Background] Init complete. Diamond: ${diamondUsage}, Basic: ${basicUsage}`);
|
| 334 |
|
| 335 |
} catch (err) {
|
| 336 |
+
console.error(`[Background] Init Error for ${projectId}:`, err);
|
| 337 |
if(db) await db.ref(`projects/${projectId}/info/status`).set("error");
|
| 338 |
}
|
| 339 |
}
|
|
|
|
| 380 |
|
| 381 |
// SCENARIO 1: TASK COMPLETE (Worker asks PM for next job)
|
| 382 |
if (taskComplete) {
|
| 383 |
+
console.log(`[${projectId}] ✅ TASK COMPLETE.`);
|
| 384 |
const summary = `Worker completed the previous task. Logs: ${logContext?.logs || "Clean"}. \nGenerate the NEXT task using 'WORKER_PROMPT:' format.`;
|
| 385 |
|
| 386 |
const pmResult = await AIEngine.callPM(project.pmHistory, summary);
|
| 387 |
diamondUsage += (pmResult.usage?.totalTokenCount || 0);
|
| 388 |
const pmText = pmResult.text;
|
| 389 |
|
| 390 |
+
project.pmHistory.push({ role: 'user', parts: [{ text: summary }] });
|
| 391 |
+
project.pmHistory.push({ role: 'model', parts: [{ text: pmText }] });
|
| 392 |
|
| 393 |
const nextInstruction = extractWorkerPrompt(pmText);
|
| 394 |
if (!nextInstruction) {
|
|
|
|
| 399 |
}
|
| 400 |
|
| 401 |
const newPrompt = `New Objective: ${nextInstruction}`;
|
| 402 |
+
const workerResult = await AIEngine.callWorker(project.workerHistory, newPrompt, []);
|
| 403 |
basicUsage += (workerResult.usage?.totalTokenCount || 0);
|
| 404 |
const workerText = workerResult.text;
|
| 405 |
|
| 406 |
+
project.workerHistory.push({ role: 'user', parts: [{ text: newPrompt }] });
|
| 407 |
+
project.workerHistory.push({ role: 'model', parts: [{ text: workerText }] });
|
| 408 |
|
| 409 |
await StateManager.updateProject(projectId, {
|
| 410 |
pmHistory: project.pmHistory,
|
|
|
|
| 430 |
diamondUsage += (pmResult.usage?.totalTokenCount || 0);
|
| 431 |
const pmVerdict = pmResult.text;
|
| 432 |
|
| 433 |
+
if (pmVerdict.includes("[TERMINATE]")) {
|
| 434 |
+
const fixInstruction = pmVerdict.replace("[TERMINATE]", "").trim();
|
| 435 |
+
const resetPrompt = `[SYSTEM]: Previous worker terminated. \nNew Objective: ${fixInstruction}`;
|
| 436 |
|
| 437 |
// Clear History on terminate
|
| 438 |
+
const resetHistory = [];
|
| 439 |
+
const workerResult = await AIEngine.callWorker(resetHistory, resetPrompt, []);
|
| 440 |
basicUsage += (workerResult.usage?.totalTokenCount || 0);
|
| 441 |
|
| 442 |
+
resetHistory.push({ role: 'user', parts: [{ text: resetPrompt }] });
|
| 443 |
+
resetHistory.push({ role: 'model', parts: [{ text: workerResult.text }] });
|
| 444 |
|
| 445 |
await StateManager.updateProject(projectId, { workerHistory: resetHistory, failureCount: 0 });
|
| 446 |
StateManager.queueCommand(projectId, { type: "EXECUTE", payload: "print('SYSTEM: Worker Reset')" });
|
|
|
|
| 450 |
if (basicUsage > 0) await deductUserCredits(userId, basicUsage, 'basic');
|
| 451 |
return res.json({ success: true, message: "Worker Terminated." });
|
| 452 |
} else {
|
| 453 |
+
const injection = `[PM GUIDANCE]: ${pmVerdict} \n\nApply this fix now.`;
|
| 454 |
+
const workerResult = await AIEngine.callWorker(project.workerHistory, injection, []);
|
| 455 |
basicUsage += (workerResult.usage?.totalTokenCount || 0);
|
| 456 |
|
| 457 |
+
project.workerHistory.push({ role: 'user', parts: [{ text: injection }] });
|
| 458 |
+
project.workerHistory.push({ role: 'model', parts: [{ text: workerResult.text }] });
|
| 459 |
|
| 460 |
await StateManager.updateProject(projectId, { workerHistory: project.workerHistory, failureCount: 1 });
|
| 461 |
await processAndQueueResponse(projectId, workerResult.text, userId);
|
|
|
|
| 469 |
// SCENARIO 3: STANDARD INTERACTION (Worker is the Frontline)
|
| 470 |
const fullInput = `USER: ${prompt || "Automatic Feedback"}` + formatContext({ hierarchyContext, scriptContext, logContext });
|
| 471 |
|
| 472 |
+
let workerResult = await AIEngine.callWorker(project.workerHistory, fullInput, images || []);
|
| 473 |
basicUsage += (workerResult.usage?.totalTokenCount || 0);
|
| 474 |
let responseText = workerResult.text;
|
| 475 |
|
|
|
|
| 480 |
console.log(` 🔀 Task routed to PM: ${routeTask}`);
|
| 481 |
|
| 482 |
// PM generates Backend Code (Diamond)
|
| 483 |
+
const pmPrompt = `[ROUTED TASK]: ${routeTask}\nWrite the core server/backend logic yourself. Then use WORKER_PROMPT: <task> to delegate the UI/Client aspects to the Worker.`;
|
| 484 |
const pmResult = await AIEngine.callPM(project.pmHistory, pmPrompt);
|
| 485 |
diamondUsage += (pmResult.usage?.totalTokenCount || 0);
|
| 486 |
const pmText = pmResult.text;
|
| 487 |
|
| 488 |
+
project.pmHistory.push({ role: 'user', parts: [{ text: pmPrompt }] });
|
| 489 |
+
project.pmHistory.push({ role: 'model', parts: [{ text: pmText }] });
|
| 490 |
|
| 491 |
// Queue PM's Backend Code to Studio
|
| 492 |
await processAndQueueResponse(projectId, pmText, userId);
|
|
|
|
| 494 |
// Did the PM give the Worker UI/Client homework?
|
| 495 |
const nextInstruction = extractWorkerPrompt(pmText);
|
| 496 |
if (nextInstruction) {
|
| 497 |
+
const delegationPrompt = `[SYSTEM]: The PM has handled the backend logic. Now execute this UI/Client task: ${nextInstruction}`;
|
| 498 |
|
| 499 |
+
const workerDelegationResult = await AIEngine.callWorker(project.workerHistory, delegationPrompt, []);
|
| 500 |
basicUsage += (workerDelegationResult.usage?.totalTokenCount || 0);
|
| 501 |
responseText = workerDelegationResult.text;
|
| 502 |
|
| 503 |
+
project.workerHistory.push({ role: 'user', parts: [{ text: delegationPrompt }] });
|
| 504 |
+
project.workerHistory.push({ role: 'model', parts: [{ text: responseText }] });
|
| 505 |
} else {
|
| 506 |
responseText = "System Note: The PM completed the core task without delegating client logic.";
|
| 507 |
+
project.workerHistory.push({ role: 'user', parts: [{ text: "System: PM task complete." }] });
|
| 508 |
+
project.workerHistory.push({ role: 'model', parts: [{ text: responseText }] });
|
| 509 |
}
|
| 510 |
}
|
| 511 |
// INTERCEPTOR B: Worker asks PM a question
|
|
|
|
| 514 |
if (pmQuestion) {
|
| 515 |
console.log(` ❓ Worker asked PM: ${pmQuestion}`);
|
| 516 |
|
| 517 |
+
const pmConsultPrompt = `[WORKER CONSULTATION]: The Worker asks: "${pmQuestion}"\nProvide a technical answer to unblock them.`;
|
| 518 |
const pmResult = await AIEngine.callPM(project.pmHistory, pmConsultPrompt);
|
| 519 |
diamondUsage += (pmResult.usage?.totalTokenCount || 0);
|
| 520 |
const pmAnswer = pmResult.text;
|
| 521 |
|
| 522 |
+
project.pmHistory.push({ role: 'user', parts: [{ text: pmConsultPrompt }] });
|
| 523 |
+
project.pmHistory.push({ role: 'model', parts: [{ text: pmAnswer }] });
|
| 524 |
|
| 525 |
+
const injectionMsg = `[PM RESPONSE]: ${pmAnswer}`;
|
| 526 |
+
project.workerHistory.push({ role: 'user', parts: [{ text: injectionMsg }] });
|
| 527 |
+
project.workerHistory.push({ role: 'model', parts: [{ text: responseText }] });
|
| 528 |
|
| 529 |
// Re-call Worker
|
| 530 |
+
workerResult = await AIEngine.callWorker(project.workerHistory, injectionMsg, []);
|
| 531 |
basicUsage += (workerResult.usage?.totalTokenCount || 0);
|
| 532 |
responseText = workerResult.text;
|
| 533 |
|
| 534 |
+
project.workerHistory.push({ role: 'user', parts: [{ text: injectionMsg }] });
|
| 535 |
+
project.workerHistory.push({ role: 'model', parts: [{ text: responseText }] });
|
| 536 |
} else {
|
| 537 |
// Standard Worker Execution
|
| 538 |
+
project.workerHistory.push({ role: 'user', parts: [{ text: fullInput }] });
|
| 539 |
+
project.workerHistory.push({ role: 'model', parts: [{ text: responseText }] });
|
| 540 |
}
|
| 541 |
}
|
| 542 |
|
|
|
|
| 602 |
await checkMinimumCredits(userId, 'basic');
|
| 603 |
|
| 604 |
const project = await StateManager.getProject(projectId);
|
| 605 |
+
const overrideMsg = `[SYSTEM OVERRIDE]: ${instruction}`;
|
| 606 |
|
| 607 |
if (pruneHistory && project.workerHistory.length >= 2) {
|
| 608 |
project.workerHistory.pop();
|
| 609 |
project.workerHistory.pop();
|
| 610 |
}
|
| 611 |
|
| 612 |
+
const workerResult = await AIEngine.callWorker(project.workerHistory, overrideMsg, []);
|
| 613 |
basicUsage += (workerResult.usage?.totalTokenCount || 0);
|
| 614 |
|
| 615 |
+
project.workerHistory.push({ role: 'user', parts: [{ text: overrideMsg }] });
|
| 616 |
+
project.workerHistory.push({ role: 'model', parts: [{ text: workerResult.text }] });
|
| 617 |
|
| 618 |
await StateManager.updateProject(projectId, { workerHistory: project.workerHistory });
|
| 619 |
await processAndQueueResponse(projectId, workerResult.text, userId);
|