everydaytok commited on
Commit
e089c3a
·
verified ·
1 Parent(s): 9609b4f

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +39 -29
app.js CHANGED
@@ -158,7 +158,7 @@ async function runBackgroundInitialization(projectId, userId, description) {
158
  await StateManager.getProject(projectId);
159
 
160
  await StateManager.updateProject(projectId, {
161
- status: "WORKING",
162
  gdd: "",
163
  failureCount: 0
164
  });
@@ -228,7 +228,7 @@ async function runBackgroundInitialization(projectId, userId, description) {
228
 
229
  await StateManager.updateProject(projectId, {
230
  gdd: gddText,
231
- status: "IDLE"
232
  });
233
 
234
  await processAndQueueResponse(projectId, workerTextAccumulated, userId);
@@ -240,31 +240,42 @@ async function runBackgroundInitialization(projectId, userId, description) {
240
 
241
  } catch (err) {
242
  console.error(`[Background] Init Error for ${projectId}:`, err.message);
243
- await StateManager.updateProject(projectId, { status: "ERROR" });
244
  } finally {
245
  StateManager.unlock(projectId);
246
  }
247
  }
248
 
249
- // --- UPDATED: Accepts IMAGES and Handles Interaction Memory Correctly ---
250
  async function runAsyncFeedback(projectId, userId, fullInput, images = []) {
251
  let diamondUsage = 0;
252
  let basicUsage = 0;
253
 
254
  try {
255
- // 1. Refresh Project State
 
 
 
 
 
 
 
 
 
256
  const project = await StateManager.getProject(projectId);
257
- StateManager.clearSnapshot(projectId);
258
 
 
 
259
  let stopStatus = startStatusLoop(projectId, 'worker');
260
 
261
- // 2. Initial Worker Execution (The "Question" or "Attempt" phase)
262
  let firstTurnResponse = "";
263
  let thoughtText = "";
264
 
265
- // Use a local history copy so we can append to it for the second turn without waiting for DB refetch
 
266
  const currentWorkerHistory = [...(project.workerHistory || [])];
267
 
 
268
  await AIEngine.callWorkerStream(
269
  currentWorkerHistory,
270
  fullInput,
@@ -283,19 +294,17 @@ async function runAsyncFeedback(projectId, userId, fullInput, images = []) {
283
  images
284
  );
285
 
286
- // 3. Analyze for Delegation/Consultation
287
  const routeTask = extractRouteToPM(firstTurnResponse);
288
  const pmQuestion = extractPMQuestion(firstTurnResponse);
289
 
290
  let finalResponseToSave = firstTurnResponse;
291
 
292
  if (routeTask || pmQuestion) {
293
- // --- Save the Worker's first turn immediately ---
294
- await StateManager.addHistory(projectId, 'worker', 'user', fullInput);
295
  await StateManager.addHistory(projectId, 'worker', 'model', firstTurnResponse);
296
 
297
- // Push to our local history for the immediate next call
298
- currentWorkerHistory.push({ role: 'user', parts: [{ text: fullInput }] });
299
  currentWorkerHistory.push({ role: 'model', parts: [{ text: firstTurnResponse }] });
300
 
301
  let pmPrompt = "";
@@ -311,7 +320,7 @@ async function runAsyncFeedback(projectId, userId, fullInput, images = []) {
311
  pmContextPrefix = "[PM ANSWER]:";
312
  }
313
 
314
- // 4. Run Project Manager
315
  StateManager.clearSnapshot(projectId);
316
  stopStatus = startStatusLoop(projectId, 'pm');
317
 
@@ -333,20 +342,21 @@ async function runAsyncFeedback(projectId, userId, fullInput, images = []) {
333
  }
334
  );
335
 
336
- diamondUsage += 0;
337
-
338
  // Save PM History
339
  await StateManager.addHistory(projectId, 'pm', 'user', pmPrompt);
340
  await StateManager.addHistory(projectId, 'pm', 'model', pmResponseText);
341
 
342
- // --- CRITICAL FIX: EXECUTE PM CODE ---
343
- // If the PM wrote code (backend logic), execute it now.
344
  await processAndQueueResponse(projectId, pmResponseText, userId);
345
 
346
- // 5. Run Worker Continuation (The "Answer" Phase)
347
  const nextInstruction = extractWorkerPrompt(pmResponseText) || pmResponseText;
348
  const workerContinuationPrompt = `${pmContextPrefix} ${nextInstruction}\n\nBased on this, continue the task and output the code.`;
349
 
 
 
 
 
350
  StateManager.clearSnapshot(projectId);
351
  stopStatus = startStatusLoop(projectId, 'worker');
352
 
@@ -368,25 +378,21 @@ async function runAsyncFeedback(projectId, userId, fullInput, images = []) {
368
  }
369
  );
370
 
371
- // Save the continuation
372
- await StateManager.addHistory(projectId, 'worker', 'user', workerContinuationPrompt);
373
  await StateManager.addHistory(projectId, 'worker', 'model', secondTurnResponse);
374
 
375
- // Update the variable for the FINAL execution (Worker's part)
376
  finalResponseToSave = secondTurnResponse;
377
  } else {
378
- // Standard path (No PM needed), save history normally
379
- await StateManager.addHistory(projectId, 'worker', 'user', fullInput);
380
  await StateManager.addHistory(projectId, 'worker', 'model', firstTurnResponse);
381
  }
382
 
383
  StateManager.setStatus(projectId, "Idle");
384
 
385
- // 6. Execute Logic (Worker's part)
386
- // If PM ran before this, their code is already queued. Now we queue the Worker's code.
387
  await processAndQueueResponse(projectId, finalResponseToSave, userId);
388
-
389
- // 7. Update Status & Timestamp
390
  await StateManager.updateProject(projectId, { status: "idle" });
391
 
392
  if (diamondUsage > 0) await deductUserCredits(userId, diamondUsage, 'diamond');
@@ -395,6 +401,7 @@ async function runAsyncFeedback(projectId, userId, fullInput, images = []) {
395
  } catch (err) {
396
  console.error("Async Feedback Error:", err);
397
  StateManager.setStatus(projectId, "Error: " + err.message);
 
398
  await StateManager.updateProject(projectId, { status: "error" });
399
  }
400
  }
@@ -500,12 +507,14 @@ app.post('/project/feedback', async (req, res) => {
500
 
501
  await checkMinimumCredits(userId, 'basic');
502
 
 
 
503
  await StateManager.updateProject(projectId, { status: "working" });
504
 
505
  const context = formatContext({ hierarchyContext, scriptContext, logContext });
506
  const fullInput = `USER: ${prompt || "Automatic Feedback"}${context}`;
507
 
508
- // Pass images array to async runner
509
  runAsyncFeedback(projectId, userId, fullInput, images || []);
510
 
511
  res.json({ success: true, message: "Processing started" });
@@ -527,6 +536,7 @@ app.post('/project/ping', async (req, res) => {
527
  const currentStatus = StateManager.getStatus(projectId);
528
 
529
  if (isFrontend) {
 
530
  const snapshot = StateManager.getSnapshot(projectId);
531
  return res.json({ status: currentStatus, snapshot });
532
  }
 
158
  await StateManager.getProject(projectId);
159
 
160
  await StateManager.updateProject(projectId, {
161
+ status: "working",
162
  gdd: "",
163
  failureCount: 0
164
  });
 
228
 
229
  await StateManager.updateProject(projectId, {
230
  gdd: gddText,
231
+ status: "idle"
232
  });
233
 
234
  await processAndQueueResponse(projectId, workerTextAccumulated, userId);
 
240
 
241
  } catch (err) {
242
  console.error(`[Background] Init Error for ${projectId}:`, err.message);
243
+ await StateManager.updateProject(projectId, { status: "error" });
244
  } finally {
245
  StateManager.unlock(projectId);
246
  }
247
  }
248
 
249
+ // --- CORE FIX: Robust Async Feedback with Immediate Persistence ---
250
  async function runAsyncFeedback(projectId, userId, fullInput, images = []) {
251
  let diamondUsage = 0;
252
  let basicUsage = 0;
253
 
254
  try {
255
+ console.log(`[${projectId}] Feedback received. Persisting state immediately.`);
256
+
257
+ // 1. INSTANT PERSISTENCE: Save status and User Message BEFORE anything else.
258
+ // This ensures if the user reloads now, they see "Working" and their message.
259
+ await Promise.all([
260
+ StateManager.updateProject(projectId, { status: "working" }),
261
+ StateManager.addHistory(projectId, 'worker', 'user', fullInput)
262
+ ]);
263
+
264
+ // 2. Load latest state
265
  const project = await StateManager.getProject(projectId);
 
266
 
267
+ // Reset buffers for frontend streaming
268
+ StateManager.clearSnapshot(projectId);
269
  let stopStatus = startStatusLoop(projectId, 'worker');
270
 
 
271
  let firstTurnResponse = "";
272
  let thoughtText = "";
273
 
274
+ // Use local history clone so we can manipulate it for multi-turn without extra DB fetches
275
+ // Note: 'project.workerHistory' now already contains the user input we just saved in Step 1.
276
  const currentWorkerHistory = [...(project.workerHistory || [])];
277
 
278
+ // 3. First Turn (Worker Execution)
279
  await AIEngine.callWorkerStream(
280
  currentWorkerHistory,
281
  fullInput,
 
294
  images
295
  );
296
 
297
+ // 4. Analyze Output (Delegation Check)
298
  const routeTask = extractRouteToPM(firstTurnResponse);
299
  const pmQuestion = extractPMQuestion(firstTurnResponse);
300
 
301
  let finalResponseToSave = firstTurnResponse;
302
 
303
  if (routeTask || pmQuestion) {
304
+ // Save the Worker's preliminary response to DB so it doesn't vanish
 
305
  await StateManager.addHistory(projectId, 'worker', 'model', firstTurnResponse);
306
 
307
+ // Add to local context
 
308
  currentWorkerHistory.push({ role: 'model', parts: [{ text: firstTurnResponse }] });
309
 
310
  let pmPrompt = "";
 
320
  pmContextPrefix = "[PM ANSWER]:";
321
  }
322
 
323
+ // 5. Run PM
324
  StateManager.clearSnapshot(projectId);
325
  stopStatus = startStatusLoop(projectId, 'pm');
326
 
 
342
  }
343
  );
344
 
 
 
345
  // Save PM History
346
  await StateManager.addHistory(projectId, 'pm', 'user', pmPrompt);
347
  await StateManager.addHistory(projectId, 'pm', 'model', pmResponseText);
348
 
349
+ // Execute PM Code (Server Logic)
 
350
  await processAndQueueResponse(projectId, pmResponseText, userId);
351
 
352
+ // 6. Run Worker Continuation
353
  const nextInstruction = extractWorkerPrompt(pmResponseText) || pmResponseText;
354
  const workerContinuationPrompt = `${pmContextPrefix} ${nextInstruction}\n\nBased on this, continue the task and output the code.`;
355
 
356
+ // Save System Instruction
357
+ await StateManager.addHistory(projectId, 'worker', 'user', workerContinuationPrompt);
358
+ currentWorkerHistory.push({ role: 'user', parts: [{ text: workerContinuationPrompt }] });
359
+
360
  StateManager.clearSnapshot(projectId);
361
  stopStatus = startStatusLoop(projectId, 'worker');
362
 
 
378
  }
379
  );
380
 
381
+ // Save Final Response
 
382
  await StateManager.addHistory(projectId, 'worker', 'model', secondTurnResponse);
383
 
 
384
  finalResponseToSave = secondTurnResponse;
385
  } else {
386
+ // Normal path: We already saved 'user' in Step 1. Now save 'model'.
 
387
  await StateManager.addHistory(projectId, 'worker', 'model', firstTurnResponse);
388
  }
