everydaycats commited on
Commit
8edba50
·
verified ·
1 Parent(s): b6fcea9

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +127 -87
app.js CHANGED
@@ -20,59 +20,76 @@ const validateRequest = (req, res, next) => {
20
  next();
21
  };
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  /**
24
- * 1. NEW PROJECT
25
- * PM Creates GDD -> PM Creates Isolated Worker Prompt -> Worker Starts
26
  */
27
  app.post('/new/project', validateRequest, async (req, res) => {
28
  const { userId, projectId, description } = req.body;
29
 
30
  try {
31
- // 1. PM: Generate GDD
32
  const pmHistory = [];
33
- const gddPrompt = `Create a GDD for: ${description}`;
34
  const gddResponse = await AIEngine.callPM(pmHistory, gddPrompt);
35
 
36
  pmHistory.push({ role: 'user', parts: [{ text: gddPrompt }] });
37
  pmHistory.push({ role: 'model', parts: [{ text: gddResponse }] });
38
 
39
- // 2. PM: Create First Isolated Task
40
- // We force the PM to extract just the necessary context for Task 1
41
- const taskGenPrompt = "Based on the GDD, define the first technical task. \nOutput format:\nTASK_NAME: ...\nWORKER_PROMPT: ... (Write this as a direct command to the worker. Do not include the full GDD, just the relevant specs for this task).";
42
- const taskResponse = await AIEngine.callPM(pmHistory, taskGenPrompt);
43
 
44
- pmHistory.push({ role: 'user', parts: [{ text: taskGenPrompt }] });
45
  pmHistory.push({ role: 'model', parts: [{ text: taskResponse }] });
46
 
47
- // 3. Parse PM Response to get Worker Prompt
48
- const workerInstruction = extractWorkerPrompt(taskResponse) || `Initialize project structure for: ${description}`;
49
 
50
  // 4. Initialize Worker (Fresh Scope)
51
  const workerHistory = [];
52
- const initialWorkerPrompt = `CONTEXT: None. \nINSTRUCTION: ${workerInstruction}`;
53
  const workerResponse = await AIEngine.callWorker(workerHistory, initialWorkerPrompt);
54
 
55
  workerHistory.push({ role: 'user', parts: [{ text: initialWorkerPrompt }] });
56
  workerHistory.push({ role: 'model', parts: [{ text: workerResponse }] });
57
 
58
- // Save State
59
  await StateManager.updateProject(projectId, {
60
- userId, pmHistory, workerHistory,
61
- gdd: gddResponse, failureCount: 0
 
 
 
62
  });
63
 
 
64
  StateManager.queueCommand(projectId, workerResponse);
65
- res.json({ success: true, message: "Project Started", workerInstruction });
 
66
 
67
  } catch (err) {
68
  console.error(err);
69
- res.status(500).json({ error: "Init Failed" });
70
  }
71
  });
72
 
73
  /**
74
- * 2. FEEDBACK LOOP
75
- * Handles Termination and Scope Isolation
76
  */
