@arcadialdev/arcality 2.6.2 → 2.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
package/scripts/gen-and-run.mjs
CHANGED
|
@@ -27,6 +27,29 @@ import {
|
|
|
27
27
|
// Load global config at startup (injects keys into process.env)
|
|
28
28
|
setupProcessEnv();
|
|
29
29
|
|
|
30
|
+
// ── WINDOWS FIX: Interceptar Ctrl+C antes que cmd.exe ──
|
|
31
|
+
// En Windows, `arcality` se ejecuta vía arcality.cmd (creado por npm para el campo bin).
|
|
32
|
+
// cmd.exe intercepta Ctrl+C y muestra "¿Desea terminar el trabajo por lotes (S/N)?".
|
|
33
|
+
// Al presionar S, mata el árbol de procesos antes de que nuestro finally pueda correr.
|
|
34
|
+
//
|
|
35
|
+
// La solución: setRawMode(true) hace que Node capture los bytes del teclado directamente,
|
|
36
|
+
// ANTES de que cmd.exe los procese. Al detectar Ctrl+C (byte 0x03) lo convertimos en
|
|
37
|
+
// un SIGINT de Node — cmd.exe nunca lo ve y nunca muestra el prompt.
|
|
38
|
+
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
39
|
+
try {
|
|
40
|
+
process.stdin.setRawMode(true);
|
|
41
|
+
process.stdin.resume();
|
|
42
|
+
process.stdin.setEncoding('utf8');
|
|
43
|
+
process.stdin.on('data', (key) => {
|
|
44
|
+
if (key === '\u0003') { // Ctrl+C
|
|
45
|
+
process.emit('SIGINT');
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
} catch (e) {
|
|
49
|
+
// Si falla (ej. en CI sin TTY), continuar sin raw mode
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
30
53
|
const __filename = fileURLToPath(import.meta.url);
|
|
31
54
|
const __dirname = path.dirname(__filename);
|
|
32
55
|
const PROJECT_ROOT = process.env.ARCALITY_ROOT || path.join(__dirname, "..");
|
|
@@ -317,8 +340,13 @@ function run(cmd, args, options = {}) {
|
|
|
317
340
|
|
|
318
341
|
// Use standard cross-platform spawn. When shell is false, Node's child_process
|
|
319
342
|
// properly wraps arguments with spaces in quotes natively for CreateProcessW on Windows.
|
|
343
|
+
//
|
|
344
|
+
// CRITICAL (Windows): stdin MUST be 'ignore' (not 'inherit') to prevent cmd.exe from
|
|
345
|
+
// showing the "¿Desea terminar el trabajo por lotes (S/N)?" prompt on Ctrl+C.
|
|
346
|
+
// When stdin is 'inherit', cmd.exe intercepts Ctrl+C at the console level and kills
|
|
347
|
+
// the entire process tree before our SIGINT handler can run, bypassing the finally block.
|
|
320
348
|
const p = spawn(cmd, args, {
|
|
321
|
-
stdio: isDebug ? ['
|
|
349
|
+
stdio: isDebug ? ['ignore', 'inherit', 'inherit'] : ['ignore', 'pipe', 'pipe'],
|
|
322
350
|
shell: false,
|
|
323
351
|
cwd,
|
|
324
352
|
env,
|
|
@@ -347,24 +375,26 @@ function run(cmd, args, options = {}) {
|
|
|
347
375
|
}
|
|
348
376
|
};
|
|
349
377
|
|
|
350
|
-
if (
|
|
351
|
-
|
|
352
|
-
}
|
|
353
|
-
if (!isDebug && p.stderr) {
|
|
378
|
+
if (p.stdout) p.stdout.on('data', handleData);
|
|
379
|
+
if (p.stderr) {
|
|
354
380
|
p.stderr.on('data', (data) => {
|
|
355
|
-
process.stderr.write(data);
|
|
381
|
+
if (isDebug) process.stderr.write(data);
|
|
382
|
+
else process.stderr.write(data);
|
|
356
383
|
outputBuffer += data.toString();
|
|
357
384
|
});
|
|
358
385
|
}
|
|
359
386
|
|
|
360
|
-
p.on('exit', (code) => {
|
|
387
|
+
p.on('exit', (code, signal) => {
|
|
361
388
|
const lastRunLog = path.join(LOGS_DIR, 'last-run.log');
|
|
362
389
|
try {
|
|
363
390
|
if (!fs.existsSync(LOGS_DIR)) fs.mkdirSync(LOGS_DIR, { recursive: true });
|
|
364
391
|
fs.writeFileSync(lastRunLog, outputBuffer);
|
|
365
392
|
} catch (e) { }
|
|
366
393
|
|
|
367
|
-
|
|
394
|
+
// code===0 → clean success
|
|
395
|
+
// code===null + signal → killed by signal (Ctrl+C) — treat as non-fatal so finally runs
|
|
396
|
+
// code===1 → test failed (Playwright returns 1 on test failure) — also non-fatal
|
|
397
|
+
if (code === 0 || signal !== null || code === 1) resolve();
|
|
368
398
|
else {
|
|
369
399
|
if (isDebug) console.error(chalk.red(`\n[DEBUG] Process exited with code ${code}`));
|
|
370
400
|
reject(new Error(`Process exited with code ${code}`));
|
|
@@ -785,6 +815,17 @@ async function main() {
|
|
|
785
815
|
try { s.stop(chalk.yellow('⚠️ Misión cancelada.')); } catch { /* ignore */ }
|
|
786
816
|
}
|
|
787
817
|
|
|
818
|
+
// Terminar el proceso hijo de Playwright directamente (Windows-safe)
|
|
819
|
+
if (activeChildProcess && !activeChildProcess.killed) {
|
|
820
|
+
try {
|
|
821
|
+
if (process.platform === 'win32') {
|
|
822
|
+
spawn('taskkill', ['/pid', String(activeChildProcess.pid), '/f', '/t'], { stdio: 'ignore', windowsHide: true });
|
|
823
|
+
} else {
|
|
824
|
+
activeChildProcess.kill('SIGTERM');
|
|
825
|
+
}
|
|
826
|
+
} catch { /* silencioso */ }
|
|
827
|
+
}
|
|
828
|
+
|
|
788
829
|
try {
|
|
789
830
|
const ctxDir = process.env.CONTEXT_DIR;
|
|
790
831
|
if (ctxDir && fs.existsSync(ctxDir)) {
|
|
@@ -850,14 +891,24 @@ async function main() {
|
|
|
850
891
|
const testFile = path.join(PROJECT_ROOT, 'tests', '_helpers', 'agentic-runner.spec.ts');
|
|
851
892
|
const configFile = path.join(PROJECT_ROOT, 'playwright.config.js');
|
|
852
893
|
|
|
853
|
-
// Resolve playwright CLI — CRITICAL
|
|
854
|
-
//
|
|
855
|
-
//
|
|
856
|
-
//
|
|
894
|
+
// Resolve playwright CLI — CRITICAL singleton alignment:
|
|
895
|
+
//
|
|
896
|
+
// @playwright/test uses `playwright` internally. When npm detects a version mismatch
|
|
897
|
+
// between the top-level `playwright` and what `@playwright/test` needs, it installs
|
|
898
|
+
// a NESTED copy at: @playwright/test/node_modules/playwright/
|
|
899
|
+
//
|
|
900
|
+
// Playwright's test runner tracks the active suite via a module-level singleton
|
|
901
|
+
// (currentSuite). If the CLI uses a DIFFERENT playwright instance than the one
|
|
902
|
+
// @playwright/test loaded, their singletons are out of sync → "did not expect test()
|
|
903
|
+
// to be called here".
|
|
857
904
|
//
|
|
858
|
-
//
|
|
859
|
-
//
|
|
860
|
-
//
|
|
905
|
+
// Fix: ALWAYS prefer the playwright nested INSIDE @playwright/test, because that is
|
|
906
|
+
// guaranteed to be the same instance that @playwright/test uses for its internals.
|
|
907
|
+
//
|
|
908
|
+
// Resolution priority:
|
|
909
|
+
// 1. <project>/@playwright/test/node_modules/playwright/cli.js ← nested (safe)
|
|
910
|
+
// 2. <project>/playwright/cli.js ← sibling top-level
|
|
911
|
+
// (repeated for ORIGINAL_CWD and PROJECT_ROOT)
|
|
861
912
|
let playwrightCli;
|
|
862
913
|
try {
|
|
863
914
|
const searchPaths = [ORIGINAL_CWD, PROJECT_ROOT];
|
|
@@ -875,14 +926,21 @@ async function main() {
|
|
|
875
926
|
|
|
876
927
|
if (!testRunnerPath) throw resolveError;
|
|
877
928
|
|
|
878
|
-
|
|
879
|
-
|
|
929
|
+
const testRunnerDir = path.dirname(testRunnerPath); // → .../node_modules/@playwright/test/
|
|
930
|
+
|
|
931
|
+
// Priority 1: playwright nested INSIDE @playwright/test — same singleton as the package uses
|
|
932
|
+
const nestedCli = path.join(testRunnerDir, 'node_modules', 'playwright', 'cli.js');
|
|
933
|
+
// Priority 2: playwright as a top-level sibling (clean installs, versions aligned)
|
|
934
|
+
const siblingCli = path.join(testRunnerDir, '..', '..', 'playwright', 'cli.js');
|
|
880
935
|
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
936
|
+
if (fs.existsSync(nestedCli)) {
|
|
937
|
+
playwrightCli = nestedCli;
|
|
938
|
+
} else if (fs.existsSync(siblingCli)) {
|
|
939
|
+
playwrightCli = siblingCli;
|
|
940
|
+
} else {
|
|
941
|
+
throw new Error(
|
|
942
|
+
`Playwright CLI not found. Tried:\n (nested) ${nestedCli}\n (sibling) ${siblingCli}`
|
|
943
|
+
);
|
|
886
944
|
}
|
|
887
945
|
|
|
888
946
|
if (process.argv.includes('--debug')) {
|
|
@@ -894,15 +952,15 @@ async function main() {
|
|
|
894
952
|
console.error(chalk.yellow('\nTip: Run `npm install @playwright/test` in your project directory.'));
|
|
895
953
|
_missionActive = false;
|
|
896
954
|
process.exit = _origProcessExit;
|
|
897
|
-
|
|
955
|
+
_origProcessExit(1);
|
|
898
956
|
}
|
|
899
957
|
|
|
900
|
-
|
|
901
958
|
const postProcessSigint = () => console.log(chalk.yellow('\n⏳ Finalizando tareas de reporte, por favor espera...'));
|
|
902
959
|
let missionResult = null;
|
|
903
960
|
let finalFailReason = null;
|
|
904
961
|
let finalUsageData = undefined;
|
|
905
962
|
let finalReportUrl = null;
|
|
963
|
+
let finalFailReasoning = null;
|
|
906
964
|
|
|
907
965
|
try {
|
|
908
966
|
// No test file path is passed — playwright.config.js uses testMatch:'agentic-runner.spec.ts'
|
|
@@ -928,7 +986,11 @@ async function main() {
|
|
|
928
986
|
if (userCanceledMission) {
|
|
929
987
|
missionResult = 'canceled';
|
|
930
988
|
finalFailReason = 'user_canceled';
|
|
931
|
-
|
|
989
|
+
// Detener el spinner explícitamente antes de salir
|
|
990
|
+
if (!process.argv.includes('--debug')) {
|
|
991
|
+
try { s.stop(chalk.yellow('⚠️ Misión cancelada por el usuario.')); } catch { }
|
|
992
|
+
}
|
|
993
|
+
return; // El handler ya tomó el control, finally se ejecutará
|
|
932
994
|
}
|
|
933
995
|
|
|
934
996
|
missionResult = 'success';
|
|
@@ -1008,6 +1070,7 @@ async function main() {
|
|
|
1008
1070
|
if (userCanceledMission) {
|
|
1009
1071
|
missionResult = 'canceled';
|
|
1010
1072
|
finalFailReason = 'user_canceled';
|
|
1073
|
+
finalFailReasoning = 'La misión fue detenida manualmente por el usuario (Ctrl+C) antes de completarse. No se trata de un fallo técnico del sistema ni del portal.';
|
|
1011
1074
|
await new Promise(r => setTimeout(r, 800));
|
|
1012
1075
|
}
|
|
1013
1076
|
|
|
@@ -1038,6 +1101,7 @@ async function main() {
|
|
|
1038
1101
|
const lastLog = path.join(ctxDir, logFiles[logFiles.length - 1]);
|
|
1039
1102
|
const logData = JSON.parse(fs.readFileSync(lastLog, 'utf8'));
|
|
1040
1103
|
finalUsageData = logData.usage || finalUsageData;
|
|
1104
|
+
finalFailReasoning = logData.fail_reasoning || null;
|
|
1041
1105
|
if (missionResult === 'failed' && !finalFailReason) {
|
|
1042
1106
|
finalFailReason = logData.fail_reason || "timeout_or_crash";
|
|
1043
1107
|
}
|
|
@@ -1045,43 +1109,51 @@ async function main() {
|
|
|
1045
1109
|
}
|
|
1046
1110
|
} catch(e) {}
|
|
1047
1111
|
|
|
1048
|
-
// ── Upload Evidence ──
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1112
|
+
// ── Upload Evidence (solo si NO fue cancelado por usuario) ──
|
|
1113
|
+
if (missionResult !== 'canceled') {
|
|
1114
|
+
try {
|
|
1115
|
+
const { getEvidenceSasToken } = await import('../src/arcalityClient.mjs');
|
|
1116
|
+
const { uploadEvidence } = await import('../src/EvidenceUploader.mjs');
|
|
1117
|
+
|
|
1118
|
+
const sasData = await getEvidenceSasToken(mission.mission_id, finalProjectId);
|
|
1119
|
+
if (process.argv.includes('--debug')) console.log(`[DEBUG] sasData received:`, sasData);
|
|
1120
|
+
|
|
1121
|
+
const actualSasUrl = sasData?.sasTokenUrl || sasData?.sasUrl || sasData?.sas_url;
|
|
1122
|
+
const actualBasePath = sasData?.basePath || sasData?.base_path;
|
|
1123
|
+
|
|
1124
|
+
if (sasData && actualSasUrl && actualBasePath) {
|
|
1125
|
+
if (!process.argv.includes('--debug')) {
|
|
1126
|
+
sRep.start('☁️ Uploading evidence to Azure...');
|
|
1127
|
+
}
|
|
1128
|
+
process.removeAllListeners('SIGINT');
|
|
1129
|
+
process.removeAllListeners('SIGTERM');
|
|
1130
|
+
process.on('SIGINT', postProcessSigint);
|
|
1131
|
+
process.on('SIGTERM', postProcessSigint);
|
|
1132
|
+
|
|
1133
|
+
await uploadEvidence(reportDir, actualSasUrl, actualBasePath);
|
|
1134
|
+
|
|
1135
|
+
try {
|
|
1136
|
+
const parsedSas = new URL(actualSasUrl);
|
|
1137
|
+
finalReportUrl = `${parsedSas.origin}${parsedSas.pathname}/${actualBasePath}/index.html`;
|
|
1138
|
+
} catch(e) {}
|
|
1139
|
+
|
|
1140
|
+
if (!process.argv.includes('--debug')) sRep.stop(chalk.green('✅ Evidence uploaded successfully.'));
|
|
1063
1141
|
}
|
|
1064
|
-
|
|
1065
|
-
process.
|
|
1066
|
-
process.
|
|
1067
|
-
process.on('SIGTERM', postProcessSigint);
|
|
1068
|
-
|
|
1069
|
-
await uploadEvidence(reportDir, actualSasUrl, actualBasePath);
|
|
1070
|
-
|
|
1071
|
-
// Construct the public URL
|
|
1072
|
-
try {
|
|
1073
|
-
const parsedSas = new URL(actualSasUrl);
|
|
1074
|
-
finalReportUrl = `${parsedSas.origin}${parsedSas.pathname}/${actualBasePath}/index.html`;
|
|
1075
|
-
} catch(e) {}
|
|
1076
|
-
|
|
1077
|
-
if (!process.argv.includes('--debug')) sRep.stop(chalk.green('✅ Evidence uploaded successfully.'));
|
|
1142
|
+
} catch (e) {
|
|
1143
|
+
if (process.argv.includes('--debug')) console.log(chalk.yellow(`⚠️ Could not upload evidence: ${e.message}`));
|
|
1144
|
+
if (!process.argv.includes('--debug')) sRep.stop(chalk.yellow('⚠️ Evidence upload failed.'));
|
|
1078
1145
|
}
|
|
1146
|
+
} else {
|
|
1147
|
+
console.log(chalk.gray(' >> Skipping evidence upload (mission was canceled).'));
|
|
1148
|
+
}
|
|
1079
1149
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1150
|
+
// ── End Mission — SIEMPRE se llama, independientemente de si la evidencia subió ──
|
|
1151
|
+
try {
|
|
1152
|
+
const { endMission } = await import('../src/arcalityClient.mjs');
|
|
1153
|
+
await endMission(mission.mission_id, missionResult || 'failed', finalUsageData, finalFailReason, finalReportUrl, finalFailReasoning);
|
|
1154
|
+
if (process.argv.includes('--debug')) console.log(chalk.gray(`[DEBUG] endMission called → result: ${missionResult}, failReason: ${finalFailReason}`));
|
|
1082
1155
|
} catch (e) {
|
|
1083
|
-
if (process.argv.includes('--debug')) console.log(chalk.yellow(`⚠️ Could not
|
|
1084
|
-
if (!process.argv.includes('--debug')) sRep.stop(chalk.yellow('⚠️ Evidence upload or end mission failed.'));
|
|
1156
|
+
if (process.argv.includes('--debug')) console.log(chalk.yellow(`⚠️ Could not end mission in backend: ${e.message}`));
|
|
1085
1157
|
}
|
|
1086
1158
|
|
|
1087
1159
|
if (globalReportProcess) {
|
|
@@ -1114,9 +1186,19 @@ async function main() {
|
|
|
1114
1186
|
process.env.ARCALITY_API_KEY
|
|
1115
1187
|
);
|
|
1116
1188
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1189
|
+
// Esperar Enter para regresar al menú
|
|
1190
|
+
// Usamos escucha directa de stdin (raw mode) porque @clack/text
|
|
1191
|
+
// no procesa Enter correctamente cuando setRawMode(true) está activo.
|
|
1192
|
+
console.log(chalk.gray('\n Press Enter to return to menu...'));
|
|
1193
|
+
await new Promise(resolve => {
|
|
1194
|
+
const onKey = (key) => {
|
|
1195
|
+
if (key === '\r' || key === '\n' || key === '\u0003') {
|
|
1196
|
+
process.stdin.off('data', onKey);
|
|
1197
|
+
if (key === '\u0003') process.emit('SIGINT'); // Ctrl+C = salir
|
|
1198
|
+
else resolve();
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
process.stdin.on('data', onKey);
|
|
1120
1202
|
});
|
|
1121
1203
|
|
|
1122
1204
|
if (skipMenu) {
|
package/src/arcalityClient.mjs
CHANGED
|
@@ -250,7 +250,7 @@ export async function startMission(prompt, targetUrl) {
|
|
|
250
250
|
* @param {'success'|'failed'|'cancelled'} result
|
|
251
251
|
* @param {object} usage - Optional usage statistics directly from the AI
|
|
252
252
|
*/
|
|
253
|
-
export async function endMission(missionId, result, usage = null, failReason = null, reportUrl = null) {
|
|
253
|
+
export async function endMission(missionId, result, usage = null, failReason = null, reportUrl = null, failReasoning = null) {
|
|
254
254
|
const apiBase = getEffectiveApiBase();
|
|
255
255
|
if (!apiBase || !missionId) return { ok: true };
|
|
256
256
|
|
|
@@ -262,7 +262,7 @@ export async function endMission(missionId, result, usage = null, failReason = n
|
|
|
262
262
|
'Content-Type': 'application/json',
|
|
263
263
|
'x-api-key': key
|
|
264
264
|
},
|
|
265
|
-
body: JSON.stringify({ result, usage, failReason, reportUrl })
|
|
265
|
+
body: JSON.stringify({ result, usage, failReason, failReasoning, reportUrl })
|
|
266
266
|
});
|
|
267
267
|
} catch { }
|
|
268
268
|
return { ok: true };
|
|
@@ -2810,6 +2810,7 @@ function captureValidationRule(errorMessage, context) {
|
|
|
2810
2810
|
let aiMarkedSuccess = false;
|
|
2811
2811
|
let hasCriticalError = false;
|
|
2812
2812
|
let failReason = "";
|
|
2813
|
+
let failReasoning = "";
|
|
2813
2814
|
let resultsSaved = false;
|
|
2814
2815
|
let isFinished = false;
|
|
2815
2816
|
let stepCount = 0;
|
|
@@ -2902,6 +2903,7 @@ function captureValidationRule(errorMessage, context) {
|
|
|
2902
2903
|
success: finalSuccess,
|
|
2903
2904
|
error: hasCriticalError,
|
|
2904
2905
|
fail_reason: failReason,
|
|
2906
|
+
fail_reasoning: failReasoning,
|
|
2905
2907
|
timestamp: Date.now()
|
|
2906
2908
|
};
|
|
2907
2909
|
memories.push(missionData);
|
|
@@ -2912,7 +2914,7 @@ function captureValidationRule(errorMessage, context) {
|
|
|
2912
2914
|
}
|
|
2913
2915
|
try {
|
|
2914
2916
|
const logPath = path2.join(contextDir, `agent-log-${Date.now()}.json`);
|
|
2915
|
-
fs2.writeFileSync(logPath, JSON.stringify({ prompt, history, steps: stepCount, success: finalSuccess, error: hasCriticalError, fail_reason: failReason, usage: totalMissionUsage, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
|
|
2917
|
+
fs2.writeFileSync(logPath, JSON.stringify({ prompt, history, steps: stepCount, success: finalSuccess, error: hasCriticalError, fail_reason: failReason, fail_reasoning: failReasoning, usage: totalMissionUsage, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
|
|
2916
2918
|
try {
|
|
2917
2919
|
fs2.appendFileSync(path2.join(contextDir, "arcality-history.log"), JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), mission: prompt.substring(0, 150), success: finalSuccess, steps: stepCount, usage: totalMissionUsage }) + "\n");
|
|
2918
2920
|
} catch (e) {
|
|
@@ -3248,6 +3250,8 @@ ${readableHistory}`;
|
|
|
3248
3250
|
console.log(`>>ARCALITY_STATUS>> \u{1F6D1} Error interno del Portal/Red detectado: "${networkError}"`);
|
|
3249
3251
|
hasCriticalError = true;
|
|
3250
3252
|
failReason = "portal_network_error";
|
|
3253
|
+
failReasoning = `El portal web fall\xF3 al intentar comunicarse con su backend (ej. una petici\xF3n XHR o Fetch fall\xF3 o dio timeout). El agente QA no tiene control sobre las ca\xEDdas del servidor.
|
|
3254
|
+
Detalles t\xE9cnicos capturados en la red: ${networkError}`;
|
|
3251
3255
|
isFinished = true;
|
|
3252
3256
|
history.push(`Turno ${stepCount}: \u{1F6D1} ERROR CR\xCDTICO DE SISTEMA/RED: ${networkError}. El portal fall\xF3 internamente al ejecutar una petici\xF3n XHR/Fetch.`);
|
|
3253
3257
|
saveMissionResults();
|
|
@@ -3258,6 +3262,8 @@ ${readableHistory}`;
|
|
|
3258
3262
|
console.log(`>>ARCALITY_STATUS>> \u{1F6D1} Error Fatal de JavaScript detectado: "${jsError}"`);
|
|
3259
3263
|
hasCriticalError = true;
|
|
3260
3264
|
failReason = "portal_js_crash";
|
|
3265
|
+
failReasoning = `La aplicaci\xF3n web sufri\xF3 una excepci\xF3n no manejada de JavaScript en el navegador, provocando que la p\xE1gina dejara de responder o funcionara de manera err\xE1tica.
|
|
3266
|
+
Detalles de la excepci\xF3n en consola: ${jsError}`;
|
|
3261
3267
|
isFinished = true;
|
|
3262
3268
|
history.push(`Turno ${stepCount}: \u{1F6D1} ERROR CR\xCDTICO DE APLICACI\xD3N: ${jsError}. El portal sufri\xF3 una excepci\xF3n no manejada y dej\xF3 de responder.`);
|
|
3263
3269
|
saveMissionResults();
|
|
@@ -3275,6 +3281,7 @@ ${readableHistory}`;
|
|
|
3275
3281
|
console.error(` >> Tipo: portal_internal_error | El agente NO puede corregir errores del servidor.`);
|
|
3276
3282
|
hasCriticalError = true;
|
|
3277
3283
|
failReason = "portal_internal_error";
|
|
3284
|
+
failReasoning = `El servidor devolvi\xF3 un error sist\xE9mico irrecuperable que fue mostrado en la interfaz visual: "${sysErrTxt.trim()}". El agente QA no puede reparar bugs l\xF3gicos o de infraestructura del servidor, por lo que la misi\xF3n fue detenida preventivamente.`;
|
|
3278
3285
|
isFinished = true;
|
|
3279
3286
|
history.push(`Turno ${stepCount}: \u{1F6D1} ERROR SIST\xC9MICO DEL PORTAL: "${sysErrTxt.trim().substring(0, 200)}". Este es un fallo del servidor, no un error de datos del agente. El portal devolvi\xF3 HTTP 200 pero report\xF3 un error interno en la UI.`);
|
|
3280
3287
|
saveMissionResults();
|
|
@@ -3359,7 +3366,24 @@ ${readableHistory}`;
|
|
|
3359
3366
|
const effectivePrompt = patternContext ? `${prompt}
|
|
3360
3367
|
|
|
3361
3368
|
${patternContext}` : prompt;
|
|
3362
|
-
|
|
3369
|
+
try {
|
|
3370
|
+
response = await agent.askIA(effectivePrompt, history, stepCount);
|
|
3371
|
+
} catch (err) {
|
|
3372
|
+
if (err.message?.includes("APPLICATION BUG:")) {
|
|
3373
|
+
const bugDesc = err.message.replace("APPLICATION BUG:", "").trim();
|
|
3374
|
+
console.log(`>>ARCALITY_STATUS>> \u{1F6D1} Application Bug reportado: ${bugDesc}`);
|
|
3375
|
+
aiMarkedSuccess = false;
|
|
3376
|
+
hasCriticalError = true;
|
|
3377
|
+
failReason = "application_bug";
|
|
3378
|
+
failReasoning = bugDesc;
|
|
3379
|
+
isFinished = true;
|
|
3380
|
+
history.push(`Turno ${stepCount}: \u{1F6D1} [APPLICATION BUG] ${bugDesc}`);
|
|
3381
|
+
saveMissionResults();
|
|
3382
|
+
break;
|
|
3383
|
+
} else {
|
|
3384
|
+
throw err;
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3363
3387
|
if (response.usage) {
|
|
3364
3388
|
const inputs = response.usage.input_tokens || 0;
|
|
3365
3389
|
const cacheCreates = response.usage.cache_creation_input_tokens || 0;
|
|
@@ -3381,13 +3405,17 @@ ${patternContext}` : prompt;
|
|
|
3381
3405
|
aiMarkedSuccess = false;
|
|
3382
3406
|
hasCriticalError = true;
|
|
3383
3407
|
failReason = "cost_limit_reached";
|
|
3408
|
+
failReasoning = response.thought;
|
|
3384
3409
|
}
|
|
3385
3410
|
}
|
|
3386
3411
|
}
|
|
3412
|
+
if (response && response.thought && response.thought.includes("DISCULPA:")) {
|
|
3413
|
+
failReasoning = response.thought.replace("DISCULPA:", "").trim();
|
|
3414
|
+
}
|
|
3387
3415
|
console.log(`
|
|
3388
3416
|
======================================================`);
|
|
3389
3417
|
console.log(`\u{1F916} [RAZONAMIENTO DEL AGENTE]`);
|
|
3390
|
-
console.log(`${response.thought}`);
|
|
3418
|
+
console.log(`${response ? response.thought : "N/A"}`);
|
|
3391
3419
|
console.log(`======================================================
|
|
3392
3420
|
`);
|
|
3393
3421
|
if (!response.finish && history.length >= 6) {
|
|
@@ -3417,6 +3445,8 @@ ${patternContext}` : prompt;
|
|
|
3417
3445
|
aiMarkedSuccess = false;
|
|
3418
3446
|
hasCriticalError = true;
|
|
3419
3447
|
failReason = "loop_detected";
|
|
3448
|
+
failReasoning = `El agente QA entr\xF3 en un bucle l\xF3gico intentando ejecutar la misma acci\xF3n repetidamente sin observar cambios significativos en el DOM.
|
|
3449
|
+
Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(uniqueTargets).join(", ")}.`;
|
|
3420
3450
|
} else if ((isClickLoop || isFillLoop) && agentRecovered) {
|
|
3421
3451
|
console.log(`>>ARCALITY_STATUS>> \u{1F504} Loop candidato detectado pero el agente se recuper\xF3 en este turno. Continuando misi\xF3n.`);
|
|
3422
3452
|
}
|
|
@@ -3441,6 +3471,7 @@ ${patternContext}` : prompt;
|
|
|
3441
3471
|
aiMarkedSuccess = false;
|
|
3442
3472
|
hasCriticalError = true;
|
|
3443
3473
|
failReason = "portal_rendering_failure";
|
|
3474
|
+
failReasoning = response.thought;
|
|
3444
3475
|
history.push(`Turno ${stepCount}: \u{1F6D1} [PORTAL_RENDER_FAILURE] La p\xE1gina ${blankUrl} carg\xF3 en blanco (${consecutiveWaits} esperas sin progreso). Los widgets no se renderizaron. Bug del portal detectado por Arcality QA. Evidencia: ${blankEvidencePath}`);
|
|
3445
3476
|
saveMissionResults();
|
|
3446
3477
|
}
|
|
@@ -4027,7 +4058,21 @@ Evidence: ${JSON.stringify(v.evidence).substring(0, 400)}`,
|
|
|
4027
4058
|
console.log(`>>ARCALITY_STATUS>> \u{1F4CA} Uso total de tokens esta misi\xF3n: IN=${totalMissionUsage.input_tokens} | OUT=${totalMissionUsage.output_tokens} | Cache writes=${totalMissionUsage.cache_creation_input_tokens} | Cache reads=${totalMissionUsage.cache_read_input_tokens}`);
|
|
4028
4059
|
await persistCollectiveMemory(true).catch(() => {
|
|
4029
4060
|
});
|
|
4030
|
-
|
|
4061
|
+
let finalErrorMsg = `Misi\xF3n no completada o finalizada con errores.
|
|
4062
|
+
|
|
4063
|
+
`;
|
|
4064
|
+
finalErrorMsg += `======================================================
|
|
4065
|
+
`;
|
|
4066
|
+
finalErrorMsg += `\u{1F3AF} TIPO DE FALLO: ${failReason}
|
|
4067
|
+
`;
|
|
4068
|
+
if (failReasoning) {
|
|
4069
|
+
finalErrorMsg += `\u{1F9E0} AN\xC1LISIS DEL AGENTE DE QA:
|
|
4070
|
+
${failReasoning}
|
|
4071
|
+
`;
|
|
4072
|
+
}
|
|
4073
|
+
finalErrorMsg += `======================================================
|
|
4074
|
+
`;
|
|
4075
|
+
throw new Error(finalErrorMsg);
|
|
4031
4076
|
} else {
|
|
4032
4077
|
console.log(`>>ARCALITY_STATUS>> \u{1F4CA} Uso total de tokens esta misi\xF3n: IN=${totalMissionUsage.input_tokens} | OUT=${totalMissionUsage.output_tokens} | Cache writes=${totalMissionUsage.cache_creation_input_tokens} | Cache reads=${totalMissionUsage.cache_read_input_tokens}`);
|
|
4033
4078
|
}
|