@arcadialdev/arcality 2.4.35 → 2.4.37

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcadialdev/arcality",
3
- "version": "2.4.35",
3
+ "version": "2.4.37",
4
4
  "description": "AI-powered QA testing tool — Autonomous web testing agent by Arcadial",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -939,11 +939,11 @@ ${relevantFailures.map((m) => `- ${m.prompt}`).join("\n")}
939
939
  const role = htmlEl.getAttribute("role") || "";
940
940
  const text = (htmlEl.innerText || htmlEl.getAttribute("aria-label") || htmlEl.getAttribute("title") || "").trim().replace(/\s+/g, " ");
941
941
  const textLower = text.toLowerCase();
942
- const isCriticalFailure = (textLower.includes("error") || textLower.includes("fall\xF3") || textLower.includes("no se puede") || textLower.includes("ya existe") || textLower.includes("existe") || textLower.includes("inv\xE1lido") || textLower.includes("incorrecto") || textLower.includes("ligado") || textLower.includes("depende") || textLower.includes("duplicado") || textLower.includes("repetido") || /* Validaciones ZOD / Wizard de campos de formulario */
942
+ const isCriticalFailure = (textLower.includes("error") || textLower.includes("fall\xF3") || textLower.includes("no se puede") || textLower.includes("ya existe") || textLower.includes("inv\xE1lido") || textLower.includes("incorrecto") || textLower.includes("ligado") || textLower.includes("depende") || textLower.includes("duplicado") || textLower.includes("repetido") || /* Validaciones ZOD / Wizard de campos de formulario */
943
943
  textLower.includes("debe ser mayor") || textLower.includes("debe ser menor") || textLower.includes("debe ser igual") || textLower.includes("debe ser mayor o igual") || textLower.includes("debe ser menor o igual") || /* ZOD date messages (Spanish) */
944
944
  textLower.includes("debe ser posterior") || textLower.includes("debe ser anterior") || textLower.includes("debe ser una fecha") || textLower.includes("fecha debe ser") || textLower.includes("posterior o igual a la fecha") || textLower.includes("anterior o igual a la fecha") || textLower.includes("posterior a la fecha") || textLower.includes("anterior a la fecha") || textLower.includes("fecha") && (textLower.includes("actual") || textLower.includes("pasada") || textLower.includes("futura") || textLower.includes("requerida") || textLower.includes("inv\xE1lida") || textLower.includes("incorrecta") || textLower.includes("posterior") || textLower.includes("anterior")) || /* ZOD number/range messages */
945
- textLower.includes("debe ser un n\xFAmero") || textLower.includes("debe ser positivo") || textLower.includes("debe ser negativo") || textLower.includes("m\xEDnimo") && textLower.includes("caracteres") || textLower.includes("m\xE1ximo") && textLower.includes("caracteres") || textLower.includes("al menos") || textLower.includes("como m\xE1ximo") || /* Generic ZOD/form required */
946
- textLower.includes("campo requerido") || textLower.includes("campo obligatorio") || textLower.includes("requerido") || textLower.includes("obligatorio") || textLower.includes("no puede estar vac\xEDo") || textLower.includes("no puede ser") || textLower.includes("no es v\xE1lido") || textLower.includes("no es correcto") || textLower.includes("recuerda llenar") || textLower.includes("completa los campos") || textLower.includes("campos obligatorios") || textLower.includes("formato incorrecto") || textLower.includes("formato inv\xE1lido") || textLower.includes("valor no permitido")) && text.length < 250;
945
+ textLower.includes("debe ser un n\xFAmero") || textLower.includes("debe ser positivo") || textLower.includes("debe ser negativo") || textLower.includes("m\xEDnimo") && textLower.includes("caracteres") || textLower.includes("m\xE1ximo") && textLower.includes("caracteres") || /* Generic ZOD/form required — must be specific, NOT broad instructional text */
946
+ textLower.includes("campo requerido") || textLower.includes("campo obligatorio") || textLower === "requerido" || textLower === "obligatorio" || textLower.includes("este campo es requerido") || textLower.includes("este campo es obligatorio") || textLower.includes("no puede estar vac\xEDo") || textLower.includes("no puede ser") || textLower.includes("no es v\xE1lido") || textLower.includes("no es correcto") || textLower.includes("formato incorrecto") || textLower.includes("formato inv\xE1lido") || textLower.includes("valor no permitido")) && text.length < 200 && !textLower.includes("recuerda") && !textLower.includes("antes de guardar");
947
947
  const isCriticalSuccess = (textLower.includes("exitosamente") || textLower.includes("guardado") || textLower.includes("creado") || textLower.includes("success") || textLower.includes("correctamente") && text.length < 100) && text.length < 200;