77
  app.post('/project/feedback', async (req, res) => {
78
  const { projectId, prompt, hierarchyContext, scriptContext, logContext, taskComplete } = req.body;
@@ -80,33 +97,32 @@ app.post('/project/feedback', async (req, res) => {
80
  const project = await StateManager.getProject(projectId);
81
  if (!project) return res.status(404).json({ error: "Project not found." });
82
 
83
- // --- A. TASK COMPLETE: PM Assigns Next Task ---
 
 
84
  if (taskComplete) {
85
- console.log(`[${projectId}] ✅ TASK COMPLETE. Calling PM.`);
86
-
87
- // 1. Tell PM the task is done
88
- const summary = `Worker completed the task. Logs: ${logContext?.logs || "Clean"}. Generate the NEXT task. \nREMEMBER: Output 'WORKER_PROMPT: ...' with isolated instructions.`;
89
 
 
 
90
  const pmResponse = await AIEngine.callPM(project.pmHistory, summary);
 
91
  project.pmHistory.push({ role: 'user', parts: [{ text: summary }] });
92
  project.pmHistory.push({ role: 'model', parts: [{ text: pmResponse }] });
93
 
94
- // 2. Parse new instruction
95
- const nextWorkerInstruction = extractWorkerPrompt(pmResponse);
96
-
97
- if (!nextWorkerInstruction) {
98
- // PM thinks project is done or failed to format
99
  await StateManager.updateProject(projectId, { pmHistory: project.pmHistory, status: "IDLE" });
100
- return res.json({ success: true, message: "Project Idle/Finished." });
101
  }
102
 
103
- // 3. NUKE WORKER (Scope Isolation)
104
- // We clear history and give the new standalone prompt
105
- console.log(`[${projectId}] 🧹 Nuking Worker for next task...`);
106
  const newWorkerHistory = [];
107
- const newPrompt = `New Objective: ${nextWorkerInstruction}`;
108
-
109
  const workerResponse = await AIEngine.callWorker(newWorkerHistory, newPrompt);
 
110
  newWorkerHistory.push({ role: 'user', parts: [{ text: newPrompt }] });
111
  newWorkerHistory.push({ role: 'model', parts: [{ text: workerResponse }] });
112
 
@@ -116,71 +132,79 @@ app.post('/project/feedback', async (req, res) => {
116
  failureCount: 0
117
  });
118
 
119
- // Send Clean Console command + Code
120
  StateManager.queueCommand(projectId, "CLEAR_CONSOLE");
121
  StateManager.queueCommand(projectId, workerResponse);
122
 
123
  return res.json({ success: true, message: "Next Task Assigned" });
124
  }
125
 
126
- // --- B. ERROR HANDLING & PM INTERVENTION ---
 
 
127
  let isFailure = false;
128
- if (logContext?.logs && ["Error", "Exception", "failed"].some(k => logContext.logs.includes(k))) {
129
- isFailure = true;
130
- project.failureCount = (project.failureCount || 0) + 1;
 
 
 
131
  }
132
 
133
- // Standard Feedback Context
134
- const fullInput = `USER: ${prompt || "Auto-Feedback"}` + formatContext({ hierarchyContext, scriptContext, logContext });
135
-
136
- // Check for Escalation (Failure > 2)
137
  if (project.failureCount > 2) {
138
- console.log(`[${projectId}] 🚨 Escalating to PM...`);
139
 
140
- // Ask PM for judgment
141
- const pmGuidancePrompt = sysPrompts.pm_guidance_prompt.replace('{{LOGS}}', logContext?.logs);
142
- const pmVerdict = await AIEngine.callPM(project.pmHistory, pmGuidancePrompt);
143
 
144
- // CHECK FOR TERMINATION ORDER
145
  if (pmVerdict.includes("[TERMINATE]")) {
146
- console.log(`[${projectId}] 💀 PM Ordered Termination.`);
147
 
148
- // Extract the fix from PM's verdict to use as the new 'Initial Prompt'
149
  const fixInstruction = pmVerdict.replace("[TERMINATE]", "").trim();
150
 
151
- // Reset Worker completely
152
  const resetHistory = [];
153
- const resetPrompt = `[SYSTEM]: You were terminated for incompetence. \nNew Objective: ${fixInstruction}`;
154
-
155
  const workerResp = await AIEngine.callWorker(resetHistory, resetPrompt);
 
156
  resetHistory.push({ role: 'user', parts: [{ text: resetPrompt }] });
157
  resetHistory.push({ role: 'model', parts: [{ text: workerResp }] });
158
 
159
- await StateManager.updateProject(projectId, {
160
- workerHistory: resetHistory,
161
- failureCount: 0
162
- });
163
-
164
  StateManager.queueCommand(projectId, "CLEAR_CONSOLE");
165
  StateManager.queueCommand(projectId, workerResp);
 
166
  return res.json({ success: true, message: "Worker Terminated & Replaced." });
167
- } else {
168
- // Standard PM Guidance (Injection)
169
- const injection = `[PM GUIDANCE]: ${pmVerdict} \n\nFix your code immediately.`;
 
 
170
  const workerResp = await AIEngine.callWorker(project.workerHistory, injection);
171
 
172
  project.workerHistory.push({ role: 'user', parts: [{ text: injection }] });
173
  project.workerHistory.push({ role: 'model', parts: [{ text: workerResp }] });
174
 
175
- await StateManager.updateProject(projectId, { workerHistory: project.workerHistory, failureCount: 0 }); // Reset count after help
 
176
  await StateManager.queueCommand(projectId, workerResp);
177
- return res.json({ success: true, message: "PM Guidance Sent." });
 
178
  }
179
  }
180
 
181
- // --- C. STANDARD LOOP (Worker iterates) ---
 
 
182
  try {
183
  const response = await AIEngine.callWorker(project.workerHistory, fullInput);
 
184
  project.workerHistory.push({ role: 'user', parts: [{ text: fullInput }] });
185
  project.workerHistory.push({ role: 'model', parts: [{ text: response }] });
186
 
@@ -193,21 +217,24 @@ app.post('/project/feedback', async (req, res) => {
193
  res.json({ success: true });
194
 
195
  } catch (err) {
196
- console.error(err);
197
  res.status(500).json({ error: "AI Failed" });
198
  }
199
  });
200
 
201
- // --- HUMAN OVERRIDE (Preserved) ---
 
 
202
  app.post('/human/override', validateRequest, async (req, res) => {
203
  const { projectId, instruction, pruneHistory } = req.body;
204
  const project = await StateManager.getProject(projectId);
205
 
206
  const overrideMsg = `[SYSTEM OVERRIDE]: ${instruction}`;
207
 
208
- // If we prune, we remove the last error context so the AI isn't confused
209
- if (pruneHistory && project.workerHistory.length > 2) {
210
- project.workerHistory = project.workerHistory.slice(0, -2);
 
211
  }
212
 
213
  const response = await AIEngine.callWorker(project.workerHistory, overrideMsg);
@@ -216,16 +243,40 @@ app.post('/human/override', validateRequest, async (req, res) => {
216
 
217
  await StateManager.updateProject(projectId, { workerHistory: project.workerHistory });
218
  await StateManager.queueCommand(projectId, response);
219
- res.json({ success: true });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  });
221
 
 
 
 
222
  app.post('/project/ping', async (req, res) => {
223
  const { projectId } = req.body;
224
  const command = await StateManager.popCommand(projectId);
 
225
  if (command) {
226
- // If the command string is exactly "CLEAR_CONSOLE", send special action
227
  if (command.payload === "CLEAR_CONSOLE") {
228
- res.json({ action: "CLEAR_LOGS" });
229
  } else {
230
  res.json({
231
  action: command.type,
@@ -238,17 +289,6 @@ app.post('/project/ping', async (req, res) => {
238
  }
239
  });
240
 
241
- // Helper to extract "WORKER_PROMPT:" from PM text
242
- function extractWorkerPrompt(text) {
243
- const match = text.match(/WORKER_PROMPT:\s*(.*)/s);
244
- return match ? match[1].trim() : null;
245
- }
246
-
247
- function formatContext({ hierarchyContext, scriptContext, logContext }) {
248
- let out = "";
249
- if (scriptContext) out += `\n[SCRIPT]: ${scriptContext.targetName}`;
250
- if (logContext) out += `\n[LOGS]: ${logContext.logs}`;
251
- return out;
252
- }
253
-
254
- app.listen(PORT, () => { console.log(`Backend Running on ${PORT}`); });
 
20
  next();
21
  };
22
 
23
+ // Helper to extract PM's isolated instruction
24
+ function extractWorkerPrompt(text) {
25
+ const match = text.match(/WORKER_PROMPT:\s*(.*)/s);
26
+ return match ? match[1].trim() : null;
27
+ }
28
+
29
+ // Helper to format context for the AI
30
+ function formatContext({ hierarchyContext, scriptContext, logContext }) {
31
+ let out = "";
32
+ if (scriptContext) out += `\n[TARGET SCRIPT]: ${scriptContext.targetName}\n[SOURCE PREVIEW]: ${scriptContext.scriptSource.substring(0, 500)}...`;
33
+ if (logContext) out += `\n[LAST LOGS]: ${logContext.logs}`;
34
+ return out;
35
+ }
36
+
37
  /**
38
+ * 1. NEW PROJECT (Restored Full Logic)
39
+ * Flow: PM (GDD) -> PM (Tasks) -> Worker (Task 1 Isolated)
40
  */
41
  app.post('/new/project', validateRequest, async (req, res) => {
42
  const { userId, projectId, description } = req.body;
43
 
44
  try {
45
+ // 1. Generate GDD
46
  const pmHistory = [];
47
+ const gddPrompt = `Create a comprehensive GDD for: ${description}`;
48
  const gddResponse = await AIEngine.callPM(pmHistory, gddPrompt);
49
 
50
  pmHistory.push({ role: 'user', parts: [{ text: gddPrompt }] });
51
  pmHistory.push({ role: 'model', parts: [{ text: gddResponse }] });
52
 
53
+ // 2. Generate Initial Task List & Extract First Prompt
54
+ const taskPrompt = "Based on the GDD, generate the first technical milestone.\nOutput format:\nTASK_NAME: <Name>\nWORKER_PROMPT: <Specific, isolated instructions for the worker>";
55
+ const taskResponse = await AIEngine.callPM(pmHistory, taskPrompt);
 
56
 
57
+ pmHistory.push({ role: 'user', parts: [{ text: taskPrompt }] });
58
  pmHistory.push({ role: 'model', parts: [{ text: taskResponse }] });
59
 
60
+ // 3. Extract the Isolated Prompt
61
+ const initialWorkerInstruction = extractWorkerPrompt(taskResponse) || `Initialize structure for: ${description}`;
62
 
63
  // 4. Initialize Worker (Fresh Scope)
64
  const workerHistory = [];
65
+ const initialWorkerPrompt = `CONTEXT: None (New Project). \nINSTRUCTION: ${initialWorkerInstruction}`;
66
  const workerResponse = await AIEngine.callWorker(workerHistory, initialWorkerPrompt);
67
 
68
  workerHistory.push({ role: 'user', parts: [{ text: initialWorkerPrompt }] });
69
  workerHistory.push({ role: 'model', parts: [{ text: workerResponse }] });
70
 
71
+ // 5. Save State
72
  await StateManager.updateProject(projectId, {
73
+ userId,
74
+ pmHistory,
75
+ workerHistory,
76
+ gdd: gddResponse,
77
+ failureCount: 0
78
  });
79
 
80
+ // Queue for Plugin
81
  StateManager.queueCommand(projectId, workerResponse);
82
+
83
+ res.json({ success: true, message: "Project Initialized", gddPreview: gddResponse.substring(0, 200) });
84
 
85
  } catch (err) {
86
  console.error(err);
87
+ res.status(500).json({ error: "Initialization Failed" });
88
  }
89
  });
90
 
91
  /**
92
+ * 2. FEEDBACK LOOP (Restored Escalation & Termination)
 
93
  */
94
  app.post('/project/feedback', async (req, res) => {
95
  const { projectId, prompt, hierarchyContext, scriptContext, logContext, taskComplete } = req.body;
 
97
  const project = await StateManager.getProject(projectId);
98
  if (!project) return res.status(404).json({ error: "Project not found." });
99
 
100
+ // ==========================================
101
+ // A. TASK COMPLETE -> SCOPE SWITCH
102
+ // ==========================================
103
  if (taskComplete) {
104
+ console.log(`[${projectId}] ✅ TASK COMPLETE.`);
 
 
 
105
 
106
+ // 1. Report to PM
107
+ const summary = `Worker completed the previous task. Logs: ${logContext?.logs || "Clean"}. \nGenerate the NEXT task using 'WORKER_PROMPT:' format.`;
108
  const pmResponse = await AIEngine.callPM(project.pmHistory, summary);
109
+
110
  project.pmHistory.push({ role: 'user', parts: [{ text: summary }] });
111
  project.pmHistory.push({ role: 'model', parts: [{ text: pmResponse }] });
112
 
113
+ // 2. Get Next Instruction
114
+ const nextInstruction = extractWorkerPrompt(pmResponse);
115
+ if (!nextInstruction) {
 
 
116
  await StateManager.updateProject(projectId, { pmHistory: project.pmHistory, status: "IDLE" });
117
+ return res.json({ success: true, message: "No further tasks generated. Project Idle." });
118
  }
119
 
120
+ // 3. NUKE WORKER (Scope Reset)
121
+ console.log(`[${projectId}] 🧹 Nuking Worker for next task.`);
 
122
  const newWorkerHistory = [];
123
+ const newPrompt = `New Objective: ${nextInstruction}`;
 
124
  const workerResponse = await AIEngine.callWorker(newWorkerHistory, newPrompt);
125
+
126
  newWorkerHistory.push({ role: 'user', parts: [{ text: newPrompt }] });
127
  newWorkerHistory.push({ role: 'model', parts: [{ text: workerResponse }] });
128
 
 
132
  failureCount: 0
133
  });
134
 
135
+ // Clear Console Command + New Code
136
  StateManager.queueCommand(projectId, "CLEAR_CONSOLE");
137
  StateManager.queueCommand(projectId, workerResponse);
138
 
139
  return res.json({ success: true, message: "Next Task Assigned" });
140
  }
141
 
142
+ // ==========================================
143
+ // B. ERROR HANDLING & ESCALATION
144
+ // ==========================================
145
  let isFailure = false;
146
+ if (logContext?.logs) {
147
+ const errs = ["Error", "Exception", "failed", "Stack Begin"];
148
+ if (errs.some(k => logContext.logs.includes(k))) {
149
+ isFailure = true;
150
+ project.failureCount = (project.failureCount || 0) + 1;
151
+ }
152
  }
153
 
154
+ // Prepare Prompt
155
+ const fullInput = `USER: ${prompt || "Automatic Feedback"}` + formatContext({ hierarchyContext, scriptContext, logContext });
156
+
157
+ // --- ESCALATION LOGIC (Restored) ---
158
  if (project.failureCount > 2) {
159
+ console.log(`[${projectId}] 🚨 Escalating to PM (Failures: ${project.failureCount})...`);
160
 
161
+ // 1. Ask PM for solution
162
+ const pmPrompt = sysPrompts.pm_guidance_prompt.replace('{{LOGS}}', logContext?.logs);
163
+ const pmVerdict = await AIEngine.callPM(project.pmHistory, pmPrompt);
164
 
165
+ // 2. CHECK: Did PM order termination?
166
  if (pmVerdict.includes("[TERMINATE]")) {
167
+ console.log(`[${projectId}] 💀 PM TERMINATED WORKER.`);
168
 
 
169
  const fixInstruction = pmVerdict.replace("[TERMINATE]", "").trim();
170
 
171
+ // Hard Reset
172
  const resetHistory = [];
173
+ const resetPrompt = `[SYSTEM]: Previous worker terminated. \nNew Objective: ${fixInstruction}`;
 
174
  const workerResp = await AIEngine.callWorker(resetHistory, resetPrompt);
175
+
176
  resetHistory.push({ role: 'user', parts: [{ text: resetPrompt }] });
177
  resetHistory.push({ role: 'model', parts: [{ text: workerResp }] });
178
 
179
+ await StateManager.updateProject(projectId, { workerHistory: resetHistory, failureCount: 0 });
 
 
 
 
180
  StateManager.queueCommand(projectId, "CLEAR_CONSOLE");
181
  StateManager.queueCommand(projectId, workerResp);
182
+
183
  return res.json({ success: true, message: "Worker Terminated & Replaced." });
184
+ }
185
+ else {
186
+ // 3. Just Guidance (Soft Fix)
187
+ console.log(`[${projectId}] 💡 PM Provided Guidance.`);
188
+ const injection = `[PM GUIDANCE]: ${pmVerdict} \n\nApply this fix now.`;
189
  const workerResp = await AIEngine.callWorker(project.workerHistory, injection);
190
 
191
  project.workerHistory.push({ role: 'user', parts: [{ text: injection }] });
192
  project.workerHistory.push({ role: 'model', parts: [{ text: workerResp }] });
193
 
194
+ // Reset count slightly so we don't spam PM, but keep watch
195
+ await StateManager.updateProject(projectId, { workerHistory: project.workerHistory, failureCount: 0 });
196
  await StateManager.queueCommand(projectId, workerResp);
197
+
198
+ return res.json({ success: true, message: "PM Guidance Applied." });
199
  }
200
  }
201
 
202
+ // ==========================================
203
+ // C. STANDARD WORKER LOOP
204
+ // ==========================================
205
  try {
206
  const response = await AIEngine.callWorker(project.workerHistory, fullInput);
207
+
208
  project.workerHistory.push({ role: 'user', parts: [{ text: fullInput }] });
209
  project.workerHistory.push({ role: 'model', parts: [{ text: response }] });
210
 
 
217
  res.json({ success: true });
218
 
219
  } catch (err) {
220
+ console.error("AI Error:", err);
221
  res.status(500).json({ error: "AI Failed" });
222
  }
223
  });
224
 
225
+ /**
226
+ * 3. HUMAN OVERRIDE (God Mode)
227
+ */
228
  app.post('/human/override', validateRequest, async (req, res) => {
229
  const { projectId, instruction, pruneHistory } = req.body;
230
  const project = await StateManager.getProject(projectId);
231
 
232
  const overrideMsg = `[SYSTEM OVERRIDE]: ${instruction}`;
233
 
234
+ // Optional: Prune the argument if they are stuck
235
+ if (pruneHistory && project.workerHistory.length >= 2) {
236
+ project.workerHistory.pop(); // last model
237
+ project.workerHistory.pop(); // last user
238
  }
239
 
240
  const response = await AIEngine.callWorker(project.workerHistory, overrideMsg);
 
243
 
244
  await StateManager.updateProject(projectId, { workerHistory: project.workerHistory });
245
  await StateManager.queueCommand(projectId, response);
246
+ res.json({ success: true, message: "Override Executed" });
247
+ });
248
+
249
+ /**
250
+ * 4. WORKER KILL (Manual Reassignment)
251
+ */
252
+ app.post('/worker/kill', validateRequest, async (req, res) => {
253
+ const { projectId } = req.body;
254
+ // Logic: Just reset the history with a "Try again" prompt
255
+ const project = await StateManager.getProject(projectId);
256
+
257
+ const rebootPrompt = "[SYSTEM]: Worker process restarted. Attempt the task again from scratch.";
258
+ const response = await AIEngine.callWorker([{ role: 'user', parts: [{ text: rebootPrompt }] }], rebootPrompt);
259
+
260
+ await StateManager.updateProject(projectId, {
261
+ workerHistory: [{ role: 'user', parts: [{ text: rebootPrompt }] }], // Nuke history
262
+ workerHistory: [{ role: 'model', parts: [{ text: response }] }],
263
+ failureCount: 0
264
+ });
265
+
266
+ StateManager.queueCommand(projectId, response);
267
+ res.json({ success: true, message: "Worker Killed & Restarted" });
268
  });
269
 
270
+ /**
271
+ * 5. PING (Plugin Polling)
272
+ */
273
  app.post('/project/ping', async (req, res) => {
274
  const { projectId } = req.body;
275
  const command = await StateManager.popCommand(projectId);
276
+
277
  if (command) {
 
278
  if (command.payload === "CLEAR_CONSOLE") {
279
+ res.json({ action: "CLEAR_LOGS" });
280
  } else {
281
  res.json({
282
  action: command.type,
 
289
  }
290
  });
291
 
292
+ app.listen(PORT, () => {
293
+ console.log(`AI Backend Running on ${PORT}`);
294
+ });