@arcadialdev/arcality 3.0.0 → 3.0.2

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.
@@ -815,8 +815,8 @@ async function scan_sensitive_data_exposure(page) {
815
815
  }
816
816
 
817
817
  // tests/_helpers/ai-agent-helper.ts
818
- var fs3 = __toESM(require("fs"));
819
- var path3 = __toESM(require("path"));
818
+ var fs4 = __toESM(require("fs"));
819
+ var path4 = __toESM(require("path"));
820
820
 
821
821
  // src/KnowledgeService.ts
822
822
  var crypto = __toESM(require("crypto"));
@@ -1263,6 +1263,189 @@ async function getKnownFieldIdentifiers(pathUrl) {
1263
1263
  }
1264
1264
  }
1265
1265
 
1266
+ // tests/_helpers/qa-context.ts
1267
+ var fs = __toESM(require("fs"));
1268
+ var path = __toESM(require("path"));
1269
+ var RECOGNIZED_SECTION_PATTERNS = [
1270
+ /reglas?.*(dominio|negocio)|dominio|negocio|general/i,
1271
+ /navegacion|ui|interfaz|flujo/i,
1272
+ /prohibid|anti-patron|anti patron|nunca/i,
1273
+ /datos|credenciales|usuarios?|cuentas?/i,
1274
+ /resultado|validacion|assert|exito|esperad/i
1275
+ ];
1276
+ var PLACEHOLDER_PATTERNS = [
1277
+ /^ejemplo:/i,
1278
+ /^\(ejemplo/i,
1279
+ /^reemplaza /i,
1280
+ /^describe /i,
1281
+ /^agrega /i,
1282
+ /^completa /i,
1283
+ /^escribe /i,
1284
+ /^si no aplica/i,
1285
+ /^borra /i,
1286
+ /^pendiente$/i,
1287
+ /^\.\.\.$/
1288
+ ];
1289
+ function stripHtmlComments(content) {
1290
+ return content.replace(/<!--[\s\S]*?-->/g, "");
1291
+ }
1292
+ function normalizeLine(line) {
1293
+ return line.replace(/\t/g, " ").trim();
1294
+ }
1295
+ function isBulletLine(line) {
1296
+ return /^[-*+]\s+/.test(line) || /^\d+\.\s+/.test(line);
1297
+ }
1298
+ function toBulletText(line) {
1299
+ return line.replace(/^[-*+]\s+/, "").replace(/^\d+\.\s+/, "").trim();
1300
+ }
1301
+ function isPlaceholderRule(rule) {
1302
+ const normalized = normalizeLine(rule);
1303
+ if (!normalized)
1304
+ return true;
1305
+ return PLACEHOLDER_PATTERNS.some((pattern) => pattern.test(normalized));
1306
+ }
1307
+ function isRecognizedSection(title) {
1308
+ return RECOGNIZED_SECTION_PATTERNS.some((pattern) => pattern.test(title));
1309
+ }
1310
+ function analyzeQaContext(projectRoot = process.cwd()) {
1311
+ const filePath = path.join(projectRoot, ".arcality", "qa-context.md");
1312
+ const emptyResult = {
1313
+ filePath,
1314
+ exists: false,
1315
+ rawContent: "",
1316
+ sanitizedContent: "",
1317
+ sections: [],
1318
+ ruleCount: 0,
1319
+ sectionCount: 0,
1320
+ recognizedSectionCount: 0,
1321
+ ignoredExampleCount: 0,
1322
+ warnings: [],
1323
+ status: "missing",
1324
+ isValid: false
1325
+ };
1326
+ if (!fs.existsSync(filePath)) {
1327
+ return emptyResult;
1328
+ }
1329
+ const rawContent = fs.readFileSync(filePath, "utf8");
1330
+ const cleanedContent = stripHtmlComments(rawContent);
1331
+ const lines = cleanedContent.split(/\r?\n/);
1332
+ const warnings = [];
1333
+ const sections = [];
1334
+ let currentSection = null;
1335
+ let ignoredExampleCount = 0;
1336
+ const openSection = (title) => {
1337
+ currentSection = {
1338
+ title: title.trim(),
1339
+ recognized: isRecognizedSection(title),
1340
+ rules: []
1341
+ };
1342
+ sections.push(currentSection);
1343
+ };
1344
+ for (const rawLine of lines) {
1345
+ const line = normalizeLine(rawLine);
1346
+ if (!line)
1347
+ continue;
1348
+ if (/^##\s+/.test(line)) {
1349
+ openSection(line.replace(/^##\s+/, "").trim());
1350
+ continue;
1351
+ }
1352
+ if (!isBulletLine(line))
1353
+ continue;
1354
+ if (!currentSection) {
1355
+ openSection("Reglas sin seccion");
1356
+ }
1357
+ const rule = toBulletText(line);
1358
+ if (isPlaceholderRule(rule)) {
1359
+ ignoredExampleCount += 1;
1360
+ continue;
1361
+ }
1362
+ if (currentSection) {
1363
+ currentSection.rules.push(rule);
1364
+ }
1365
+ }
1366
+ const nonEmptySections = sections.filter((section) => section.rules.length > 0);
1367
+ const ruleCount = nonEmptySections.reduce((total, section) => total + section.rules.length, 0);
1368
+ const recognizedSectionCount = nonEmptySections.filter((section) => section.recognized).length;
1369
+ if (!rawContent.trim()) {
1370
+ warnings.push("El archivo existe pero esta vacio.");
1371
+ }
1372
+ if (sections.length === 0 && rawContent.trim()) {
1373
+ warnings.push('No se detectaron secciones markdown con encabezados "##".');
1374
+ }
1375
+ if (ruleCount === 0) {
1376
+ if (ignoredExampleCount > 0) {
1377
+ warnings.push("Solo se detectaron ejemplos o placeholders. Reemplaza los bullets de ejemplo por reglas reales.");
1378
+ } else {
1379
+ warnings.push("No se detectaron reglas accionables. Agrega al menos un bullet real en alguna seccion.");
1380
+ }
1381
+ }
1382
+ if (ruleCount > 0 && recognizedSectionCount === 0) {
1383
+ warnings.push("Hay reglas, pero ninguna cae en las secciones sugeridas. Revisa la estructura del archivo.");
1384
+ }
1385
+ const sanitizedContent = nonEmptySections.map((section) => {
1386
+ const rules = section.rules.map((rule) => `- ${rule}`).join("\n");
1387
+ return `## ${section.title}
1388
+ ${rules}`;
1389
+ }).join("\n\n");
1390
+ let status = "ready";
1391
+ if (!rawContent.trim())
1392
+ status = "empty";
1393
+ else if (ruleCount === 0 && ignoredExampleCount > 0)
1394
+ status = "template-only";
1395
+ else if (warnings.length > 0)
1396
+ status = "warning";
1397
+ return {
1398
+ filePath,
1399
+ exists: true,
1400
+ rawContent,
1401
+ sanitizedContent,
1402
+ sections: nonEmptySections,
1403
+ ruleCount,
1404
+ sectionCount: nonEmptySections.length,
1405
+ recognizedSectionCount,
1406
+ ignoredExampleCount,
1407
+ warnings,
1408
+ status,
1409
+ isValid: ruleCount > 0
1410
+ };
1411
+ }
1412
+ function loadQaContextForPrompt(projectRoot = process.cwd()) {
1413
+ const analysis = analyzeQaContext(projectRoot);
1414
+ return {
1415
+ analysis,
1416
+ promptBlock: analysis.isValid ? analysis.sanitizedContent : ""
1417
+ };
1418
+ }
1419
+ function formatQaContextSummary(analysis) {
1420
+ const lines = [];
1421
+ lines.push("QA Context Summary");
1422
+ lines.push(`Status: ${analysis.status}`);
1423
+ lines.push(`Path: ${analysis.filePath}`);
1424
+ lines.push(`Rules loaded: ${analysis.ruleCount}`);
1425
+ lines.push(`Sections with rules: ${analysis.sectionCount}`);
1426
+ lines.push(`Recognized sections: ${analysis.recognizedSectionCount}`);
1427
+ lines.push(`Ignored examples/placeholders: ${analysis.ignoredExampleCount}`);
1428
+ if (analysis.sections.length > 0) {
1429
+ lines.push("");
1430
+ lines.push("Sections applied:");
1431
+ for (const section of analysis.sections) {
1432
+ lines.push(`- ${section.title}: ${section.rules.length} rule(s)`);
1433
+ }
1434
+ }
1435
+ if (analysis.warnings.length > 0) {
1436
+ lines.push("");
1437
+ lines.push("Warnings:");
1438
+ for (const warning of analysis.warnings) {
1439
+ lines.push(`- ${warning}`);
1440
+ }
1441
+ }
1442
+ if (!analysis.exists) {
1443
+ lines.push("");
1444
+ lines.push("No local QA Context file was found for this mission.");
1445
+ }
1446
+ return lines.join("\n");
1447
+ }
1448
+
1266
1449
  // tests/_helpers/ai-agent-helper.ts
1267
1450
  var qaSecurityTools = [
1268
1451
  {
@@ -1311,6 +1494,9 @@ var AIAgentHelper = class {
1311
1494
  sessionStorage = /* @__PURE__ */ new Map();
1312
1495
  criticalNetworkError = null;
1313
1496
  criticalJsError = null;
1497
+ qaContextAnalysisCache = null;
1498
+ qaContextPromptCache = "";
1499
+ qaContextStatusLogged = false;
1314
1500
  constructor(page, contextDir = "out", testInfo, knowledgeService) {
1315
1501
  this.page = page;
1316
1502
  this.contextDir = contextDir;
@@ -1372,29 +1558,29 @@ var AIAgentHelper = class {
1372
1558
  try {
1373
1559
  let skillsContext = "";
1374
1560
  let loadedCount = 0;
1375
- const toolsRoot = process.env.ARCALITY_ROOT || path3.join(__dirname, "..", "..");
1561
+ const toolsRoot = process.env.ARCALITY_ROOT || path4.join(__dirname, "..", "..");
1376
1562
  const possibleDirs = [
1377
- path3.join(toolsRoot, ".agent", "skills"),
1378
- path3.join(toolsRoot, ".agents", "skills")
1563
+ path4.join(toolsRoot, ".agent", "skills"),
1564
+ path4.join(toolsRoot, ".agents", "skills")
1379
1565
  ];
1380
1566
  for (const rootDir of possibleDirs) {
1381
- if (!fs3.existsSync(rootDir))
1567
+ if (!fs4.existsSync(rootDir))
1382
1568
  continue;
1383
1569
  if (loadedCount === 0) {
1384
1570
  skillsContext += "\n\u{1F6E0}\uFE0F SKILLS INSTALADAS (Metodolog\xEDas activas):\n";
1385
1571
  }
1386
- const entries = fs3.readdirSync(rootDir, { withFileTypes: true });
1572
+ const entries = fs4.readdirSync(rootDir, { withFileTypes: true });
1387
1573
  for (const entry of entries) {
1388
1574
  let content = "";
1389
1575
  let skillName = entry.name;
1390
1576
  if (entry.isDirectory()) {
1391
- const skillPath = path3.join(rootDir, entry.name, "SKILL.md");
1392
- if (fs3.existsSync(skillPath)) {
1393
- content = fs3.readFileSync(skillPath, "utf8");
1577
+ const skillPath = path4.join(rootDir, entry.name, "SKILL.md");
1578
+ if (fs4.existsSync(skillPath)) {
1579
+ content = fs4.readFileSync(skillPath, "utf8");
1394
1580
  }
1395
1581
  } else if (entry.name.endsWith(".md")) {
1396
- const skillPath = path3.join(rootDir, entry.name);
1397
- content = fs3.readFileSync(skillPath, "utf8");
1582
+ const skillPath = path4.join(rootDir, entry.name);
1583
+ content = fs4.readFileSync(skillPath, "utf8");
1398
1584
  }
1399
1585
  if (content) {
1400
1586
  skillsContext += `
@@ -1417,9 +1603,9 @@ ${content}
1417
1603
  }
1418
1604
  loadMemory(prompt) {
1419
1605
  try {
1420
- const memoryFile = path3.join(this.contextDir, "memoria-agente.json");
1421
- if (fs3.existsSync(memoryFile)) {
1422
- const memories = JSON.parse(fs3.readFileSync(memoryFile, "utf8"));
1606
+ const memoryFile = path4.join(this.contextDir, "memoria-agente.json");
1607
+ if (fs4.existsSync(memoryFile)) {
1608
+ const memories = JSON.parse(fs4.readFileSync(memoryFile, "utf8"));
1423
1609
  const keywords = prompt.toLowerCase().split(" ").filter((w) => w.length > 4);
1424
1610
  const relevantSuccess = memories.filter((m) => {
1425
1611
  if (!m.success)
@@ -1790,13 +1976,13 @@ ${relevantFailures.map((m) => `- ${m.prompt}`).join("\n")}
1790
1976
  */
1791
1977
  async getActionFromGuia(prompt, historyLength) {
1792
1978
  try {
1793
- const fs5 = require("fs");
1794
- const path5 = require("path");
1795
- const memoryFile = path5.join(this.contextDir, "memoria-agente.json");
1796
- if (!fs5.existsSync(memoryFile)) {
1979
+ const fs6 = require("fs");
1980
+ const path6 = require("path");
1981
+ const memoryFile = path6.join(this.contextDir, "memoria-agente.json");
1982
+ if (!fs6.existsSync(memoryFile)) {
1797
1983
  return null;
1798
1984
  }
1799
- const memories = JSON.parse(fs5.readFileSync(memoryFile, "utf8"));
1985
+ const memories = JSON.parse(fs6.readFileSync(memoryFile, "utf8"));
1800
1986
  const state = await this.getPageState();
1801
1987
  const currentUrl = this.page.url();
1802
1988
  const extractCriticalKeywords = (text) => {
@@ -2082,11 +2268,11 @@ ${relevantFailures.map((m) => `- ${m.prompt}`).join("\n")}
2082
2268
  }
2083
2269
  let customUserContext = "";
2084
2270
  try {
2085
- const fs5 = require("fs");
2086
- const path5 = require("path");
2087
- const customContextPath = path5.join(process.cwd(), ".arcality", "qa-context.md");
2088
- if (fs5.existsSync(customContextPath)) {
2089
- const content = fs5.readFileSync(customContextPath, "utf8");
2271
+ const fs6 = require("fs");
2272
+ const path6 = require("path");
2273
+ const customContextPath = path6.join(process.cwd(), ".arcality", "qa-context.md");
2274
+ if (false) {
2275
+ const content = fs6.readFileSync(customContextPath, "utf8");
2090
2276
  customUserContext = `
2091
2277
  <CUSTOMER_BUSINESS_RULES>
2092
2278
  El Ing. de QA o Cliente final ha provisto las siguientes reglas estrictas para este negocio/dominio. DEBES priorizar esta informaci\xF3n sobre todas tus skills base:
@@ -2096,6 +2282,38 @@ ${content}
2096
2282
  }
2097
2283
  } catch (e) {
2098
2284
  }
2285
+ try {
2286
+ if (!this.qaContextAnalysisCache) {
2287
+ const qaContextState = loadQaContextForPrompt(process.cwd());
2288
+ this.qaContextAnalysisCache = qaContextState.analysis;
2289
+ this.qaContextPromptCache = qaContextState.promptBlock;
2290
+ }
2291
+ const qaContextAnalysis = this.qaContextAnalysisCache;
2292
+ if (!this.qaContextStatusLogged) {
2293
+ if (qaContextAnalysis.exists && this.qaContextPromptCache) {
2294
+ console.log(`>>ARCALITY_STATUS>> QA Context local inyectado (${qaContextAnalysis.ruleCount} reglas, ${qaContextAnalysis.sectionCount} secciones).`);
2295
+ } else if (qaContextAnalysis.exists) {
2296
+ console.log(`>>ARCALITY_STATUS>> QA Context local detectado, pero sin reglas utilizables.`);
2297
+ }
2298
+ if (qaContextAnalysis.warnings.length > 0) {
2299
+ for (const warning of qaContextAnalysis.warnings) {
2300
+ console.log(`>>ARCALITY_STATUS>> QA Context warning: ${warning}`);
2301
+ }
2302
+ }
2303
+ this.qaContextStatusLogged = true;
2304
+ }
2305
+ if (this.qaContextPromptCache) {
2306
+ customUserContext = `
2307
+ <CUSTOMER_BUSINESS_RULES>
2308
+ El Ing. de QA o Cliente final ha provisto reglas locales para este negocio/dominio. Debes priorizar esta informacion sobre tus heuristicas base cuando exista conflicto.
2309
+ ${this.qaContextPromptCache}
2310
+ </CUSTOMER_BUSINESS_RULES>
2311
+ `;
2312
+ } else {
2313
+ customUserContext = "";
2314
+ }
2315
+ } catch (e) {
2316
+ }
2099
2317
  const systemPromptBlocks = [
2100
2318
  {
2101
2319
  type: "text",
@@ -3210,8 +3428,8 @@ var SecurityScanner = class {
3210
3428
 
3211
3429
  // tests/_helpers/agentic-runner.spec.ts
3212
3430
  var import_config = require("dotenv/config");
3213
- var fs4 = __toESM(require("fs"));
3214
- var path4 = __toESM(require("path"));
3431
+ var fs5 = __toESM(require("fs"));
3432
+ var path5 = __toESM(require("path"));
3215
3433
  var crypto2 = __toESM(require("crypto"));
3216
3434
  var _sessionRuleCache = /* @__PURE__ */ new Set();
3217
3435
  function captureValidationRule(errorMessage, context) {
@@ -3242,15 +3460,30 @@ function captureValidationRule(errorMessage, context) {
3242
3460
  const history = [];
3243
3461
  const urlHistory = [];
3244
3462
  const stepsData = [];
3245
- const memoryFile = path4.join(contextDir, "memoria-arcality.json");
3246
- if (fs4.existsSync(memoryFile)) {
3463
+ const memoryFile = path5.join(contextDir, "memoria-arcality.json");
3464
+ if (fs5.existsSync(memoryFile)) {
3247
3465
  try {
3248
- fs4.unlinkSync(memoryFile);
3466
+ fs5.unlinkSync(memoryFile);
3249
3467
  } catch {
3250
3468
  }
3251
3469
  }
3252
3470
  const agent = new AIAgentHelper(page, contextDir, testInfo);
3253
3471
  const prompt = process.env.SMART_PROMPT || "Navega al inicio y verifica que el sitio cargue";
3472
+ const qaContextAnalysis = analyzeQaContext(process.cwd());
3473
+ if (qaContextAnalysis.exists && qaContextAnalysis.isValid) {
3474
+ console.log(`>>ARCALITY_STATUS>> QA Context validado al arranque (${qaContextAnalysis.ruleCount} reglas, ${qaContextAnalysis.sectionCount} secciones).`);
3475
+ } else if (qaContextAnalysis.exists) {
3476
+ console.log(`>>ARCALITY_STATUS>> QA Context detectado con advertencias.`);
3477
+ for (const warning of qaContextAnalysis.warnings) {
3478
+ console.log(`>>ARCALITY_STATUS>> QA Context warning: ${warning}`);
3479
+ }
3480
+ } else {
3481
+ console.log(`>>ARCALITY_STATUS>> QA Context local no configurado para esta mision.`);
3482
+ }
3483
+ await testInfo.attach("qa_context_summary", {
3484
+ body: Buffer.from(formatQaContextSummary(qaContextAnalysis), "utf-8"),
3485
+ contentType: "text/plain"
3486
+ });
3254
3487
  const successKeywords = /exitosamente|creado correctamente|guardado correctamente|misión cumplida|registered successfully|created successfully|saved successfully|operación exitosa|registro guardado|successfully|correctly/i;
3255
3488
  const failureKeywords = /\berror\b|falló|failed|no se puede|cannot|unable|ya existe|already exists|existente|exist|inválido|invalid|incorrecto|incorrect|ligado|linked|depende|depends|duplicado|duplicate|repetido|repeated|conflict|reintentar|retry|missing|required|obligatorio|bad request|400|500/i;
3256
3489
  const systemErrorKeywords = /error interno|internal error|intentarlo más tarde|try again later|inténtelo más tarde|servicio no disponible|service unavailable|something went wrong|algo salió mal|ha ocurrido un error inesperado|unexpected error|server error|no se pudo procesar|could not be processed|comuníquese con soporte|contact support/i;
@@ -3273,14 +3506,14 @@ function captureValidationRule(errorMessage, context) {
3273
3506
  if (resultsSaved)
3274
3507
  return;
3275
3508
  resultsSaved = true;
3276
- if (!fs4.existsSync(contextDir))
3277
- fs4.mkdirSync(contextDir, { recursive: true });
3509
+ if (!fs5.existsSync(contextDir))
3510
+ fs5.mkdirSync(contextDir, { recursive: true });
3278
3511
  const finalSuccess = aiMarkedSuccess && !hasCriticalError;
3279
3512
  try {
3280
- const memoryFile2 = path4.join(contextDir, "memoria-arcality.json");
3513
+ const memoryFile2 = path5.join(contextDir, "memoria-arcality.json");
3281
3514
  let memories = [];
3282
- if (fs4.existsSync(memoryFile2)) {
3283
- const content = fs4.readFileSync(memoryFile2, "utf8").trim();
3515
+ if (fs5.existsSync(memoryFile2)) {
3516
+ const content = fs5.readFileSync(memoryFile2, "utf8").trim();
3284
3517
  if (content) {
3285
3518
  try {
3286
3519
  memories = JSON.parse(content);
@@ -3354,16 +3587,16 @@ function captureValidationRule(errorMessage, context) {
3354
3587
  timestamp: Date.now()
3355
3588
  };
3356
3589
  memories.push(missionData);
3357
- fs4.writeFileSync(memoryFile2, JSON.stringify(memories, null, 2));
3590
+ fs5.writeFileSync(memoryFile2, JSON.stringify(memories, null, 2));
3358
3591
  console.log(`>>ARCALITY_STATUS>> \u{1F9E0} Memoria guardada: ${cleanSteps.length} pasos limpios (${rawSteps.length - cleanSteps.length} corruptos filtrados)`);
3359
3592
  } catch (err) {
3360
3593
  console.error("\u274C Fall\xF3 al guardar memoria:", err);
3361
3594
  }
3362
3595
  try {
3363
- const logPath = path4.join(contextDir, `agent-log-${Date.now()}.json`);
3364
- fs4.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));
3596
+ const logPath = path5.join(contextDir, `agent-log-${Date.now()}.json`);
3597
+ fs5.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));
3365
3598
  try {
3366
- fs4.appendFileSync(path4.join(contextDir, "arcality-history.log"), JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), mission: prompt.substring(0, 150), success: finalSuccess, steps: stepCount, usage: totalMissionUsage }) + "\n");
3599
+ fs5.appendFileSync(path5.join(contextDir, "arcality-history.log"), JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), mission: prompt.substring(0, 150), success: finalSuccess, steps: stepCount, usage: totalMissionUsage }) + "\n");
3367
3600
  } catch (e) {
3368
3601
  }
3369
3602
  } catch (err) {
@@ -3549,11 +3782,11 @@ function captureValidationRule(errorMessage, context) {
3549
3782
  timestamp: Date.now()
3550
3783
  };
3551
3784
  try {
3552
- const memoryFile2 = path4.join(contextDir, "memoria-agente.json");
3785
+ const memoryFile2 = path5.join(contextDir, "memoria-agente.json");
3553
3786
  let memories = [];
3554
- if (fs4.existsSync(memoryFile2)) {
3787
+ if (fs5.existsSync(memoryFile2)) {
3555
3788
  try {
3556
- memories = JSON.parse(fs4.readFileSync(memoryFile2, "utf8"));
3789
+ memories = JSON.parse(fs5.readFileSync(memoryFile2, "utf8"));
3557
3790
  } catch {
3558
3791
  memories = [];
3559
3792
  }
@@ -3565,14 +3798,14 @@ function captureValidationRule(errorMessage, context) {
3565
3798
  if (existingIdx < 0) {
3566
3799
  if (resolvedStepsData.length > 0) {
3567
3800
  memories.push(localMemoryData);
3568
- fs4.writeFileSync(memoryFile2, JSON.stringify(memories, null, 2));
3801
+ fs5.writeFileSync(memoryFile2, JSON.stringify(memories, null, 2));
3569
3802
  console.log(`>>ARCALITY_STATUS>> \u2728 Memoria sincronizada en memoria-agente.json (${resolvedStepsData.length} pasos). Arcality entrar\xE1 en MODO GU\xCDA.`);
3570
3803
  } else {
3571
3804
  console.warn(`>>ARCALITY_STATUS>> \u26A0\uFE0F Memoria NO sincronizada: el patr\xF3n del backend tiene 0 pasos t\xE9cnicos v\xE1lidos (steps_technical vac\xEDo).`);
3572
3805
  }
3573
3806
  } else if (!existingHasSteps && resolvedStepsData.length > 0) {
3574
3807
  memories[existingIdx] = localMemoryData;
3575
- fs4.writeFileSync(memoryFile2, JSON.stringify(memories, null, 2));
3808
+ fs5.writeFileSync(memoryFile2, JSON.stringify(memories, null, 2));
3576
3809
  console.log(`>>ARCALITY_STATUS>> \u{1F504} Patr\xF3n corrupto (0 pasos) sobreescrito en memoria-agente.json (${resolvedStepsData.length} pasos nuevos). MODO GU\xCDA activado.`);
3577
3810
  } else {
3578
3811
  console.log(`>>ARCALITY_STATUS>> \u2139\uFE0F Patr\xF3n ya existe en memoria-agente.json con ${(memories[existingIdx]?.steps_data || []).length} pasos. MODO GU\xCDA activado sin re-escritura.`);
@@ -3916,7 +4149,7 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
3916
4149
  const lastSixHistory = history.slice(-6);
3917
4150
  const consecutiveWaits = lastSixHistory.filter((h) => h.includes('wait en "')).length;
3918
4151
  if (consecutiveWaits >= 3) {
3919
- const blankEvidencePath = path4.join(contextDir, `portal-blank-state-${Date.now()}.png`);
4152
+ const blankEvidencePath = path5.join(contextDir, `portal-blank-state-${Date.now()}.png`);
3920
4153
  await page.screenshot({ path: blankEvidencePath, fullPage: true }).catch(() => {
3921
4154
  });
3922
4155
  const blankUrl = page.url();
@@ -4188,7 +4421,7 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
4188
4421
  const panelCount = Math.min(panels.length, 6);
4189
4422
  if (panelCount >= 2 && emptyPanelCount >= panelCount * 0.6) {
4190
4423
  const blankUrl = page.url();
4191
- const blankEvidencePath = path4.join(contextDir, `portal-blank-dashboard-${Date.now()}.png`);
4424
+ const blankEvidencePath = path5.join(contextDir, `portal-blank-dashboard-${Date.now()}.png`);
4192
4425
  await page.screenshot({ path: blankEvidencePath, fullPage: true }).catch(() => {
4193
4426
  });
4194
4427
  console.error(`
@@ -4469,8 +4702,8 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
4469
4702
  console.log("\u{1F916} [SMART-YAML] Generating mission card...");
4470
4703
  const smartYaml = await agent.summarizeMissionToYaml(prompt, history, target);
4471
4704
  if (smartYaml) {
4472
- const yamlPath = path4.join(contextDir, "last-mission-smart.yaml");
4473
- fs4.writeFileSync(yamlPath, smartYaml);
4705
+ const yamlPath = path5.join(contextDir, "last-mission-smart.yaml");
4706
+ fs5.writeFileSync(yamlPath, smartYaml);
4474
4707
  console.log(`>>ARCALITY_STATUS>> \u{1F4DD} Smart Mission Card generated.`);
4475
4708
  await testInfo.attach("smart_mission_card", {
4476
4709
  body: Buffer.from(smartYaml, "utf-8"),
@@ -4542,15 +4775,15 @@ ${failReasoning}
4542
4775
  } else {
4543
4776
  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}`);
4544
4777
  }
4545
- if (fs4.existsSync(contextDir)) {
4778
+ if (fs5.existsSync(contextDir)) {
4546
4779
  try {
4547
- const files = fs4.readdirSync(contextDir);
4780
+ const files = fs5.readdirSync(contextDir);
4548
4781
  for (const file of files) {
4549
4782
  if (file.endsWith(".json") || file.endsWith(".png")) {
4550
- fs4.unlinkSync(path4.join(contextDir, file));
4783
+ fs5.unlinkSync(path5.join(contextDir, file));
4551
4784
  }
4552
4785
  }
4553
- fs4.rmSync(contextDir, { recursive: true, force: true });
4786
+ fs5.rmSync(contextDir, { recursive: true, force: true });
4554
4787
  console.log(`>>ARCALITY_STATUS>> \u{1F9F9} Carpeta context eliminada (Memoria ef\xEDmera completada).`);
4555
4788
  } catch (err) {
4556
4789
  console.warn(`No se pudo limpiar la carpeta context: ${err}`);