everydaytok commited on
Commit
f2f67f2
·
verified ·
1 Parent(s): 110db83

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +96 -20
app.js CHANGED
@@ -21,6 +21,49 @@ app.use(bodyParser.json({ limit: '50mb' }));
21
  const MIN_BASIC_REQUIRED = 50;
22
  const MIN_DIAMOND_REQUIRED = 50;
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  async function checkMinimumCredits(userId, type = 'basic') {
25
  if (!supabase) return;
26
 
@@ -105,6 +148,8 @@ function formatContext({ hierarchyContext, scriptContext, logContext }) {
105
  return out;
106
  }
107
 
 
 
108
  async function runBackgroundInitialization(projectId, userId, description) {
109
  if (StateManager.isLocked(projectId)) {
110
  console.log(`[Init Guard] Project ${projectId} is already initializing. Skipping.`);
@@ -128,11 +173,14 @@ async function runBackgroundInitialization(projectId, userId, description) {
128
 
129
  const pmHistory = [];
130
 
131
- StateManager.setStatus(projectId, "Project Manager: Writing Design Doc...");
 
132
 
133
  const gddPrompt = `Create a comprehensive Game Design Document (GDD) for: ${description}`;
134
  const gddResult = await AIEngine.callPM(pmHistory, gddPrompt);
135
 
 
 
136
  if (!gddResult?.text) throw new Error("PM failed to generate GDD");
137
 
138
  const gddText = gddResult.text;
@@ -144,10 +192,14 @@ async function runBackgroundInitialization(projectId, userId, description) {
144
  pmHistory.push({ role: 'user', parts: [{ text: gddPrompt }] });
145
  pmHistory.push({ role: 'model', parts: [{ text: gddText }] });
146
 
147
- StateManager.setStatus(projectId, "Project Manager: Planning Tasks...");
 
 
148
  const taskPrompt = "Based on the GDD, generate the first technical milestone.\nOutput format:\nTASK_NAME: <Name>\nWORKER_PROMPT: <Specific, isolated instructions for the worker>";
149
  const taskResult = await AIEngine.callPM(pmHistory, taskPrompt);
150
 
 
 
151
  if (!taskResult?.text) throw new Error("PM failed to generate Task");
152
 
153
  const taskText = taskResult.text;
@@ -159,7 +211,8 @@ async function runBackgroundInitialization(projectId, userId, description) {
159
  const initialWorkerInstruction = extractWorkerPrompt(taskText) || `Initialize structure for: ${description}`;
160
  const initialWorkerPrompt = `CONTEXT: New Project. \nINSTRUCTION: ${initialWorkerInstruction}`;
161
 
162
- StateManager.setStatus(projectId, "Worker: Initializing...");
 
163
 
164
  let workerTextAccumulated = "";
165
 
@@ -167,10 +220,12 @@ async function runBackgroundInitialization(projectId, userId, description) {
167
  [],
168
  initialWorkerPrompt,
169
  (thought) => {
 
170
  StateManager.setStatus(projectId, "Worker: Thinking...");
171
  StateManager.appendSnapshotOnly(projectId, thought);
172
  },
173
  (chunk) => {
 
174
  StateManager.setStatus(projectId, "Worker: Coding...");
175
  workerTextAccumulated += chunk;
176
  StateManager.appendStream(projectId, chunk);
@@ -211,35 +266,41 @@ async function runAsyncFeedback(projectId, userId, fullInput) {
211
 
212
  // 1. Clear previous ghost text
213
  StateManager.clearSnapshot(projectId);
214
- StateManager.setStatus(projectId, "Worker: Starting...");
215
 
216
- let responseText = ""; // The actual code
217
- let thoughtText = ""; // The reasoning (optional to save, we generally discard)
 
 
 
218
 
219
- // 2. Call Worker
220
  await AIEngine.callWorkerStream(
221
  project.workerHistory,
222
  fullInput,
223
  (thought) => {
 
224
  thoughtText += thought;
225
  StateManager.setStatus(projectId, "Worker: Thinking...");
226
  StateManager.appendSnapshotOnly(projectId, thought);
227
  },
228
  (chunk) => {
 
229
  responseText += chunk;
230
  StateManager.setStatus(projectId, "Worker: Coding...");
231
  StateManager.appendStream(projectId, chunk);
232
  }
233
  );
234
 
235
- // 3. Interceptors
236
  const routeTask = extractRouteToPM(responseText);
237
  const pmQuestion = extractPMQuestion(responseText);
238
 
239
  if (routeTask) {
240
  console.log(`[${projectId}] Routing task to PM...`);
241
  StateManager.clearSnapshot(projectId);
242
- StateManager.setStatus(projectId, "Project Manager: Reviewing...");
 
 
243
 
244
  const pmPrompt = `[ROUTED TASK]: ${routeTask}\nWrite the backend/logic. Then use WORKER_PROMPT: <task> to delegate.`;
245
 
@@ -249,11 +310,13 @@ async function runAsyncFeedback(projectId, userId, fullInput) {
249
  project.pmHistory,
250
  pmPrompt,
251
  (thought) => {
252
- StateManager.setStatus(projectId, "Project Manager: Thinking...");
 
253
  StateManager.appendSnapshotOnly(projectId, thought);
254
  },
255
  (chunk) => {
256
- StateManager.setStatus(projectId, "Project Manager: Planning...");
 
257
  pmResponseText += chunk;
258
  StateManager.appendSnapshotOnly(projectId, chunk);
259
  }
@@ -267,17 +330,21 @@ async function runAsyncFeedback(projectId, userId, fullInput) {
267
  const nextInstruction = extractWorkerPrompt(pmResponseText);
268
  if (nextInstruction) {
269
  StateManager.clearSnapshot(projectId);
270
- StateManager.setStatus(projectId, "Worker: Applying Changes...");
 
 
271
 
272
  let subResponse = "";
273
  await AIEngine.callWorkerStream(
274
  project.workerHistory,
275
  `[PM DELEGATION]: ${nextInstruction}`,
276
  (thought) => {
 
277
  StateManager.setStatus(projectId, "Worker: Thinking...");
278
  StateManager.appendSnapshotOnly(projectId, thought);
279
  },
280
  (chunk) => {
 
281
  StateManager.setStatus(projectId, "Worker: Coding...");
282
  subResponse += chunk;
283
  StateManager.appendStream(projectId, chunk);
@@ -288,18 +355,22 @@ async function runAsyncFeedback(projectId, userId, fullInput) {
288
  } else if (pmQuestion) {
289
  console.log(`[${projectId}] Worker consulting PM...`);
290
  StateManager.clearSnapshot(projectId);
291
- StateManager.setStatus(projectId, "Project Manager: Consulting...");
 
 
292
 
293
  let pmResponseText = "";
294
  await AIEngine.callPMStream(
295
  project.pmHistory,
296
  pmQuestion,
297
  (thought) => {
298
- StateManager.setStatus(projectId, "Project Manager: Thinking...");
 
299
  StateManager.appendSnapshotOnly(projectId, thought);
300
  },
301
  (chunk) => {
302
- StateManager.setStatus(projectId, "Project Manager: Advising...");
 
303
  pmResponseText += chunk;
304
  StateManager.appendSnapshotOnly(projectId, chunk);
305
  }
@@ -311,17 +382,21 @@ async function runAsyncFeedback(projectId, userId, fullInput) {
311
  await StateManager.addHistory(projectId, 'pm', 'model', pmResponseText);
312
 
313
  StateManager.clearSnapshot(projectId);
314
- StateManager.setStatus(projectId, "Worker: Implementing Answer...");
 
 
315
 
316
  let subResponse = "";
317
  await AIEngine.callWorkerStream(
318
  project.workerHistory,
319
  `[PM ANSWER]: ${pmResponseText}`,
320
  (thought) => {
 
321
  StateManager.setStatus(projectId, "Worker: Thinking...");
322
  StateManager.appendSnapshotOnly(projectId, thought);
323
  },
324
  (chunk) => {
 
325
  StateManager.setStatus(projectId, "Worker: Coding...");
326
  subResponse += chunk;
327
  StateManager.appendStream(projectId, chunk);
@@ -332,15 +407,13 @@ async function runAsyncFeedback(projectId, userId, fullInput) {
332
 
333
  StateManager.setStatus(projectId, "Finalizing...");
334
 
335
- // 4. PERSISTENCE (CRITICAL: Wait for this before IDLE)
336
  await StateManager.addHistory(projectId, 'worker', 'user', fullInput);
337
-
338
- // Save the FINAL accumulated text (responseText), not a stream fragment
339
  await StateManager.addHistory(projectId, 'worker', 'model', responseText);
340
 
341
  await processAndQueueResponse(projectId, responseText, userId);
342
 
343
- // 5. FINISH
344
  await StateManager.updateProject(projectId, { status: "idle" });
345
 
346
  if (diamondUsage > 0) await deductUserCredits(userId, diamondUsage, 'diamond');
@@ -513,15 +586,18 @@ app.post('/human/override', validateRequest, async (req, res) => {
513
  const overrideMsg = `[SYSTEM OVERRIDE]: ${instruction}`;
514
 
515
  let overrideText = "";
 
516
 
517
  AIEngine.callWorkerStream(
518
  project.workerHistory,
519
  overrideMsg,
520
  (thought) => {
 
521
  StateManager.setStatus(projectId, "Worker: Thinking...");
522
  StateManager.appendSnapshotOnly(projectId, thought);
523
  },
524
  (chunk) => {
 
525
  StateManager.setStatus(projectId, "Worker: Coding...");
526
  overrideText += chunk;
527
  StateManager.appendStream(projectId, chunk);
 
21
  const MIN_BASIC_REQUIRED = 50;
22
  const MIN_DIAMOND_REQUIRED = 50;
23
 
24
+ // --- STATUS PHASES ---
25
+ const WORKER_PHASES = [
26
+ "Worker: Analyzing Request...",
27
+ "Worker: Reading Context...",
28
+ "Worker: Checking Hierarchy...",
29
+ "Worker: Planning Logic...",
30
+ "Worker: Preparing Environment...",
31
+ "Worker: Thinking..." // Settles here
32
+ ];
33
+
34
+ const PM_PHASES = [
35
+ "Manager: Reviewing Request...",
36
+ "Manager: Analyzing Project Structure...",
37
+ "Manager: Consulting Guidelines...",
38
+ "Manager: Formulating Strategy...",
39
+ "Manager: Delegating Tasks...",
40
+ "Manager: Thinking..." // Settles here
41
+ ];
42
+
43
+ // --- HELPERS ---
44
+
45
+ // Starts cycling status messages in the background. Returns a stop() function.
46
+ function startStatusLoop(projectId, type = 'worker') {
47
+ const phases = type === 'pm' ? PM_PHASES : WORKER_PHASES;
48
+ let index = 0;
49
+
50
+ // Set initial status immediately
51
+ StateManager.setStatus(projectId, phases[0]);
52
+
53
+ const interval = setInterval(() => {
54
+ index++;
55
+ if (index < phases.length) {
56
+ StateManager.setStatus(projectId, phases[index]);
57
+ } else {
58
+ // Stop incrementing, but keep the interval running (or clear it)
59
+ // We just let it sit on the last message
60
+ clearInterval(interval);
61
+ }
62
+ }, 1500); // Change status every 1.5 seconds
63
+
64
+ return () => clearInterval(interval);
65
+ }
66
+
67
  async function checkMinimumCredits(userId, type = 'basic') {
68
  if (!supabase) return;
69
 
 
148
  return out;
149
  }
150
 
151
+ // --- CORE BACKGROUND LOGIC ---
152
+
153
  async function runBackgroundInitialization(projectId, userId, description) {
154
  if (StateManager.isLocked(projectId)) {
155
  console.log(`[Init Guard] Project ${projectId} is already initializing. Skipping.`);
 
173
 
174
  const pmHistory = [];
175
 
176
+ // Start PM Status Loop
177
+ let stopStatus = startStatusLoop(projectId, 'pm');
178
 
179
  const gddPrompt = `Create a comprehensive Game Design Document (GDD) for: ${description}`;
180
  const gddResult = await AIEngine.callPM(pmHistory, gddPrompt);
181
 
182
+ stopStatus(); // Stop loop when done
183
+
184
  if (!gddResult?.text) throw new Error("PM failed to generate GDD");
185
 
186
  const gddText = gddResult.text;
 
192
  pmHistory.push({ role: 'user', parts: [{ text: gddPrompt }] });
193
  pmHistory.push({ role: 'model', parts: [{ text: gddText }] });
194
 
195
+ // Start PM Status Loop again for next task
196
+ stopStatus = startStatusLoop(projectId, 'pm');
197
+
198
  const taskPrompt = "Based on the GDD, generate the first technical milestone.\nOutput format:\nTASK_NAME: <Name>\nWORKER_PROMPT: <Specific, isolated instructions for the worker>";
199
  const taskResult = await AIEngine.callPM(pmHistory, taskPrompt);
200
 
201
+ stopStatus();
202
+
203
  if (!taskResult?.text) throw new Error("PM failed to generate Task");
204
 
205
  const taskText = taskResult.text;
 
211
  const initialWorkerInstruction = extractWorkerPrompt(taskText) || `Initialize structure for: ${description}`;
212
  const initialWorkerPrompt = `CONTEXT: New Project. \nINSTRUCTION: ${initialWorkerInstruction}`;
213
 
214
+ // Start Worker Loop
215
+ stopStatus = startStatusLoop(projectId, 'worker');
216
 
217
  let workerTextAccumulated = "";
218
 
 
220
  [],
221
  initialWorkerPrompt,
222
  (thought) => {
223
+ stopStatus(); // Stop fake loop, switch to real thought
224
  StateManager.setStatus(projectId, "Worker: Thinking...");
225
  StateManager.appendSnapshotOnly(projectId, thought);
226
  },
227
  (chunk) => {
228
+ stopStatus(); // Stop fake loop
229
  StateManager.setStatus(projectId, "Worker: Coding...");
230
  workerTextAccumulated += chunk;
231
  StateManager.appendStream(projectId, chunk);
 
266
 
267
  // 1. Clear previous ghost text
268
  StateManager.clearSnapshot(projectId);
 
269
 
270
+ // 2. Start Status Loop (Worker)
271
+ let stopStatus = startStatusLoop(projectId, 'worker');
272
+
273
+ let responseText = "";
274
+ let thoughtText = "";
275
 
276
+ // 3. Call Worker
277
  await AIEngine.callWorkerStream(
278
  project.workerHistory,
279
  fullInput,
280
  (thought) => {
281
+ stopStatus(); // Stop cycling
282
  thoughtText += thought;
283
  StateManager.setStatus(projectId, "Worker: Thinking...");
284
  StateManager.appendSnapshotOnly(projectId, thought);
285
  },
286
  (chunk) => {
287
+ stopStatus(); // Stop cycling
288
  responseText += chunk;
289
  StateManager.setStatus(projectId, "Worker: Coding...");
290
  StateManager.appendStream(projectId, chunk);
291
  }
292
  );
293
 
294
+ // 4. Interceptors
295
  const routeTask = extractRouteToPM(responseText);
296
  const pmQuestion = extractPMQuestion(responseText);
297
 
298
  if (routeTask) {
299
  console.log(`[${projectId}] Routing task to PM...`);
300
  StateManager.clearSnapshot(projectId);
301
+
302
+ // Start PM Loop
303
+ stopStatus = startStatusLoop(projectId, 'pm');
304
 
305
  const pmPrompt = `[ROUTED TASK]: ${routeTask}\nWrite the backend/logic. Then use WORKER_PROMPT: <task> to delegate.`;
306
 
 
310
  project.pmHistory,
311
  pmPrompt,
312
  (thought) => {
313
+ stopStatus();
314
+ StateManager.setStatus(projectId, "Manager: Thinking...");
315
  StateManager.appendSnapshotOnly(projectId, thought);
316
  },
317
  (chunk) => {
318
+ stopStatus();
319
+ StateManager.setStatus(projectId, "Manager: Planning...");
320
  pmResponseText += chunk;
321
  StateManager.appendSnapshotOnly(projectId, chunk);
322
  }
 
330
  const nextInstruction = extractWorkerPrompt(pmResponseText);
331
  if (nextInstruction) {
332
  StateManager.clearSnapshot(projectId);
333
+
334
+ // Back to Worker Loop
335
+ stopStatus = startStatusLoop(projectId, 'worker');
336
 
337
  let subResponse = "";
338
  await AIEngine.callWorkerStream(
339
  project.workerHistory,
340
  `[PM DELEGATION]: ${nextInstruction}`,
341
  (thought) => {
342
+ stopStatus();
343
  StateManager.setStatus(projectId, "Worker: Thinking...");
344
  StateManager.appendSnapshotOnly(projectId, thought);
345
  },
346
  (chunk) => {
347
+ stopStatus();
348
  StateManager.setStatus(projectId, "Worker: Coding...");
349
  subResponse += chunk;
350
  StateManager.appendStream(projectId, chunk);
 
355
  } else if (pmQuestion) {
356
  console.log(`[${projectId}] Worker consulting PM...`);
357
  StateManager.clearSnapshot(projectId);
358
+
359
+ // Start PM Loop
360
+ stopStatus = startStatusLoop(projectId, 'pm');
361
 
362
  let pmResponseText = "";
363
  await AIEngine.callPMStream(
364
  project.pmHistory,
365
  pmQuestion,
366
  (thought) => {
367
+ stopStatus();
368
+ StateManager.setStatus(projectId, "Manager: Thinking...");
369
  StateManager.appendSnapshotOnly(projectId, thought);
370
  },
371
  (chunk) => {
372
+ stopStatus();
373
+ StateManager.setStatus(projectId, "Manager: Advising...");
374
  pmResponseText += chunk;
375
  StateManager.appendSnapshotOnly(projectId, chunk);
376
  }
 
382
  await StateManager.addHistory(projectId, 'pm', 'model', pmResponseText);
383
 
384
  StateManager.clearSnapshot(projectId);
385
+
386
+ // Back to Worker Loop
387
+ stopStatus = startStatusLoop(projectId, 'worker');
388
 
389
  let subResponse = "";
390
  await AIEngine.callWorkerStream(
391
  project.workerHistory,
392
  `[PM ANSWER]: ${pmResponseText}`,
393
  (thought) => {
394
+ stopStatus();
395
  StateManager.setStatus(projectId, "Worker: Thinking...");
396
  StateManager.appendSnapshotOnly(projectId, thought);
397
  },
398
  (chunk) => {
399
+ stopStatus();
400
  StateManager.setStatus(projectId, "Worker: Coding...");
401
  subResponse += chunk;
402
  StateManager.appendStream(projectId, chunk);
 
407
 
408
  StateManager.setStatus(projectId, "Finalizing...");
409
 
410
+ // 5. PERSISTENCE (Wait for DB)
411
  await StateManager.addHistory(projectId, 'worker', 'user', fullInput);
 
 
412
  await StateManager.addHistory(projectId, 'worker', 'model', responseText);
413
 
414
  await processAndQueueResponse(projectId, responseText, userId);
415
 
416
+ // 6. FINISH
417
  await StateManager.updateProject(projectId, { status: "idle" });
418
 
419
  if (diamondUsage > 0) await deductUserCredits(userId, diamondUsage, 'diamond');
 
586
  const overrideMsg = `[SYSTEM OVERRIDE]: ${instruction}`;
587
 
588
  let overrideText = "";
589
+ let stopStatus = startStatusLoop(projectId, 'worker');
590
 
591
  AIEngine.callWorkerStream(
592
  project.workerHistory,
593
  overrideMsg,
594
  (thought) => {
595
+ stopStatus();
596
  StateManager.setStatus(projectId, "Worker: Thinking...");
597
  StateManager.appendSnapshotOnly(projectId, thought);
598
  },
599
  (chunk) => {
600
+ stopStatus();
601
  StateManager.setStatus(projectId, "Worker: Coding...");
602
  overrideText += chunk;
603
  StateManager.appendStream(projectId, chunk);