389
 
390
  StateManager.setStatus(projectId, "Idle");
391
 
392
+ // 7. Final Execution & Cleanup
 
393
  await processAndQueueResponse(projectId, finalResponseToSave, userId);
394
+
395
+ // Ensure status goes back to idle AND timestamp is updated
396
  await StateManager.updateProject(projectId, { status: "idle" });
397
 
398
  if (diamondUsage > 0) await deductUserCredits(userId, diamondUsage, 'diamond');
 
401
  } catch (err) {
402
  console.error("Async Feedback Error:", err);
403
  StateManager.setStatus(projectId, "Error: " + err.message);
404
+ // Ensure we don't get stuck in 'working'
405
  await StateManager.updateProject(projectId, { status: "error" });
406
  }
407
  }
 
507
 
508
  await checkMinimumCredits(userId, 'basic');
509
 
510
+ // Response started -> Status is now WORKING (In Memory)
511
+ // Actual DB persistence happens inside runAsyncFeedback first thing
512
  await StateManager.updateProject(projectId, { status: "working" });
513
 
514
  const context = formatContext({ hierarchyContext, scriptContext, logContext });
515
  const fullInput = `USER: ${prompt || "Automatic Feedback"}${context}`;
516
 
517
+ // Trigger Async
518
  runAsyncFeedback(projectId, userId, fullInput, images || []);
519
 
520
  res.json({ success: true, message: "Processing started" });
 
536
  const currentStatus = StateManager.getStatus(projectId);
537
 
538
  if (isFrontend) {
539
+ // Return Snapshot for streaming
540
  const snapshot = StateManager.getSnapshot(projectId);
541
  return res.json({ status: currentStatus, snapshot });
542
  }