948
948
  const isCriticalFeedback = isCriticalFailure || isCriticalSuccess;
949
949
  if (!isCriticalFeedback && (tag === "span" || tag === "p" || tag === "label" || tag === "i" || tag === "svg" || tag === "img" || tag === "div")) {
@@ -956,6 +956,48 @@ ${relevantFailures.map((m) => `- ${m.prompt}`).join("\n")}
956
956
  const idx = startIdx + localCount++;
957
957
  htmlEl.setAttribute("agent-idx", idx.toString());
958
958
  let name = text || htmlEl.getAttribute("aria-label") || htmlEl.getAttribute("title") || htmlEl.getAttribute("placeholder");
959
+ if (!name && (tag === "input" || tag === "select" || htmlEl.getAttribute("role") === "combobox")) {
960
+ const labelledBy = htmlEl.getAttribute("aria-labelledby");
961
+ if (labelledBy) {
962
+ const labelEl = document.getElementById(labelledBy);
963
+ if (labelEl)
964
+ name = labelEl.innerText.trim();
965
+ }
966
+ if (!name && htmlEl.id) {
967
+ const forLabel = document.querySelector(`label[for="${htmlEl.id}"]`);
968
+ if (forLabel)
969
+ name = forLabel.innerText.trim();
970
+ }
971
+ if (!name) {
972
+ const parentLabel = htmlEl.closest("label");
973
+ if (parentLabel)
974
+ name = parentLabel.innerText.replace(htmlEl.textContent || "", "").trim();
975
+ }
976
+ if (!name) {
977
+ let prev = htmlEl.previousElementSibling;
978
+ let tries = 0;
979
+ while (prev && tries < 3) {
980
+ const prevText = (prev.innerText || "").trim();
981
+ if (prevText && prevText.length > 1 && prevText.length < 60) {
982
+ name = prevText;
983
+ break;
984
+ }
985
+ prev = prev.previousElementSibling;
986
+ tries++;
987
+ }
988
+ }
989
+ if (!name) {
990
+ const container = htmlEl.closest("div, fieldset, section");
991
+ if (container) {
992
+ const labelInContainer = container.querySelector('label, [class*="label"], [class*="placeholder"]');
993
+ if (labelInContainer) {
994
+ const labelText = (labelInContainer.innerText || "").trim();
995
+ if (labelText && labelText.length < 60)
996
+ name = labelText;
997
+ }
998
+ }
999
+ }
1000
+ }
959
1001
  if (!name) {
960
1002
  const iconMatch = htmlEl.outerHTML.match(/fa-([a-z0-9-]+)|md-([a-z0-9-]+)|bi-([a-z0-9-]+)/i);
961
1003
  if (iconMatch) {
@@ -1205,10 +1247,18 @@ ${relevantFailures.map((m) => `- ${m.prompt}`).join("\n")}
1205
1247
  return c.selector === memStep.action_data?.selector || cleanMemName !== "" && (cleanMemName === cleanCurrName || cleanCurrName.includes(cleanMemName));
1206
1248
  });
1207
1249
  if (!foundComponent && memStep.action_data?.action !== "wait") {
1208
- console.log(`>>ARCALITY_STATUS>> \u23F3 Componente target no visible ("${memStep.componentName}"). Forzando espera de transici\xF3n DOM...`);
1209
1250
  console.log(`>>ARCALITY_STATUS>> \u{1F50D} Descartado (Componente visual no encontrado: ${memStep.componentName})`);
1210
1251
  return null;
1211
1252
  }
1253
+ if (foundComponent && memStep.action_data?.action === "fill") {
1254
+ const compType = (foundComponent.type || "").toLowerCase();
1255
+ const compName = (foundComponent.name || "").toLowerCase();
1256
+ const looksLikeDropdown = compType.includes("select") || compType.includes("combobox") || compName.includes("tipo") || compName.includes("type") || compName.includes("categor\xEDa") || compName.includes("estado") || compName.includes("frecuencia") || compName.includes("alcance");
1257
+ if (looksLikeDropdown) {
1258
+ console.log(`>>ARCALITY_STATUS>> \u{1F50D} Descartado (fill en dropdown detectado: ${foundComponent.name}). Delegando a IA.`);
1259
+ return null;
1260
+ }
1261
+ }
1212
1262
  const memCompName = memStep.componentName || "";
1213
1263
  if (memCompName.length > 80) {
1214
1264
  console.log(`>>ARCALITY_STATUS>> \u{1F50D} Descartado (Mensaje enorme o nombre inconsistente: ${memCompName.substring(0, 20)}...)`);
@@ -1463,6 +1513,10 @@ These are NOT optional. You MUST use these tools in these situations:
1463
1513
  # CRITICAL RULES
1464
1514
  1. **NEVER BLIND-RETRY**: If an action fails, you MUST investigate with \`inspect_element_details\` before retrying. Repeating the exact same failed action is FORBIDDEN.
1465
1515
  2. **NO REPEAT ACTIONS**: Check HISTORY. If you already filled a field or clicked a button, do NOT do it again unless the UI clearly reset OR the button belongs to a different step/modal/section mentioned in the mission (e.g., Save in modal vs Save in main form).
1516
+ 3. **GROUP ACTIONS**: You MUST group non-conflicting actions in a single turn to save time and tokens. You can \`fill\` multiple text inputs in the SAME turn.
1517
+ - NEVER use \`click\` on a text field before using \`fill\`. The \`fill\` action automatically focuses the field.
1518
+ - Example: \`fill\` Nombre, \`fill\` Fecha, \`fill\` Descripci\xF3n all in one single turn.
1519
+ - BUT keep dropdown interactions isolated: Turn A = \`click\` dropdown. Turn B = \`click\` option.
1466
1520
 
1467
1521
  3. **CURRENT STATE > MEMORY**: What you see in CURRENT COMPONENTS is the truth. SUCCESS MEMORY is a guide, not gospel.
1468
1522
  4. **PAGE TRANSITIONS**: If URL contains '/edit' or you see a 'Save' button, you ARE in the edit view. Don't go back.
@@ -1493,9 +1547,7 @@ These are NOT optional. You MUST use these tools in these situations:
1493
1547
  # WIZARD & MULTI-STEP FORMS (CRITICAL)
1494
1548
  When a form is divided into numbered steps (e.g., Step 1 \u2192 Step 2 \u2192 Step 3):
1495
1549
 
1496
- 1. **VALIDATION ERROR ON SAVE = STEP HAS INVALID DATA**: If you see a \u{1F6D1} [ERROR_CR\xCDTICO] after clicking Save/Next and you are on step N, it means a PREVIOUS step contains bad data. You MUST navigate BACK to that step.
1497
- - Look for a "Back" / "Anterior" / "Periodo" / step-number button and click it to return.
1498
- - NEVER retry the Save button without first fixing the invalid data in the previous step.
1550
+ 1. **VALIDATION ERROR ON ADVANCE**: If you click "Next/Siguiente" and the UI DOES NOT advance to the next step, AND you see a \u{1F6D1} [ERROR_CR\xCDTICO], you must fix the data on the CURRENT step. DO NOT GO BACK to previous steps simply because you see an error, unless the error explicitly states that a previous step's data is invalid.
1499
1551
 
1500
1552
  2. **DATE VALIDATION ERRORS \u2014 MANDATORY PROTOCOL (ZOD-AWARE)**:
1501
1553
  - ZOD generates Spanish validation messages such as:
@@ -1506,8 +1558,8 @@ When a form is divided into numbered steps (e.g., Step 1 \u2192 Step 2 \u2192 St
1506
1558
  - ANY \u{1F6D1} [ERROR_CR\xCDTICO] containing FECHA + (POSTERIOR/ANTERIOR/ACTUAL/PASADA/FUTURA) is a **ZOD DATE VALIDATION ERROR**.
1507
1559
  - **RECOVERY PROTOCOL** (follow in order, NEVER skip):
1508
1560
  1. STOP \u2014 do NOT click Save/Siguiente again with the same data.
1509
- 2. Identify which wizard step contains the failing date field (check HISTORY \u2014 usually Step 1 or Step 2).
1510
- 3. Click the Back/Anterior/step-number button to return to that step.
1561
+ 2. Identify which wizard step contains the failing date field (check HISTORY).
1562
+ 3. If you are NOT on that step, click the Back/Anterior button to return to it.
1511
1563
  4. Locate the date input using \`validate_element_state\` with 'has_value' to confirm its current (bad) value.
1512
1564
  5. Clear and re-fill the date using \`interact_native_control\` with control_type \`'date'\` and a value \u2265 TODAY in YYYY-MM-DD format.
1513
1565
  6. TODAY: \`${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}\` \u2014 use this exact expression to compute the date dynamically.
@@ -1515,7 +1567,9 @@ When a form is divided into numbered steps (e.g., Step 1 \u2192 Step 2 \u2192 St
1515
1567
 
1516
1568
  3. **NEVER LOOP ON THE SAME STEP**: If you've been on the same wizard step for 3+ turns without advancing, go back one step and verify the data is correct before trying to advance again.
1517
1569
 
1518
- 4. **STEP VERIFICATION**: Before clicking Next/Save on any step, VERIFY that date fields have future-or-today dates. Use \`validate_element_state\` with 'has_value' to confirm.`
1570
+ 4. **STEP VERIFICATION**: Before clicking Next/Save on any step, VERIFY that date fields have future-or-today dates. Use \`validate_element_state\` with 'has_value' to confirm.
1571
+
1572
+ 5. **FINAL PREVIEW STEP**: If you reach a final step like "Previsualizaci\xF3n", "Resumen", or see a "Terminar" / "Finalizar" button, your objective is ALMOST COMPLETE. Do NOT go back to previous steps just because you see read-only data. Click the final submit button ("Terminar", "Guardar", "Finalizar") to complete the mission.`
1519
1573
  }
1520
1574
  ];
