@arcadialdev/arcality 3.0.0 → 3.0.1
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 +1 -1
- package/scripts/gen-and-run.mjs +950 -176
- package/scripts/init.mjs +221 -178
- package/tests/_helpers/agentic-runner.bundle.spec.js +285 -52
|
@@ -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
|
|
819
|
-
var
|
|
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 ||
|
|
1561
|
+
const toolsRoot = process.env.ARCALITY_ROOT || path4.join(__dirname, "..", "..");
|
|
1376
1562
|
const possibleDirs = [
|
|
1377
|
-
|
|
1378
|
-
|
|
1563
|
+
path4.join(toolsRoot, ".agent", "skills"),
|
|
1564
|
+
path4.join(toolsRoot, ".agents", "skills")
|
|
1379
1565
|
];
|
|
1380
1566
|
for (const rootDir of possibleDirs) {
|
|
1381
|
-
if (!
|
|
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 =
|
|
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 =
|
|
1392
|
-
if (
|
|
1393
|
-
content =
|
|
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 =
|
|
1397
|
-
content =
|
|
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 =
|
|
1421
|
-
if (
|
|
1422
|
-
const memories = JSON.parse(
|
|
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
|
|
1794
|
-
const
|
|
1795
|
-
const memoryFile =
|
|
1796
|
-
if (!
|
|
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(
|
|
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
|
|
2086
|
-
const
|
|
2087
|
-
const customContextPath =
|
|
2088
|
-
if (
|
|
2089
|
-
const content =
|
|
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
|
|
3214
|
-
var
|
|
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 =
|
|
3246
|
-
if (
|
|
3463
|
+
const memoryFile = path5.join(contextDir, "memoria-arcality.json");
|
|
3464
|
+
if (fs5.existsSync(memoryFile)) {
|
|
3247
3465
|
try {
|
|
3248
|
-
|
|
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 (!
|
|
3277
|
-
|
|
3509
|
+
if (!fs5.existsSync(contextDir))
|
|
3510
|
+
fs5.mkdirSync(contextDir, { recursive: true });
|
|
3278
3511
|
const finalSuccess = aiMarkedSuccess && !hasCriticalError;
|
|
3279
3512
|
try {
|
|
3280
|
-
const memoryFile2 =
|
|
3513
|
+
const memoryFile2 = path5.join(contextDir, "memoria-arcality.json");
|
|
3281
3514
|
let memories = [];
|
|
3282
|
-
if (
|
|
3283
|
-
const content =
|
|
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
|
-
|
|
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 =
|
|
3364
|
-
|
|
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
|
-
|
|
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 =
|
|
3785
|
+
const memoryFile2 = path5.join(contextDir, "memoria-agente.json");
|
|
3553
3786
|
let memories = [];
|
|
3554
|
-
if (
|
|
3787
|
+
if (fs5.existsSync(memoryFile2)) {
|
|
3555
3788
|
try {
|
|
3556
|
-
memories = JSON.parse(
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
4473
|
-
|
|
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 (
|
|
4778
|
+
if (fs5.existsSync(contextDir)) {
|
|
4546
4779
|
try {
|
|
4547
|
-
const files =
|
|
4780
|
+
const files = fs5.readdirSync(contextDir);
|
|
4548
4781
|
for (const file of files) {
|
|
4549
4782
|
if (file.endsWith(".json") || file.endsWith(".png")) {
|
|
4550
|
-
|
|
4783
|
+
fs5.unlinkSync(path5.join(contextDir, file));
|
|
4551
4784
|
}
|
|
4552
4785
|
}
|
|
4553
|
-
|
|
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}`);
|