Update server.js
Browse files
server.js
CHANGED
|
@@ -952,7 +952,9 @@ COMMANDS FORMAT RULES β VIOLATIONS WILL CRASH THE SYSTEM:
|
|
| 952 |
- EVERY command must be a complete shell command runnable in bash
|
| 953 |
- NEVER include comments, explanations, or step labels inside the commands array
|
| 954 |
|
| 955 |
-
CRITICAL: done:true only when sed -n CONFIRMS the change is physically in the file, OR full_rewrite:true is set.
|
|
|
|
|
|
|
| 956 |
|
| 957 |
const contRaw = await callAI(continuePrompt, "", modelKey, transcript);
|
| 958 |
let cont = sanitizeAIResponse(parseJSON(contRaw));
|
|
@@ -1050,6 +1052,33 @@ CRITICAL: done:true only when sed -n CONFIRMS the change is physically in the fi
|
|
| 1050 |
if (cont.done || !cont.commands || cont.commands.length === 0) {
|
| 1051 |
// Double-check: are all tasks actually marked done?
|
| 1052 |
const stillPending = sessionTasks.filter(t => !t.done);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1053 |
if (stillPending.length === 0 || cont.done) {
|
| 1054 |
send("status", { text: cont.status || "All tasks complete β" });
|
| 1055 |
break;
|
|
@@ -1073,7 +1102,12 @@ CRITICAL: done:true only when sed -n CONFIRMS the change is physically in the fi
|
|
| 1073 |
fileContent,
|
| 1074 |
plan,
|
| 1075 |
sessionTasks,
|
| 1076 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1077 |
hadTeeWrites: allCommandResults.some(r => (r.cmd.startsWith("tee ") || r.cmd.includes(" > ")) && r.code === 0),
|
| 1078 |
hadFullRewrite: allCommandResults.some(r => r.cmd.startsWith("[full_rewrite]") && r.code === 0),
|
| 1079 |
};
|
|
@@ -1383,20 +1417,39 @@ RIGHT β content that is source code: "content": "const x = require('y');\\n\\n
|
|
| 1383 |
}
|
| 1384 |
fileContent = fs.existsSync(absPath) ? fs.readFileSync(absPath, "utf8") : fileContent;
|
| 1385 |
|
| 1386 |
-
|
| 1387 |
-
|
| 1388 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1389 |
modelKey,
|
| 1390 |
transcript
|
| 1391 |
);
|
| 1392 |
-
const
|
| 1393 |
-
|
|
|
|
| 1394 |
saveTasks(allTasks);
|
| 1395 |
send("task_update", { tasks: allTasks });
|
| 1396 |
versionFile(filePath);
|
| 1397 |
updateMeta({ last_ai_edit: new Date().toISOString() });
|
| 1398 |
-
send("file_updated", { filename: filePath, content: fileContent, description:
|
| 1399 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1400 |
|
| 1401 |
} else {
|
| 1402 |
// ββ No sed edits were made β ask AI for complete file content βββ
|
|
@@ -1453,15 +1506,31 @@ CURRENT FILE CONTENT (line numbers for reference):\n${
|
|
| 1453 |
}
|
| 1454 |
fileContent = fs.readFileSync(absPath, "utf8");
|
| 1455 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1456 |
const allTasks = [
|
| 1457 |
...existingTasks.map(t => ({ ...t, done: true })),
|
| 1458 |
-
...(plan.tasks || []).map(t => ({ ...t, done:
|
| 1459 |
];
|
| 1460 |
saveTasks(allTasks);
|
| 1461 |
updateMeta({ last_ai_edit: new Date().toISOString() });
|
| 1462 |
send("task_update", { tasks: allTasks });
|
| 1463 |
-
send("file_updated", { filename: filePath, content: fileContent, description: editParsed.description });
|
| 1464 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1465 |
send("tree_update", {});
|
| 1466 |
}
|
| 1467 |
}
|
|
|
|
| 952 |
- EVERY command must be a complete shell command runnable in bash
|
| 953 |
- NEVER include comments, explanations, or step labels inside the commands array
|
| 954 |
|
| 955 |
+
CRITICAL: done:true only when sed -n or grep CONFIRMS the change is physically in the file, OR full_rewrite:true is set.
|
| 956 |
+
NEVER set done:true based only on the fact that sed ran β sed exits 0 even when the pattern did not match.
|
| 957 |
+
MANDATORY last step before done:true: run grep -n to prove the new code exists in the file.`;
|
| 958 |
|
| 959 |
const contRaw = await callAI(continuePrompt, "", modelKey, transcript);
|
| 960 |
let cont = sanitizeAIResponse(parseJSON(contRaw));
|
|
|
|
| 1052 |
if (cont.done || !cont.commands || cont.commands.length === 0) {
|
| 1053 |
// Double-check: are all tasks actually marked done?
|
| 1054 |
const stillPending = sessionTasks.filter(t => !t.done);
|
| 1055 |
+
|
| 1056 |
+
// ββ MANDATORY GROUND-TRUTH CHECK βββββββββββββββββββββββββββββββββ
|
| 1057 |
+
// Never trust the AI's self-reported done:true without verifying
|
| 1058 |
+
// the actual file on disk. The AI cannot see its own sed output β
|
| 1059 |
+
// it only knows what it sent, not what actually landed.
|
| 1060 |
+
if (cont.done && stillPending.length === 0) {
|
| 1061 |
+
fileContent = fs.existsSync(absPath) ? fs.readFileSync(absPath, "utf8") : fileContent;
|
| 1062 |
+
const checkRaw = await callAI(
|
| 1063 |
+
`Check if the user's request is fully present in the file content.
|
| 1064 |
+
Return ONLY JSON: {"verified": true | false, "reason": "what is present or missing"}`,
|
| 1065 |
+
`REQUEST: "${message}"\nFILE CONTENT:\n${fileContent.substring(0, 3000)}`,
|
| 1066 |
+
modelKey,
|
| 1067 |
+
transcript
|
| 1068 |
+
);
|
| 1069 |
+
const check = parseJSON(checkRaw);
|
| 1070 |
+
if (check?.verified === false) {
|
| 1071 |
+
// AI claimed done but the change isn't in the file β keep going
|
| 1072 |
+
send("status", { text: `Verification failed: ${check.reason} β retrying...` });
|
| 1073 |
+
// Force one more round
|
| 1074 |
+
sessionTasks = sessionTasks.map(t => ({ ...t, done: false }));
|
| 1075 |
+
send("task_update", { tasks: sessionTasks });
|
| 1076 |
+
if (round < maxRounds) continue;
|
| 1077 |
+
}
|
| 1078 |
+
send("status", { text: cont.status || "All tasks complete β" });
|
| 1079 |
+
break;
|
| 1080 |
+
}
|
| 1081 |
+
|
| 1082 |
if (stillPending.length === 0 || cont.done) {
|
| 1083 |
send("status", { text: cont.status || "All tasks complete β" });
|
| 1084 |
break;
|
|
|
|
| 1102 |
fileContent,
|
| 1103 |
plan,
|
| 1104 |
sessionTasks,
|
| 1105 |
+
// A sed edit only "happened" if it ran AND produced output or was verified β exit 0 alone is not enough
|
| 1106 |
+
// because `sed -i` returns 0 even when the pattern didn't match anything on some systems.
|
| 1107 |
+
hadSedEdits: allCommandResults.some(r =>
|
| 1108 |
+
r.cmd.startsWith("sed -i") && r.code === 0 &&
|
| 1109 |
+
(r.structured?.linesChanged === "applied" || !r.stderr)
|
| 1110 |
+
),
|
| 1111 |
hadTeeWrites: allCommandResults.some(r => (r.cmd.startsWith("tee ") || r.cmd.includes(" > ")) && r.code === 0),
|
| 1112 |
hadFullRewrite: allCommandResults.some(r => r.cmd.startsWith("[full_rewrite]") && r.code === 0),
|
| 1113 |
};
|
|
|
|
| 1417 |
}
|
| 1418 |
fileContent = fs.existsSync(absPath) ? fs.readFileSync(absPath, "utf8") : fileContent;
|
| 1419 |
|
| 1420 |
+
// ββ GROUND-TRUTH VERIFICATION ββββββββββββββββββββββββββββββββββββ
|
| 1421 |
+
// Ask the AI to check the ACTUAL file content against the request.
|
| 1422 |
+
// This is the only honest source β not the command log.
|
| 1423 |
+
const verifyRaw = await callAI(
|
| 1424 |
+
`You are a code reviewer. Compare the user's request against the ACTUAL file content below.
|
| 1425 |
+
Determine if the request was fully implemented.
|
| 1426 |
+
Return ONLY JSON (no markdown):
|
| 1427 |
+
{
|
| 1428 |
+
"implemented": true | false,
|
| 1429 |
+
"summary": "one honest sentence: what changed, or what is MISSING if not implemented",
|
| 1430 |
+
"missing": "describe what is still missing, or null if fully done"
|
| 1431 |
+
}`,
|
| 1432 |
+
`ORIGINAL REQUEST: "${message}"
|
| 1433 |
+
ACTUAL FILE CONTENT (this is the real file on disk β trust this, not the command log):
|
| 1434 |
+
${fileContent.substring(0, 3500)}`,
|
| 1435 |
modelKey,
|
| 1436 |
transcript
|
| 1437 |
);
|
| 1438 |
+
const verify = parseJSON(verifyRaw);
|
| 1439 |
+
|
| 1440 |
+
const allTasks = [...existingTasks.map(t => ({ ...t, done: true })), ...(plan.tasks || []).map(t => ({ ...t, done: verify?.implemented !== false }))];
|
| 1441 |
saveTasks(allTasks);
|
| 1442 |
send("task_update", { tasks: allTasks });
|
| 1443 |
versionFile(filePath);
|
| 1444 |
updateMeta({ last_ai_edit: new Date().toISOString() });
|
| 1445 |
+
send("file_updated", { filename: filePath, content: fileContent, description: verify?.summary || "Updated" });
|
| 1446 |
+
|
| 1447 |
+
if (verify?.implemented === false) {
|
| 1448 |
+
// Change did NOT land β report honestly, do not claim success
|
| 1449 |
+
send("message", { text: `β οΈ Edit incomplete: ${verify.summary}${verify.missing ? ` β still missing: ${verify.missing}` : ""}` });
|
| 1450 |
+
} else {
|
| 1451 |
+
send("message", { text: verify?.summary || "Changes applied successfully." });
|
| 1452 |
+
}
|
| 1453 |
|
| 1454 |
} else {
|
| 1455 |
// ββ No sed edits were made β ask AI for complete file content βββ
|
|
|
|
| 1506 |
}
|
| 1507 |
fileContent = fs.readFileSync(absPath, "utf8");
|
| 1508 |
|
| 1509 |
+
// Verify honestly against real file
|
| 1510 |
+
const verifyFbRaw = await callAI(
|
| 1511 |
+
`You are a code reviewer. Check if the user's request is implemented in the actual file.
|
| 1512 |
+
Return ONLY JSON:
|
| 1513 |
+
{"implemented": true | false, "summary": "one honest sentence", "missing": "what is missing or null"}`,
|
| 1514 |
+
`REQUEST: "${message}"\nACTUAL FILE (trust this):\n${fileContent.substring(0, 3500)}`,
|
| 1515 |
+
modelKey,
|
| 1516 |
+
transcript
|
| 1517 |
+
);
|
| 1518 |
+
const verifyFb = parseJSON(verifyFbRaw);
|
| 1519 |
+
|
| 1520 |
const allTasks = [
|
| 1521 |
...existingTasks.map(t => ({ ...t, done: true })),
|
| 1522 |
+
...(plan.tasks || []).map(t => ({ ...t, done: verifyFb?.implemented !== false }))
|
| 1523 |
];
|
| 1524 |
saveTasks(allTasks);
|
| 1525 |
updateMeta({ last_ai_edit: new Date().toISOString() });
|
| 1526 |
send("task_update", { tasks: allTasks });
|
| 1527 |
+
send("file_updated", { filename: filePath, content: fileContent, description: verifyFb?.summary || editParsed.description });
|
| 1528 |
+
|
| 1529 |
+
if (verifyFb?.implemented === false) {
|
| 1530 |
+
send("message", { text: `β οΈ Edit incomplete: ${verifyFb.summary}${verifyFb.missing ? ` β still missing: ${verifyFb.missing}` : ""}` });
|
| 1531 |
+
} else {
|
| 1532 |
+
send("message", { text: verifyFb?.summary || editParsed.status || editParsed.description });
|
| 1533 |
+
}
|
| 1534 |
send("tree_update", {});
|
| 1535 |
}
|
| 1536 |
}
|