1521
1575
  if (this.testInfo) {
@@ -1647,7 +1701,12 @@ ${history.slice(-25).join("\n") || "None"}`
1647
1701
  const textResponse = message.content.find((c) => c.type === "text")?.text || "";
1648
1702
  if (toolCalls.length === 0) {
1649
1703
  console.log(`(${((Date.now() - startTime) / 1e3).toFixed(1)}s)`);
1650
- return { thought: textResponse || "No action decided", actions: [], usage: totalUsage };
1704
+ console.log(`>>ARCALITY_STATUS>> \u26A0\uFE0F Agente devolvi\xF3 texto sin acci\xF3n. Reinyectando instrucci\xF3n mandatoria...`);
1705
+ anthropicMessages.push({
1706
+ role: "user",
1707
+ content: [{ type: "text", text: `MANDATORY: You MUST call one of the available tools right now. You are NOT allowed to return plain text only. If you are unsure, call 'perform_ui_actions' with the most logical next action based on the mission and history. If you are truly stuck, call 'report_inability_to_proceed'. DO NOT return text without a tool call.` }]
1708
+ });
1709
+ continue;
1651
1710
  }
1652
1711
  const toolResults = [];
1653
1712
  let uiActionCall = null;
@@ -2863,14 +2922,17 @@ function captureValidationRule(errorMessage, context) {
2863
2922
  }
2864
2923
  }
2865
2924
  const lastAction = history[history.length - 1];
2866
- if (lastAction && lastAction.includes(`click en "${desc}"`)) {
2925
+ const isGenericName = !desc || desc === "[input]" || desc === "[button]" || desc === "[select]" || desc === "[div]" || desc.trim().length < 3;
2926
+ if (!isGenericName && lastAction && lastAction.includes(`click en "${desc}"`)) {
2867
2927
  const isDotsTrigger = desc.includes("dots") || desc.includes("vert") || desc.includes("menu") || desc.includes("opciones");
2868
2928
  const sameActionCount = history.filter((h) => h.includes(`click en "${desc}"`)).length;
2869
- const toleranceLimit = allowRepeatedNames ? 3 : 1;
2870
- if (isDotsTrigger && sameActionCount < 2) {
2929
+ const toleranceLimit = allowRepeatedNames ? 4 : 2;
2930
+ if (isDotsTrigger && sameActionCount < 3) {
2871
2931
  console.log(` \u26A0\uFE0F Re-intentando click en men\xFA "${desc}"...`);
2872
2932
  } else if (allowRepeatedNames && sameActionCount < toleranceLimit) {
2873
2933
  console.log(` \u26A0\uFE0F Click repetido permitido por contexto de misi\xF3n en "${desc}"...`);
2934
+ } else if (sameActionCount < toleranceLimit) {
2935
+ console.log(` \u26A0\uFE0F Re-intento ${sameActionCount} en "${desc}"...`);
2874
2936
  } else {
2875
2937
  const bodyText = await page.innerText("body").catch(() => "");
2876
2938
  if (bodyText.match(/ligado|depende|no se puede/i)) {
@@ -2881,9 +2943,9 @@ function captureValidationRule(errorMessage, context) {
2881
2943
  break;
2882
2944
  }
2883
2945
  console.log(`
2884
- \u{1F6D1} [AUTO-STOP] Bloqueando click duplicado en "${desc}". Forzando re-evaluaci\xF3n.`);
2885
- history.push(`Turno ${stepCount}: Bloqueado click repetido en "${desc}" (Evitando bucle)`);
2886
- await page.waitForTimeout(1e3);
2946
+ \u{1F6D1} [AUTO-STOP] Bloqueando click repetido en "${desc}" (${sameActionCount} veces). Forzando re-evaluaci\xF3n.`);
2947
+ history.push(`Turno ${stepCount}: \u{1F6D1} [AUTO-STOP] El click en "${desc}" fue bloqueado despu\xE9s de ${sameActionCount} repeticiones. Usa un IDX diferente o intenta hacer scroll para revelar el elemento. Si es un dropdown que no abre, usa interact_native_control.`);
2948
+ await page.waitForTimeout(800);
2887
2949
  break;
2888
2950
  }
2889
2951
  }