@arcadialdev/arcality 2.6.7 → 3.0.0

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.
@@ -4,6 +4,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __getProtoOf = Object.getPrototypeOf;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __esm = (fn, res) => function __init() {
8
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
9
+ };
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
7
14
  var __copyProps = (to, from, except, desc) => {
8
15
  if (from && typeof from === "object" || typeof from === "function") {
9
16
  for (let key of __getOwnPropNames(from))
@@ -21,6 +28,399 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
28
  mod
22
29
  ));
23
30
 
31
+ // src/configLoader.mjs
32
+ function getApiUrl() {
33
+ return "https://arcalityqadev.arcadial.lat";
34
+ }
35
+ function loadLocalArcalityConfig() {
36
+ try {
37
+ const configPath = import_node_path.default.join(process.cwd(), "arcality.config");
38
+ if (import_node_fs.default.existsSync(configPath)) {
39
+ let raw = import_node_fs.default.readFileSync(configPath, "utf8");
40
+ if (raw.charCodeAt(0) === 65279)
41
+ raw = raw.slice(1);
42
+ return JSON.parse(raw);
43
+ }
44
+ } catch {
45
+ }
46
+ return null;
47
+ }
48
+ function getApiKey() {
49
+ const localConfig = loadLocalArcalityConfig();
50
+ if (localConfig?.apiKey) {
51
+ process.env.ARCALITY_API_KEY = localConfig.apiKey;
52
+ return { key: localConfig.apiKey, source: "config" };
53
+ }
54
+ if (process.env.ARCALITY_API_KEY) {
55
+ return { key: process.env.ARCALITY_API_KEY, source: "env" };
56
+ }
57
+ return { key: null, source: "none" };
58
+ }
59
+ var import_node_fs, import_node_path, import_node_os, import_dotenv, CONFIG_DIR, CONFIG_FILE;
60
+ var init_configLoader = __esm({
61
+ "src/configLoader.mjs"() {
62
+ import_node_fs = __toESM(require("node:fs"), 1);
63
+ import_node_path = __toESM(require("node:path"), 1);
64
+ import_node_os = __toESM(require("node:os"), 1);
65
+ import_dotenv = __toESM(require("dotenv"), 1);
66
+ import_dotenv.default.config();
67
+ CONFIG_DIR = import_node_path.default.join(import_node_os.default.homedir(), ".arcality");
68
+ CONFIG_FILE = import_node_path.default.join(CONFIG_DIR, "config.json");
69
+ }
70
+ });
71
+
72
+ // src/arcalityClient.mjs
73
+ var arcalityClient_exports = {};
74
+ __export(arcalityClient_exports, {
75
+ ArcalityClient: () => ArcalityClient,
76
+ createAdoTask: () => createAdoTask,
77
+ endMission: () => endMission,
78
+ fetchMissions: () => fetchMissions,
79
+ getEvidenceSasToken: () => getEvidenceSasToken,
80
+ startMission: () => startMission,
81
+ validateApiKey: () => validateApiKey
82
+ });
83
+ function getTodayKey() {
84
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
85
+ }
86
+ function loadLocalUsage() {
87
+ try {
88
+ if (import_node_fs2.default.existsSync(USAGE_FILE)) {
89
+ return JSON.parse(import_node_fs2.default.readFileSync(USAGE_FILE, "utf8"));
90
+ }
91
+ } catch {
92
+ }
93
+ return {};
94
+ }
95
+ function saveLocalUsage(usage) {
96
+ try {
97
+ if (!import_node_fs2.default.existsSync(CONFIG_DIR)) {
98
+ import_node_fs2.default.mkdirSync(CONFIG_DIR, { recursive: true });
99
+ }
100
+ import_node_fs2.default.writeFileSync(USAGE_FILE, JSON.stringify(usage, null, 2), "utf8");
101
+ } catch {
102
+ }
103
+ }
104
+ function getLocalDailyUsage() {
105
+ const usage = loadLocalUsage();
106
+ const today = getTodayKey();
107
+ return usage[today] || 0;
108
+ }
109
+ function incrementLocalUsage() {
110
+ const usage = loadLocalUsage();
111
+ const today = getTodayKey();
112
+ usage[today] = (usage[today] || 0) + 1;
113
+ const keys = Object.keys(usage).sort().reverse();
114
+ const cleaned = {};
115
+ for (const k of keys.slice(0, 7)) {
116
+ cleaned[k] = usage[k];
117
+ }
118
+ saveLocalUsage(cleaned);
119
+ return cleaned[today];
120
+ }
121
+ function loadArcalityConfig() {
122
+ try {
123
+ const configPath = import_node_path2.default.join(process.cwd(), "arcality.config");
124
+ if (import_node_fs2.default.existsSync(configPath)) {
125
+ let raw = import_node_fs2.default.readFileSync(configPath, "utf8");
126
+ if (raw.charCodeAt(0) === 65279)
127
+ raw = raw.slice(1);
128
+ return JSON.parse(raw);
129
+ }
130
+ } catch {
131
+ }
132
+ return null;
133
+ }
134
+ function getEffectiveApiBase() {
135
+ return getApiUrl();
136
+ }
137
+ function getEffectiveApiKey() {
138
+ const localConfig = loadArcalityConfig();
139
+ if (localConfig?.apiKey)
140
+ return localConfig.apiKey;
141
+ const { key } = getApiKey();
142
+ return key;
143
+ }
144
+ function loadProjectId() {
145
+ const localConfig = loadArcalityConfig();
146
+ if (localConfig?.projectId)
147
+ return localConfig.projectId;
148
+ return process.env.ARCALITY_PROJECT_ID || null;
149
+ }
150
+ async function validateApiKey() {
151
+ const key = getEffectiveApiKey();
152
+ if (!key) {
153
+ return { valid: false, error: "no_api_key", mode: "mock" };
154
+ }
155
+ if (!key.startsWith("arc_")) {
156
+ return { valid: false, error: "invalid_format", mode: "mock" };
157
+ }
158
+ const apiBase = getEffectiveApiBase();
159
+ if (apiBase) {
160
+ try {
161
+ const controller = new AbortController();
162
+ const timeout = setTimeout(() => controller.abort(), 5e3);
163
+ const res = await fetch(`${apiBase}/api/v1/auth/validate`, {
164
+ method: "POST",
165
+ headers: {
166
+ "Content-Type": "application/json",
167
+ "x-api-key": key
168
+ },
169
+ signal: controller.signal
170
+ });
171
+ clearTimeout(timeout);
172
+ if (res.status === 401)
173
+ return { valid: false, error: "invalid_api_key", mode: "live" };
174
+ if (res.status === 403)
175
+ return { valid: false, error: "plan_expired", mode: "live" };
176
+ if (!res.ok)
177
+ return { valid: false, error: "server_error", mode: "live" };
178
+ const data = await res.json();
179
+ if (process.env.DEBUG || process.argv.includes("--debug")) {
180
+ console.log(`[DEBUG] Auth validation response keys:`, Object.keys(data));
181
+ console.log(`[DEBUG] Auth validation response:`, JSON.stringify(data, null, 2));
182
+ }
183
+ return { ...data, valid: true, mode: "live" };
184
+ } catch (e) {
185
+ const reason = e?.name === "AbortError" ? "timeout (5s)" : e?.message || "unknown";
186
+ console.warn(`\u26A0\uFE0F Backend not available (${apiBase}): ${reason}. Using local mode.`);
187
+ }
188
+ }
189
+ const dailyUsed = getLocalDailyUsage();
190
+ return {
191
+ valid: true,
192
+ mode: "mock",
193
+ plan: "internal",
194
+ daily_used: dailyUsed,
195
+ daily_limit: DAILY_MISSION_LIMIT,
196
+ remaining: Math.max(0, DAILY_MISSION_LIMIT - dailyUsed)
197
+ };
198
+ }
199
+ async function startMission(prompt, targetUrl) {
200
+ const key = getEffectiveApiKey();
201
+ if (!key)
202
+ return { allowed: false, error: "no_api_key" };
203
+ const apiBase = getEffectiveApiBase();
204
+ if (apiBase) {
205
+ try {
206
+ const projectId = process.env.ARCALITY_PROJECT_ID || loadArcalityConfig()?.projectId;
207
+ const controller = new AbortController();
208
+ const timeout = setTimeout(() => controller.abort(), 5e3);
209
+ const res = await fetch(`${apiBase}/api/v1/missions/start`, {
210
+ method: "POST",
211
+ headers: {
212
+ "Content-Type": "application/json",
213
+ "x-api-key": key
214
+ },
215
+ body: JSON.stringify({
216
+ prompt,
217
+ prompt_hash: simpleHash(prompt),
218
+ target_url: targetUrl,
219
+ project_id: projectId || void 0
220
+ }),
221
+ signal: controller.signal
222
+ });
223
+ clearTimeout(timeout);
224
+ if (res.status === 429) {
225
+ const data = await res.json();
226
+ return { allowed: false, error: "mission_limit_exceeded", ...data };
227
+ }
228
+ if (!res.ok)
229
+ return { allowed: false, error: "server_error" };
230
+ return { allowed: true, ...await res.json() };
231
+ } catch (e) {
232
+ const reason = e?.name === "AbortError" ? "timeout (5s)" : e?.message || "unknown";
233
+ console.warn(`\u26A0\uFE0F startMission failed (${apiBase}): ${reason}. Falling back to mock mode.`);
234
+ }
235
+ }
236
+ const dailyUsed = getLocalDailyUsage();
237
+ if (dailyUsed >= DAILY_MISSION_LIMIT) {
238
+ const tomorrow = /* @__PURE__ */ new Date();
239
+ tomorrow.setDate(tomorrow.getDate() + 1);
240
+ tomorrow.setHours(0, 0, 0, 0);
241
+ return {
242
+ allowed: false,
243
+ error: "mission_limit_exceeded",
244
+ daily_used: dailyUsed,
245
+ daily_limit: DAILY_MISSION_LIMIT,
246
+ remaining: 0,
247
+ resets_at: tomorrow.toISOString()
248
+ };
249
+ }
250
+ const newCount = incrementLocalUsage();
251
+ return {
252
+ allowed: true,
253
+ mode: "mock",
254
+ mission_id: `mock_${Date.now().toString(36)}`,
255
+ daily_used: newCount,
256
+ daily_limit: DAILY_MISSION_LIMIT,
257
+ remaining: DAILY_MISSION_LIMIT - newCount
258
+ };
259
+ }
260
+ async function fetchMissions(projectId, tags) {
261
+ const key = getEffectiveApiKey();
262
+ if (!key)
263
+ return { success: false, error: "no_api_key", missions: [] };
264
+ const apiBase = getEffectiveApiBase();
265
+ if (!apiBase) {
266
+ return { success: true, mode: "mock", missions: [] };
267
+ }
268
+ try {
269
+ const url = new URL(`${apiBase}/api/v1/projects/${projectId}/missions`);
270
+ if (tags) {
271
+ url.searchParams.append("tags", tags);
272
+ }
273
+ const controller = new AbortController();
274
+ const timeout = setTimeout(() => controller.abort(), 1e4);
275
+ const res = await fetch(url.toString(), {
276
+ method: "GET",
277
+ headers: {
278
+ "x-api-key": key
279
+ },
280
+ signal: controller.signal
281
+ });
282
+ clearTimeout(timeout);
283
+ if (!res.ok) {
284
+ const errText = await res.text();
285
+ console.warn(`[DEBUG] fetchMissions failed: ${res.status} - ${errText}`);
286
+ return { success: false, error: "server_error", missions: [] };
287
+ }
288
+ const data = await res.json();
289
+ return { success: true, missions: data.missions || [] };
290
+ } catch (e) {
291
+ const reason = e?.name === "AbortError" ? "timeout (10s)" : e?.message || "unknown";
292
+ console.warn(`\u26A0\uFE0F fetchMissions failed (${apiBase}): ${reason}.`);
293
+ return { success: false, error: reason, missions: [] };
294
+ }
295
+ }
296
+ async function endMission(missionId, result, usage = null, failReason = null, reportUrl = null, failReasoning = null) {
297
+ const apiBase = getEffectiveApiBase();
298
+ if (!apiBase || !missionId)
299
+ return { ok: true };
300
+ const key = getEffectiveApiKey();
301
+ try {
302
+ await fetch(`${apiBase}/api/v1/missions/${missionId}/end`, {
303
+ method: "POST",
304
+ headers: {
305
+ "Content-Type": "application/json",
306
+ "x-api-key": key
307
+ },
308
+ body: JSON.stringify({ result, usage, failReason, failReasoning, reportUrl })
309
+ });
310
+ } catch {
311
+ }
312
+ return { ok: true };
313
+ }
314
+ function simpleHash(str) {
315
+ let hash = 0;
316
+ for (let i = 0; i < str.length; i++) {
317
+ const char = str.charCodeAt(i);
318
+ hash = (hash << 5) - hash + char;
319
+ hash |= 0;
320
+ }
321
+ return "ph_" + Math.abs(hash).toString(36);
322
+ }
323
+ async function createAdoTask(title, description) {
324
+ const apiBase = getEffectiveApiBase();
325
+ if (!apiBase) {
326
+ if (process.env.DEBUG)
327
+ console.log(`[ADO] No hay ARCALITY_API_URL configurado. Omitiendo creaci\xF3n de Task en Azure DevOps.`);
328
+ return { success: false, error: "no_api_url" };
329
+ }
330
+ const projectId = loadProjectId();
331
+ if (!projectId) {
332
+ if (process.env.DEBUG)
333
+ console.log(`[ADO] No hay Project ID configurado. Omitiendo creaci\xF3n de Task en Azure DevOps.`);
334
+ return { success: false, error: "no_project_id" };
335
+ }
336
+ const key = getEffectiveApiKey();
337
+ if (!key) {
338
+ if (process.env.DEBUG)
339
+ console.log(`[ADO] No hay API key configurada. Omitiendo creaci\xF3n de Task en Azure DevOps.`);
340
+ return { success: false, error: "no_api_key" };
341
+ }
342
+ try {
343
+ const controller = new AbortController();
344
+ const timeout = setTimeout(() => controller.abort(), 15e3);
345
+ const res = await fetch(`${apiBase}/api/v1/integrations/azuredevops/task`, {
346
+ method: "POST",
347
+ headers: {
348
+ "Content-Type": "application/json",
349
+ "x-api-key": key
350
+ },
351
+ body: JSON.stringify({ project_id: projectId, title, description }),
352
+ signal: controller.signal
353
+ });
354
+ clearTimeout(timeout);
355
+ if (res.status === 400) {
356
+ const data2 = await res.json().catch(() => ({}));
357
+ if (data2?.error === "create_task_failed") {
358
+ if (process.env.DEBUG)
359
+ console.log(`[ADO] Integraci\xF3n no configurada para esta organizaci\xF3n: ${data2.message}`);
360
+ } else {
361
+ console.warn(`\u26A0\uFE0F [ADO] Error al crear Task en Azure DevOps: ${data2?.message || "Bad Request"}`);
362
+ }
363
+ return { success: false, error: data2?.message || "create_task_failed" };
364
+ }
365
+ if (!res.ok) {
366
+ console.warn(`\u26A0\uFE0F [ADO] Error HTTP ${res.status} al crear Task en Azure DevOps.`);
367
+ return { success: false, error: `http_${res.status}` };
368
+ }
369
+ const data = await res.json();
370
+ console.log(`\u2705 [ADO] Task creada en Azure DevOps: ${data.task_url || "(sin URL)"}`);
371
+ return { success: true, taskUrl: data.task_url };
372
+ } catch (e) {
373
+ const reason = e?.name === "AbortError" ? "timeout (15s)" : e?.message || "unknown";
374
+ console.warn(`\u26A0\uFE0F [ADO] No se pudo crear la Task en Azure DevOps: ${reason}`);
375
+ return { success: false, error: reason };
376
+ }
377
+ }
378
+ async function getEvidenceSasToken(missionId, projectId) {
379
+ const apiBase = getEffectiveApiBase();
380
+ if (!apiBase || !missionId)
381
+ return null;
382
+ const key = getEffectiveApiKey();
383
+ if (!key)
384
+ return null;
385
+ try {
386
+ const organization_id = process.env.ARCALITY_ORG_ID || "";
387
+ const res = await fetch(`${apiBase}/api/v1/missions/${missionId}/evidence/sas`, {
388
+ method: "POST",
389
+ headers: {
390
+ "Content-Type": "application/json",
391
+ "x-api-key": key
392
+ },
393
+ body: JSON.stringify({ organization_id, project_id: projectId })
394
+ });
395
+ if (!res.ok) {
396
+ const txt = await res.text();
397
+ console.log(`[DEBUG] SAS Token API Failed: ${res.status} - ${txt}`);
398
+ return null;
399
+ }
400
+ const data = await res.json();
401
+ return data;
402
+ } catch (e) {
403
+ console.log(`[DEBUG] SAS Token API Error: ${e.message}`);
404
+ return null;
405
+ }
406
+ }
407
+ var import_node_fs2, import_node_path2, DAILY_MISSION_LIMIT, USAGE_FILE, ArcalityClient;
408
+ var init_arcalityClient = __esm({
409
+ "src/arcalityClient.mjs"() {
410
+ import_node_fs2 = __toESM(require("node:fs"), 1);
411
+ import_node_path2 = __toESM(require("node:path"), 1);
412
+ init_configLoader();
413
+ DAILY_MISSION_LIMIT = 35;
414
+ USAGE_FILE = import_node_path2.default.join(CONFIG_DIR, "usage.json");
415
+ ArcalityClient = class {
416
+ constructor(apiKey) {
417
+ this.apiUrl = getApiUrl();
418
+ this.apiKey = apiKey;
419
+ }
420
+ };
421
+ }
422
+ });
423
+
24
424
  // tests/_helpers/agentic-runner.spec.ts
25
425
  var import_test = require("@playwright/test");
26
426
 
@@ -415,8 +815,8 @@ async function scan_sensitive_data_exposure(page) {
415
815
  }
416
816
 
417
817
  // tests/_helpers/ai-agent-helper.ts
418
- var fs = __toESM(require("fs"));
419
- var path = __toESM(require("path"));
818
+ var fs3 = __toESM(require("fs"));
819
+ var path3 = __toESM(require("path"));
420
820
 
421
821
  // src/KnowledgeService.ts
422
822
  var crypto = __toESM(require("crypto"));
@@ -925,16 +1325,20 @@ var AIAgentHelper = class {
925
1325
  });
926
1326
  this.page.on("requestfailed", (request) => {
927
1327
  const failure = request.failure();
1328
+ const errorText = failure?.errorText || "Aborted";
1329
+ if (errorText.includes("net::ERR_ABORTED") || errorText === "Aborted") {
1330
+ return;
1331
+ }
928
1332
  const resourceType = request.resourceType();
929
1333
  const url = request.url();
930
1334
  if (["fetch", "xhr", "document"].includes(resourceType)) {
931
1335
  if (!url.includes("google") && !url.includes("analytics") && !url.includes("telemetry")) {
932
1336
  if (resourceType === "document" || request.method() !== "GET") {
933
- this.criticalNetworkError = `Network Request Failed (${request.method()}): ${url} - ${failure?.errorText || "Aborted"}`;
1337
+ this.criticalNetworkError = `Network Request Failed (${request.method()}): ${url} - ${errorText}`;
934
1338
  }
935
1339
  }
936
1340
  }
937
- this.logs.push(`[NETWORK_ERROR] ${request.method()} ${request.url()} - ${failure?.errorText || "Unknown error"}`);
1341
+ this.logs.push(`[NETWORK_ERROR] ${request.method()} ${request.url()} - ${errorText}`);
938
1342
  if (this.logs.length > 30)
939
1343
  this.logs.shift();
940
1344
  });
@@ -968,29 +1372,29 @@ var AIAgentHelper = class {
968
1372
  try {
969
1373
  let skillsContext = "";
970
1374
  let loadedCount = 0;
971
- const toolsRoot = process.env.ARCALITY_ROOT || path.join(__dirname, "..", "..");
1375
+ const toolsRoot = process.env.ARCALITY_ROOT || path3.join(__dirname, "..", "..");
972
1376
  const possibleDirs = [
973
- path.join(toolsRoot, ".agent", "skills"),
974
- path.join(toolsRoot, ".agents", "skills")
1377
+ path3.join(toolsRoot, ".agent", "skills"),
1378
+ path3.join(toolsRoot, ".agents", "skills")
975
1379
  ];
976
1380
  for (const rootDir of possibleDirs) {
977
- if (!fs.existsSync(rootDir))
1381
+ if (!fs3.existsSync(rootDir))
978
1382
  continue;
979
1383
  if (loadedCount === 0) {
980
1384
  skillsContext += "\n\u{1F6E0}\uFE0F SKILLS INSTALADAS (Metodolog\xEDas activas):\n";
981
1385
  }
982
- const entries = fs.readdirSync(rootDir, { withFileTypes: true });
1386
+ const entries = fs3.readdirSync(rootDir, { withFileTypes: true });
983
1387
  for (const entry of entries) {
984
1388
  let content = "";
985
1389
  let skillName = entry.name;
986
1390
  if (entry.isDirectory()) {
987
- const skillPath = path.join(rootDir, entry.name, "SKILL.md");
988
- if (fs.existsSync(skillPath)) {
989
- content = fs.readFileSync(skillPath, "utf8");
1391
+ const skillPath = path3.join(rootDir, entry.name, "SKILL.md");
1392
+ if (fs3.existsSync(skillPath)) {
1393
+ content = fs3.readFileSync(skillPath, "utf8");
990
1394
  }
991
1395
  } else if (entry.name.endsWith(".md")) {
992
- const skillPath = path.join(rootDir, entry.name);
993
- content = fs.readFileSync(skillPath, "utf8");
1396
+ const skillPath = path3.join(rootDir, entry.name);
1397
+ content = fs3.readFileSync(skillPath, "utf8");
994
1398
  }
995
1399
  if (content) {
996
1400
  skillsContext += `
@@ -1013,9 +1417,9 @@ ${content}
1013
1417
  }
1014
1418
  loadMemory(prompt) {
1015
1419
  try {
1016
- const memoryFile = path.join(this.contextDir, "memoria-agente.json");
1017
- if (fs.existsSync(memoryFile)) {
1018
- const memories = JSON.parse(fs.readFileSync(memoryFile, "utf8"));
1420
+ const memoryFile = path3.join(this.contextDir, "memoria-agente.json");
1421
+ if (fs3.existsSync(memoryFile)) {
1422
+ const memories = JSON.parse(fs3.readFileSync(memoryFile, "utf8"));
1019
1423
  const keywords = prompt.toLowerCase().split(" ").filter((w) => w.length > 4);
1020
1424
  const relevantSuccess = memories.filter((m) => {
1021
1425
  if (!m.success)
@@ -1288,7 +1692,7 @@ ${relevantFailures.map((m) => `- ${m.prompt}`).join("\n")}
1288
1692
  if (bodyTextLength2 < 80 && components2 < 10) {
1289
1693
  console.error(`
1290
1694
  \u{1F6D1} [BLANK_STATE_CRASH] Vista en blanco confirmada tras espera (Comps: ${components2}, Texto: ${bodyTextLength2} chars). URL: ${urlForBlankCheck}`);
1291
- throw new Error(`[BLANK_STATE_CRASH] El portal carg\xF3 en blanco. Esto es un fallo de renderizado del portal, no del agente.`);
1695
+ throw new Error(`[BLANK_STATE_CRASH] El portal carg\xF3 en blanco. Esto es un fallo de renderizado del portal, no de Arcality.`);
1292
1696
  } else {
1293
1697
  console.log(`>>ARCALITY_STATUS>> \u2705 [BLANK_STATE] Falsa alarma. P\xE1gina recuper\xF3 contenido tras espera (${components2} comps, ${bodyTextLength2} chars).`);
1294
1698
  }
@@ -1386,13 +1790,13 @@ ${relevantFailures.map((m) => `- ${m.prompt}`).join("\n")}
1386
1790
  */
1387
1791
  async getActionFromGuia(prompt, historyLength) {
1388
1792
  try {
1389
- const fs3 = require("fs");
1390
- const path3 = require("path");
1391
- const memoryFile = path3.join(this.contextDir, "memoria-agente.json");
1392
- if (!fs3.existsSync(memoryFile)) {
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)) {
1393
1797
  return null;
1394
1798
  }
1395
- const memories = JSON.parse(fs3.readFileSync(memoryFile, "utf8"));
1799
+ const memories = JSON.parse(fs5.readFileSync(memoryFile, "utf8"));
1396
1800
  const state = await this.getPageState();
1397
1801
  const currentUrl = this.page.url();
1398
1802
  const extractCriticalKeywords = (text) => {
@@ -1678,11 +2082,11 @@ ${relevantFailures.map((m) => `- ${m.prompt}`).join("\n")}
1678
2082
  }
1679
2083
  let customUserContext = "";
1680
2084
  try {
1681
- const fs3 = require("fs");
1682
- const path3 = require("path");
1683
- const customContextPath = path3.join(process.cwd(), ".arcality", "qa-context.md");
1684
- if (fs3.existsSync(customContextPath)) {
1685
- const content = fs3.readFileSync(customContextPath, "utf8");
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");
1686
2090
  customUserContext = `
1687
2091
  <CUSTOMER_BUSINESS_RULES>
1688
2092
  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:
@@ -1788,9 +2192,10 @@ These are NOT optional. You MUST use these tools in these situations:
1788
2192
  - If HISTORY contains a line with "ERROR VISIBLE EN PANTALLA" or "ERROR POST-GUARDADO" with words like "repetido", "duplicado", "ya existe", or "already exists":
1789
2193
  1. You MUST identify WHICH field caused the duplication (usually the main name/title field).
1790
2194
  2. You MUST use \`fill\` to REPLACE that field's value with a NEW unique value. Add a numeric suffix like \`_${(/* @__PURE__ */ new Date()).getMinutes()}${(/* @__PURE__ */ new Date()).getSeconds()}\` or a short random number.
1791
- 3. ONLY AFTER changing the value, attempt to save again.
1792
- 4. **NEVER attempt to save with the same value that was already rejected.** This is FORBIDDEN.
1793
- 5. If you cannot determine which field is the duplicate, use \`capture_console_errors\` to get hints from the application.
2195
+ 3. **MANDATORY SINGLE-TURN GROUPING**: You MUST perform the correction (using \`fill\`) AND click the submit/save button (using \`click\` on Siguiente/Guardar/Save) in the EXACT SAME TURN. Do NOT split this into separate turns.
2196
+ 4. **DO NOT CLOSE ERROR TOASTS/ALERTS**: Closing toasts/alerts is strictly FORBIDDEN. It wastes a turn and hides the error context from the runner. Playwright can interact with elements underneath perfectly.
2197
+ 5. **NEVER attempt to save with the same value that was already rejected.** This is FORBIDDEN.
2198
+ 6. If you cannot determine which field is the duplicate, use \`capture_console_errors\` to get hints from the application.
1794
2199
  7. **DATA UNIQUENESS**: When generating test data, ALWAYS include a unique suffix (timestamp, counter). Never use "Test123" or "Prueba" alone.
1795
2200
  8. **FINISH CORRECTLY**: Use \`finish: true\` ONLY when the mission objective is FULLY achieved.
1796
2201
  - **MANDATORY FORMS RULE**: If your mission involves creating, editing, or configuring something, YOU MUST CLICK THE FINAL SUBMIT/SAVE BUTTON ("Guardar", "Siguiente", "Finalizar") AND VERIFY THE SUCCESS MESSAGE BEFORE emitting \`finish: true\`. Typing the last field is NOT finishing the mission! Do NOT finish prematurely.
@@ -1801,6 +2206,7 @@ These are NOT optional. You MUST use these tools in these situations:
1801
2206
  11. **NEGATIVE TESTING**: If mission expects an error (e.g., "verifica que no se pueda guardar sin campos"), and you see that error, the mission is SUCCESS. Use \`finish: true\`.
1802
2207
  12. **GUIDE CONFLICT**: If SUCCESS MEMORY disagrees with what you see on screen, ABANDON the memory and trust your eyes.
1803
2208
  13. **NATIVE CONTROLS**: For \`<input type="time">\`, \`<input type="date">\`, or similar native pickers, NEVER use \`fill\` repeatedly. Use \`interact_native_control\` or \`send_keyboard_event\` instead. If \`fill\` fails once, switch strategy immediately.
2209
+ 14. **USER-DEFINED BUGS (MANDATORY & IMMEDIATE)**: If the prompt explicitly defines a condition as a bug (e.g., "Si no te deja eliminarla, es un bug de la aplicaci\xF3n", or "If X fails it's a bug"), and you encounter that condition, you MUST call \`report_application_bug\` IMMEDIATELY in that exact same turn. DO NOT perform any intermediate UI actions (such as clicking "Cancelar", closing modals, or navigating away) before calling the tool. Any intermediate UI action will cause you to lose your reasoning context in the next turn and enter an infinite loop. The call to \`report_application_bug\` must take absolute priority over any UI cleanup or close actions. DO NOT dismiss it as "expected business logic" or "referential integrity". The user's definition of a bug OVERRIDES all general reasoning.
1804
2210
 
1805
2211
  # WIZARD & MULTI-STEP FORMS (CRITICAL)
1806
2212
  When a form is divided into numbered steps (e.g., Step 1 \u2192 Step 2 \u2192 Step 3):
@@ -1841,7 +2247,7 @@ When a form is divided into numbered steps (e.g., Step 1 \u2192 Step 2 \u2192 St
1841
2247
 
1842
2248
  11. **COMBOBOX KEYBOARD FALLBACK**: If you click a combobox/select field and the dropdown options (\`[li]\`, \`[OPTION]\`) are NOT visible after 1 turn, use this keyboard sequence: a) Click the combobox field ONCE. b) Use \`send_keyboard_event\` with key "ArrowDown" to open the options and highlight the first one. c) Use \`send_keyboard_event\` with key "Enter" to select it. This avoids the toggle problem and works with all dropdown implementations.
1843
2249
 
1844
- 12. **ERROR STOP PROTOCOL (CRITICAL)**: If you see a message starting with \`\u{1F6D1} [ERROR_CR\xCDTICO]\` or \`\u{1F6D1} ERROR POST-GUARDADO\`, do NOT assume it is a transient error. You MUST analyze the text. If it says "ya existe" or "duplicado", you MUST change the offending field value (e.g., add a random number) BEFORE retrying. If you retry without changing any data and the error persists, you MUST use \`report_inability_to_proceed\` immediately. Looping on the same error toast is FORBIDDEN.`
2250
+ 12. **ERROR STOP PROTOCOL (CRITICAL)**: If you see a message starting with \`\u{1F6D1} [ERROR_CR\xCDTICO]\` or \`\u{1F6D1} ERROR POST-GUARDADO\`, do NOT assume it is a transient error. You MUST analyze the text. If it says "ya existe" or "duplicado", you MUST change the offending field value (e.g., add a random number) AND click the save button in the SAME turn. Do NOT waste a turn closing the error toast. If you retry without changing any data and the error persists, you MUST use \`report_inability_to_proceed\` immediately. Looping on the same error toast is FORBIDDEN.`
1845
2251
  }
1846
2252
  ];
1847
2253
  if (this.testInfo) {
@@ -1901,7 +2307,7 @@ ${history.slice(-25).join("\n") || "None"}`
1901
2307
  const isProxyMode = !!process.env.ARCALITY_API_URL;
1902
2308
  const endpointUrl = isProxyMode ? `${process.env.ARCALITY_API_URL}/api/v1/ai/proxy` : "https://api.anthropic.com/v1/messages";
1903
2309
  const displayTurn = stepCount !== void 0 ? stepCount : turn + 1;
1904
- console.log(`>>ARCALITY_STATUS>> \u{1F4E1} Llamando a: ${endpointUrl} (turno IA ${displayTurn}${turn > 0 ? `, reintento ${turn}` : ""})`);
2310
+ console.log(`>>ARCALITY_STATUS>> \u{1F4E1} Llamando a: ${endpointUrl} (turno IA ${displayTurn}${turn > 0 ? `, ciclo interno ${turn + 1}` : ""})`);
1905
2311
  const headers = {
1906
2312
  "Content-Type": "application/json"
1907
2313
  };
@@ -1973,7 +2379,7 @@ ${history.slice(-25).join("\n") || "None"}`
1973
2379
  const textResponse = message.content.find((c) => c.type === "text")?.text || "";
1974
2380
  if (toolCalls.length === 0) {
1975
2381
  console.log(`(${((Date.now() - startTime) / 1e3).toFixed(1)}s)`);
1976
- console.log(`>>ARCALITY_STATUS>> \u26A0\uFE0F Agente devolvi\xF3 texto sin acci\xF3n. Reinyectando instrucci\xF3n mandatoria...`);
2382
+ console.log(`>>ARCALITY_STATUS>> \u26A0\uFE0F Arcality devolvi\xF3 texto sin acci\xF3n. Reinyectando instrucci\xF3n mandatoria...`);
1977
2383
  anthropicMessages.push({
1978
2384
  role: "user",
1979
2385
  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.` }]
@@ -2029,9 +2435,43 @@ ${history.slice(-25).join("\n") || "None"}`
2029
2435
  );
2030
2436
  } catch (e) {
2031
2437
  }
2438
+ try {
2439
+ const { createAdoTask: createAdoTask2 } = await Promise.resolve().then(() => (init_arcalityClient(), arcalityClient_exports));
2440
+ const shortDesc = toolInput.bug_description.length > 80 ? toolInput.bug_description.substring(0, 80).trimEnd() + "..." : toolInput.bug_description;
2441
+ const bugTitle = `[QA Bug] ${shortDesc}`;
2442
+ const bugDescription = [
2443
+ `**Bug detectado autom\xE1ticamente por Arcality QA**`,
2444
+ ``,
2445
+ `**Descripci\xF3n:** ${toolInput.bug_description}`,
2446
+ `**Comportamiento esperado:** ${toolInput.expected_behavior}`,
2447
+ `**Comportamiento actual:** ${toolInput.actual_behavior}`,
2448
+ `**URL afectada:** ${this.page.url()}`,
2449
+ `**Fecha:** ${(/* @__PURE__ */ new Date()).toISOString()}`
2450
+ ].join("\n");
2451
+ createAdoTask2(bugTitle, bugDescription).catch(() => {
2452
+ });
2453
+ } catch (e) {
2454
+ }
2032
2455
  throw new Error(`APPLICATION BUG: ${toolInput.bug_description}`);
2033
2456
  } else if (toolName === "report_inability_to_proceed") {
2034
2457
  toolResults.push({ type: "tool_result", tool_use_id: toolUse.id, content: "Confusion acknowledged." });
2458
+ try {
2459
+ const { createAdoTask: createAdoTask2 } = await Promise.resolve().then(() => (init_arcalityClient(), arcalityClient_exports));
2460
+ const shortDesc = toolInput.bug_description.length > 80 ? toolInput.bug_description.substring(0, 80).trimEnd() + "..." : toolInput.bug_description;
2461
+ const bugTitle = `[QA Bug] ${shortDesc}`;
2462
+ const bugDescription = [
2463
+ `**Bug detectado autom\xE1ticamente por Arcality QA**`,
2464
+ ``,
2465
+ `**Descripci\xF3n:** ${toolInput.bug_description}`,
2466
+ `**Comportamiento esperado:** ${toolInput.expected_behavior}`,
2467
+ `**Comportamiento actual:** ${toolInput.actual_behavior}`,
2468
+ `**URL afectada:** ${this.page.url()}`,
2469
+ `**Fecha:** ${(/* @__PURE__ */ new Date()).toISOString()}`
2470
+ ].join("\n");
2471
+ createAdoTask2(bugTitle, bugDescription).catch(() => {
2472
+ });
2473
+ } catch (e) {
2474
+ }
2035
2475
  return {
2036
2476
  thought: `DISCULPA: ${toolInput.confusion_reason}
2037
2477
 
@@ -2770,8 +3210,8 @@ var SecurityScanner = class {
2770
3210
 
2771
3211
  // tests/_helpers/agentic-runner.spec.ts
2772
3212
  var import_config = require("dotenv/config");
2773
- var fs2 = __toESM(require("fs"));
2774
- var path2 = __toESM(require("path"));
3213
+ var fs4 = __toESM(require("fs"));
3214
+ var path4 = __toESM(require("path"));
2775
3215
  var crypto2 = __toESM(require("crypto"));
2776
3216
  var _sessionRuleCache = /* @__PURE__ */ new Set();
2777
3217
  function captureValidationRule(errorMessage, context) {
@@ -2802,10 +3242,10 @@ function captureValidationRule(errorMessage, context) {
2802
3242
  const history = [];
2803
3243
  const urlHistory = [];
2804
3244
  const stepsData = [];
2805
- const memoryFile = path2.join(contextDir, "memoria-agente.json");
2806
- if (fs2.existsSync(memoryFile)) {
3245
+ const memoryFile = path4.join(contextDir, "memoria-arcality.json");
3246
+ if (fs4.existsSync(memoryFile)) {
2807
3247
  try {
2808
- fs2.unlinkSync(memoryFile);
3248
+ fs4.unlinkSync(memoryFile);
2809
3249
  } catch {
2810
3250
  }
2811
3251
  }
@@ -2833,14 +3273,14 @@ function captureValidationRule(errorMessage, context) {
2833
3273
  if (resultsSaved)
2834
3274
  return;
2835
3275
  resultsSaved = true;
2836
- if (!fs2.existsSync(contextDir))
2837
- fs2.mkdirSync(contextDir, { recursive: true });
3276
+ if (!fs4.existsSync(contextDir))
3277
+ fs4.mkdirSync(contextDir, { recursive: true });
2838
3278
  const finalSuccess = aiMarkedSuccess && !hasCriticalError;
2839
3279
  try {
2840
- const memoryFile2 = path2.join(contextDir, "memoria-agente.json");
3280
+ const memoryFile2 = path4.join(contextDir, "memoria-arcality.json");
2841
3281
  let memories = [];
2842
- if (fs2.existsSync(memoryFile2)) {
2843
- const content = fs2.readFileSync(memoryFile2, "utf8").trim();
3282
+ if (fs4.existsSync(memoryFile2)) {
3283
+ const content = fs4.readFileSync(memoryFile2, "utf8").trim();
2844
3284
  if (content) {
2845
3285
  try {
2846
3286
  memories = JSON.parse(content);
@@ -2914,16 +3354,16 @@ function captureValidationRule(errorMessage, context) {
2914
3354
  timestamp: Date.now()
2915
3355
  };
2916
3356
  memories.push(missionData);
2917
- fs2.writeFileSync(memoryFile2, JSON.stringify(memories, null, 2));
3357
+ fs4.writeFileSync(memoryFile2, JSON.stringify(memories, null, 2));
2918
3358
  console.log(`>>ARCALITY_STATUS>> \u{1F9E0} Memoria guardada: ${cleanSteps.length} pasos limpios (${rawSteps.length - cleanSteps.length} corruptos filtrados)`);
2919
3359
  } catch (err) {
2920
3360
  console.error("\u274C Fall\xF3 al guardar memoria:", err);
2921
3361
  }
2922
3362
  try {
2923
- const logPath = path2.join(contextDir, `agent-log-${Date.now()}.json`);
2924
- 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));
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));
2925
3365
  try {
2926
- 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");
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");
2927
3367
  } catch (e) {
2928
3368
  }
2929
3369
  } catch (err) {
@@ -2958,8 +3398,8 @@ function captureValidationRule(errorMessage, context) {
2958
3398
  });
2959
3399
  console.log(`
2960
3400
  ======================================================`);
2961
- console.log(`\u{1F9E0} [MEMORIA COLECTIVA - INGESTA DE APRENDIZAJE]`);
2962
- console.log(`>>arcality>> Persistiendo conocimiento (project: ${pid}, steps: ${effectiveSteps.length}, isFailed: ${isFailed})...`);
3401
+ console.log(`\u{1F9E0} [ARCHIVE SUBSYSTEM - KNOWLEDGE INGESTION]`);
3402
+ console.log(`>>arcality>> Saving telemetry (project: ${pid}, steps: ${effectiveSteps.length}, isFailed: ${isFailed})...`);
2963
3403
  console.log(`======================================================
2964
3404
  `);
2965
3405
  try {
@@ -2980,7 +3420,7 @@ function captureValidationRule(errorMessage, context) {
2980
3420
  const ruleId = await pushRule({
2981
3421
  rule_type: "NAVIGATION",
2982
3422
  title: `Flujo de navegaci\xF3n: ${prompt.substring(0, 80)}`,
2983
- description: `Agente naveg\xF3 ${uniqueUrls.length} p\xE1ginas: ${uniqueUrls.join(" \u2192 ").substring(0, 400)}`,
3423
+ description: `Arcality naveg\xF3 ${uniqueUrls.length} p\xE1ginas: ${uniqueUrls.join(" \u2192 ").substring(0, 400)}`,
2984
3424
  severity: "suggestion"
2985
3425
  });
2986
3426
  if (ruleId)
@@ -3003,7 +3443,7 @@ function captureValidationRule(errorMessage, context) {
3003
3443
  const failRuleId = await pushRule({
3004
3444
  rule_type: "VALIDATION",
3005
3445
  title: `Misi\xF3n fallida: ${prompt.substring(0, 80)}`,
3006
- description: `El agente no pudo completar: "${prompt.substring(0, 200)}". \xDAltimos pasos: ${stepsNarrative.substring(0, 300)}`,
3446
+ description: `Arcality no pudo completar: "${prompt.substring(0, 200)}". \xDAltimos pasos: ${stepsNarrative.substring(0, 300)}`,
3007
3447
  severity: "important"
3008
3448
  });
3009
3449
  if (failRuleId)
@@ -3016,7 +3456,7 @@ function captureValidationRule(errorMessage, context) {
3016
3456
  }
3017
3457
  };
3018
3458
  console.log(`
3019
- \u{1F916} [AGENTE] Iniciando misi\xF3n: "${prompt}"`);
3459
+ \u{1F916} [ARCALITY] Iniciando misi\xF3n: "${prompt}"`);
3020
3460
  const base = process.env.BASE_URL || "";
3021
3461
  const target = process.env.TARGET_PATH || "/";
3022
3462
  if (process.env.LOGIN_USER && process.env.LOGIN_PASSWORD) {
@@ -3086,10 +3526,19 @@ function captureValidationRule(errorMessage, context) {
3086
3526
  } catch {
3087
3527
  patternJsonObj = {};
3088
3528
  }
3089
- const rawStepsTechnical = patternJsonObj.steps_technical || patternJsonObj.stepsTechnical;
3529
+ const patternKeys = Object.keys(patternJsonObj);
3530
+ const bestMatchKeys = Object.keys(bestMatch);
3531
+ console.log(`>>ARCALITY_STATUS>> \u{1F52C} [DIAGN\xD3STICO] Claves en bestMatch: [${bestMatchKeys.join(", ")}]`);
3532
+ console.log(`>>ARCALITY_STATUS>> \u{1F52C} [DIAGN\xD3STICO] Claves en patternJsonObj: [${patternKeys.join(", ")}]`);
3533
+ console.log(`>>ARCALITY_STATUS>> \u{1F52C} [DIAGN\xD3STICO] patternJsonObj.steps (tipo): ${typeof patternJsonObj.steps} | valor: ${JSON.stringify(patternJsonObj.steps)?.substring(0, 120)}`);
3534
+ console.log(`>>ARCALITY_STATUS>> \u{1F52C} [DIAGN\xD3STICO] patternJsonObj.steps_technical (tipo): ${typeof patternJsonObj.steps_technical} | es array: ${Array.isArray(patternJsonObj.steps_technical)} | length: ${Array.isArray(patternJsonObj.steps_technical) ? patternJsonObj.steps_technical.length : "N/A"}`);
3535
+ const rawStepsTechnical = patternJsonObj.steps_technical || patternJsonObj.stepsTechnical || patternJsonObj.StepsTechnical || patternJsonObj.technical_steps || patternJsonObj.technicalSteps || // Fallback: si 'steps' es un array de objetos (no un número), usarlo directamente
3536
+ (Array.isArray(patternJsonObj.steps) && patternJsonObj.steps.length > 0 && typeof patternJsonObj.steps[0] === "object" ? patternJsonObj.steps : null);
3090
3537
  const resolvedStepsData = Array.isArray(rawStepsTechnical) ? rawStepsTechnical : [];
3091
3538
  if (resolvedStepsData.length === 0) {
3092
- console.warn(`>>ARCALITY_STATUS>> \u26A0\uFE0F Patr\xF3n descargado sin steps_technical v\xE1lidos. Modo Gu\xEDa no ser\xE1 activado.`);
3539
+ console.warn(`>>ARCALITY_STATUS>> \u26A0\uFE0F Patr\xF3n descargado sin steps_technical v\xE1lidos (0 pasos encontrados en todas las variantes de nombre). Modo Gu\xEDa no ser\xE1 activado.`);
3540
+ } else {
3541
+ console.log(`>>ARCALITY_STATUS>> \u2705 [DIAGN\xD3STICO] steps_technical resueltos: ${resolvedStepsData.length} pasos. Muestra del paso[0]: ${JSON.stringify(resolvedStepsData[0]).substring(0, 200)}`);
3093
3542
  }
3094
3543
  const localMemoryData = {
3095
3544
  prompt,
@@ -3100,28 +3549,33 @@ function captureValidationRule(errorMessage, context) {
3100
3549
  timestamp: Date.now()
3101
3550
  };
3102
3551
  try {
3103
- const memoryFile2 = path2.join(contextDir, "memoria-agente.json");
3552
+ const memoryFile2 = path4.join(contextDir, "memoria-agente.json");
3104
3553
  let memories = [];
3105
- if (fs2.existsSync(memoryFile2)) {
3554
+ if (fs4.existsSync(memoryFile2)) {
3106
3555
  try {
3107
- memories = JSON.parse(fs2.readFileSync(memoryFile2, "utf8"));
3556
+ memories = JSON.parse(fs4.readFileSync(memoryFile2, "utf8"));
3108
3557
  } catch {
3109
3558
  memories = [];
3110
3559
  }
3111
3560
  }
3112
3561
  const normalizeForDedup = (s) => s.toLowerCase().replace(/\s+/g, " ").trim();
3113
3562
  const normalizedCurrentPrompt = normalizeForDedup(prompt);
3114
- const alreadyExists = memories.some((m) => normalizeForDedup(m.prompt || "") === normalizedCurrentPrompt);
3115
- if (!alreadyExists) {
3563
+ const existingIdx = memories.findIndex((m) => normalizeForDedup(m.prompt || "") === normalizedCurrentPrompt);
3564
+ const existingHasSteps = existingIdx >= 0 && (memories[existingIdx].steps_data || []).length > 0;
3565
+ if (existingIdx < 0) {
3116
3566
  if (resolvedStepsData.length > 0) {
3117
3567
  memories.push(localMemoryData);
3118
- fs2.writeFileSync(memoryFile2, JSON.stringify(memories, null, 2));
3119
- console.log(`>>ARCALITY_STATUS>> \u2728 Memoria sincronizada localmente (${resolvedStepsData.length} pasos). El Agente entrar\xE1 en modo GU\xCDA.`);
3568
+ fs4.writeFileSync(memoryFile2, JSON.stringify(memories, null, 2));
3569
+ console.log(`>>ARCALITY_STATUS>> \u2728 Memoria sincronizada en memoria-agente.json (${resolvedStepsData.length} pasos). Arcality entrar\xE1 en MODO GU\xCDA.`);
3120
3570
  } else {
3121
- console.warn(`>>ARCALITY_STATUS>> \u26A0\uFE0F Memoria NO sincronizada: el patr\xF3n del backend tiene 0 pasos t\xE9cnicos v\xE1lidos.`);
3571
+ 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).`);
3122
3572
  }
3573
+ } else if (!existingHasSteps && resolvedStepsData.length > 0) {
3574
+ memories[existingIdx] = localMemoryData;
3575
+ fs4.writeFileSync(memoryFile2, JSON.stringify(memories, null, 2));
3576
+ console.log(`>>ARCALITY_STATUS>> \u{1F504} Patr\xF3n corrupto (0 pasos) sobreescrito en memoria-agente.json (${resolvedStepsData.length} pasos nuevos). MODO GU\xCDA activado.`);
3123
3577
  } else {
3124
- console.log(`>>ARCALITY_STATUS>> \u2139\uFE0F Patr\xF3n ya existe en memoria local. Modo GU\xCDA activado sin re-escritura.`);
3578
+ 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.`);
3125
3579
  }
3126
3580
  } catch (err) {
3127
3581
  console.warn(`Error al sincronizar memoria local: ${err}`);
@@ -3159,7 +3613,7 @@ ${readableHistory}`;
3159
3613
  let containsError = false;
3160
3614
  if (page.isClosed()) {
3161
3615
  console.error(`
3162
- \u{1F6D1} [PAGE CRASH] La pesta\xF1a del navegador fue cerrada inesperadamente (inicio de ciclo).`);
3616
+ \u26A0 [LAZARUS SUBSYSTEM: Critical Crash] La pesta\xF1a del navegador fue cerrada inesperadamente (inicio de ciclo).`);
3163
3617
  console.error(` >> Causa probable: El portal caus\xF3 una navegaci\xF3n forzada, crash de tab, o redirigi\xF3 fuera del contexto.`);
3164
3618
  hasCriticalError = true;
3165
3619
  failReason = "portal_page_crash";
@@ -3257,7 +3711,7 @@ ${readableHistory}`;
3257
3711
  console.log(`>>ARCALITY_STATUS>> \u{1F6D1} Error interno del Portal/Red detectado: "${networkError}"`);
3258
3712
  hasCriticalError = true;
3259
3713
  failReason = "portal_network_error";
3260
- 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.
3714
+ failReasoning = `El portal web fall\xF3 al intentar comunicarse con su backend (ej. una petici\xF3n XHR o Fetch fall\xF3 o dio timeout). Arcality QA no tiene control sobre las ca\xEDdas del servidor.
3261
3715
  Detalles t\xE9cnicos capturados en la red: ${networkError}`;
3262
3716
  isFinished = true;
3263
3717
  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.`);
@@ -3285,12 +3739,12 @@ Detalles de la excepci\xF3n en consola: ${jsError}`;
3285
3739
  console.error(`
3286
3740
  \u{1F6D1} [SYSTEM ERROR DETECTADO] El portal report\xF3 un error de sistema no recuperable en UI:`);
3287
3741
  console.error(` Mensaje: "${sysErrTxt.trim().substring(0, 150)}"`);
3288
- console.error(` >> Tipo: portal_internal_error | El agente NO puede corregir errores del servidor.`);
3742
+ console.error(` >> Tipo: portal_internal_error | Arcality NO puede corregir errores del servidor.`);
3289
3743
  hasCriticalError = true;
3290
3744
  failReason = "portal_internal_error";
3291
- 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.`;
3745
+ failReasoning = `El servidor devolvi\xF3 un error sist\xE9mico irrecuperable que fue mostrado en la interfaz visual: "${sysErrTxt.trim()}". Arcality QA no puede reparar bugs l\xF3gicos o de infraestructura del servidor, por lo que la misi\xF3n fue detenida preventivamente.`;
3292
3746
  isFinished = true;
3293
- 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.`);
3747
+ 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 de Arcality. El portal devolvi\xF3 HTTP 200 pero report\xF3 un error interno en la UI.`);
3294
3748
  saveMissionResults();
3295
3749
  break;
3296
3750
  }
@@ -3302,7 +3756,7 @@ Detalles de la excepci\xF3n en consola: ${jsError}`;
3302
3756
  break;
3303
3757
  let hasVisibleError = false;
3304
3758
  try {
3305
- const errorEls = await page.locator('.toast, .alert, [role="alert"], [role="status"], .snackbar, .notification, .error-message').all();
3759
+ const errorEls = await page.locator('.toast, .alert, [role="alert"], [role="status"], .snackbar, .notification, .error-message, .mat-snack-bar-container, [class*="alert"], [class*="toast"]').all();
3306
3760
  for (const el of errorEls) {
3307
3761
  if (await el.isVisible().catch(() => false)) {
3308
3762
  const errTxt = await el.innerText().catch(() => "");
@@ -3321,26 +3775,26 @@ Detalles de la excepci\xF3n en consola: ${jsError}`;
3321
3775
  await page.waitForTimeout(1e3);
3322
3776
  response = await agent.getActionFromGuia(prompt, guideIdx);
3323
3777
  if (response) {
3324
- console.log(`>>ARCALITY_STATUS>> \u{1F504} Gu\xEDa recuper\xF3 el rastro tras esperar 1000ms.`);
3778
+ console.log(`>>ARCALITY_STATUS>> \u{1F504} Guide recovered path after 1000ms delay.`);
3325
3779
  }
3326
3780
  }
3327
3781
  } else {
3328
- console.log(`>>ARCALITY_STATUS>> \u26A0\uFE0F Error real detectado en pantalla. Saltando Gu\xEDa.`);
3782
+ console.log(`>>ARCALITY_STATUS>> \u26A0\uFE0F Visible UI anomaly detected. Bypassing Guide.`);
3329
3783
  guideIdx = 999999;
3330
3784
  if (stepsData.length > 0) {
3331
3785
  stepsDataBackup = [...stepsData];
3332
3786
  stepsData.length = 0;
3333
3787
  }
3334
3788
  try {
3335
- const errorEls = await page.locator('.toast, .alert, [role="alert"], [role="status"], .snackbar, .notification, .error-message, .mat-snack-bar-container').all();
3789
+ const errorEls = await page.locator('.toast, .alert, [role="alert"], [role="status"], .snackbar, .notification, .error-message, .mat-snack-bar-container, [class*="alert"], [class*="toast"]').all();
3336
3790
  for (const el of errorEls) {
3337
3791
  if (await el.isVisible().catch(() => false)) {
3338
3792
  const errTxt = await el.innerText().catch(() => "");
3339
3793
  if (errTxt && failureKeywords.test(errTxt.toLowerCase())) {
3340
3794
  const isDuplicateError = /repetido|duplicado|ya existe|already exists/i.test(errTxt);
3341
- const correctionHint = isDuplicateError ? `\u{1F6D1} ACCI\xD3N REQUERIDA: El valor que ingresaste ya existe en el sistema. DEBES regresar al campo que caus\xF3 el error y cambiar su valor por uno \xDANICO (agrega un sufijo como _${Date.now().toString().slice(-4)} o un n\xFAmero aleatorio). NO intentes guardar sin cambiar el valor primero.` : `\u{1F6D1} ACCI\xD3N REQUERIDA: El sistema mostr\xF3 un error de validaci\xF3n. Analiza el error y corrige el dato antes de reintentar.`;
3795
+ const correctionHint = isDuplicateError ? `\u{1F6D1} ACCI\xD3N REQUERIDA: El valor que ingresaste ya existe en el sistema. DEBES regresar al campo que caus\xF3 el error y cambiar su valor por uno \xDANICO (agrega un sufijo como _${Date.now().toString().slice(-4)} o un n\xFAmero aleatorio). NO intentes guardar sin cambiar el valor primero.` : `\u{1F6D1} ANOMAL\xCDA UI DETECTADA: El sistema mostr\xF3 un error de validaci\xF3n. DIRECTIVA PARA ARCALITY: Si este error significa que la misi\xF3n no puede completarse (ej. un bug del sistema o un estado bloqueado), DEBES usar 'finish: true' inmediatamente y explicar el problema. Si es un simple error de formato de datos, corrige los datos y reintenta.`;
3342
3796
  history.push(`Turno ${stepCount}: \u{1F6D1} ERROR VISIBLE EN PANTALLA: "${errTxt.trim().substring(0, 200)}". ${correctionHint}`);
3343
- console.log(`>>ARCALITY_STATUS>> \u{1F6D1} Error inyectado en historial: "${errTxt.substring(0, 80)}"`);
3797
+ console.log(`>>ARCALITY_STATUS>> \u{1F6D1} Anomal\xEDa inyectada en historial: "${errTxt.substring(0, 80)}"`);
3344
3798
  break;
3345
3799
  }
3346
3800
  }
@@ -3349,7 +3803,7 @@ Detalles de la excepci\xF3n en consola: ${jsError}`;
3349
3803
  }
3350
3804
  }
3351
3805
  if (response) {
3352
- const guideActionDesc = response.actions?.[0] ? `${response.actions[0].action} en "${response.actions[0].description || ""}"`.toLowerCase() : "";
3806
+ const guideActionDesc = response.actions?.[0] ? `${response.actions[0].action} on "${response.actions[0].description || ""}"`.toLowerCase() : "";
3353
3807
  const recentHistory = history.slice(-4).map((h) => h.toLowerCase());
3354
3808
  const isGuideRepeating = guideActionDesc && recentHistory.some((h) => h.includes(guideActionDesc.substring(0, 30)));
3355
3809
  if (isGuideRepeating) {
@@ -3361,15 +3815,15 @@ Detalles de la excepci\xF3n en consola: ${jsError}`;
3361
3815
  guideIdx++;
3362
3816
  console.log(`
3363
3817
  ======================================================`);
3364
- console.log(`\u{1F680} [USANDO GU\xCDA DE \xC9XITO - MEMORIA COLECTIVA AHORRANDO TOKENS]`);
3365
- console.log(`>>arcality>> Paso validado (Gu\xEDa #${guideStepCount}, guideIdx=${guideIdx}, NO consume turno IA)`);
3818
+ console.log(`\u{1F680} [MODO GU\xCDA ACTIVADO - EJECUTANDO RUTA R\xC1PIDA CONOCIDA]`);
3819
+ console.log(`>>arcality>> Paso validado (Gu\xEDa #${guideStepCount}, guideIdx=${guideIdx}, omite IA)`);
3366
3820
  console.log(`======================================================
3367
3821
  `);
3368
3822
  }
3369
3823
  }
3370
3824
  if (!response) {
3371
3825
  stepCount++;
3372
- console.log(`>>ARCALITY_STATUS>> \u23F3 Turno IA ${stepCount} de ${maxSteps} (Gu\xEDa us\xF3 ${guideStepCount} turnos gratis)...`);
3826
+ console.log(`>>ARCALITY_STATUS>> \u25C9 [ARCALITY] Calculando siguiente acci\xF3n... (Turno ${stepCount}/${maxSteps}) (Saltos por Gu\xEDa: ${guideStepCount})...`);
3373
3827
  const effectivePrompt = patternContext ? `${prompt}
3374
3828
 
3375
3829
  ${patternContext}` : prompt;
@@ -3421,7 +3875,7 @@ ${patternContext}` : prompt;
3421
3875
  }
3422
3876
  console.log(`
3423
3877
  ======================================================`);
3424
- console.log(`\u{1F916} [RAZONAMIENTO DEL AGENTE]`);
3878
+ console.log(`\u{1F916} [RAZONAMIENTO DE ARCALITY]`);
3425
3879
  console.log(`${response ? response.thought : "N/A"}`);
3426
3880
  console.log(`======================================================
3427
3881
  `);
@@ -3444,7 +3898,7 @@ ${patternContext}` : prompt;
3444
3898
  const loopType = isClickLoop ? "click" : "fill";
3445
3899
  const uniqueTargets = isClickLoop ? uniqueClicks : uniqueFills;
3446
3900
  console.warn(`
3447
- \u26A0\uFE0F [LOOP DETECTOR] Patr\xF3n repetitivo de ${loopType} detectado:`);
3901
+ \u26A0 [OUROBOROS SUBSYSTEM: Repetitive ${loopType} loop anomaly detected]:`);
3448
3902
  console.warn(` \xDAltimas acciones: ${lastActions.join(" \u2192 ")}`);
3449
3903
  response.thought = `\u{1F6D1} ERROR: Bucle repetitivo en ${loopType} sobre: ${Array.from(uniqueTargets).join(", ")}.`;
3450
3904
  response.finish = true;
@@ -3452,27 +3906,27 @@ ${patternContext}` : prompt;
3452
3906
  aiMarkedSuccess = false;
3453
3907
  hasCriticalError = true;
3454
3908
  failReason = "loop_detected";
3455
- 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.
3909
+ failReasoning = `Arcality QA entr\xF3 en un bucle l\xF3gico intentando ejecutar la misma acci\xF3n repetidamente sin observar cambios significativos en el DOM.
3456
3910
  Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(uniqueTargets).join(", ")}.`;
3457
3911
  } else if ((isClickLoop || isFillLoop) && agentRecovered) {
3458
- console.log(`>>ARCALITY_STATUS>> \u{1F504} Loop candidato detectado pero el agente se recuper\xF3 en este turno. Continuando misi\xF3n.`);
3912
+ console.log(`>>ARCALITY_STATUS>> \u{1F504} Loop candidato detectado pero Arcality se recuper\xF3 en este turno. Continuando misi\xF3n.`);
3459
3913
  }
3460
3914
  }
3461
3915
  if (!response.finish && history.length >= 3) {
3462
3916
  const lastSixHistory = history.slice(-6);
3463
3917
  const consecutiveWaits = lastSixHistory.filter((h) => h.includes('wait en "')).length;
3464
3918
  if (consecutiveWaits >= 3) {
3465
- const blankEvidencePath = path2.join(contextDir, `portal-blank-state-${Date.now()}.png`);
3919
+ const blankEvidencePath = path4.join(contextDir, `portal-blank-state-${Date.now()}.png`);
3466
3920
  await page.screenshot({ path: blankEvidencePath, fullPage: true }).catch(() => {
3467
3921
  });
3468
3922
  const blankUrl = page.url();
3469
3923
  console.error(`
3470
- \u{1F6D1} [PORTAL RENDERING FAILURE] El agente ejecut\xF3 ${consecutiveWaits} esperas consecutivas sin progreso.`);
3924
+ \u26A0 [VOIDWATCH SUBSYSTEM: Rendering Anomaly] Arcality ejecut\xF3 ${consecutiveWaits} esperas consecutivas sin progreso.`);
3471
3925
  console.error(` >> URL afectada: ${blankUrl}`);
3472
3926
  console.error(` >> El portal carg\xF3 el shell (navegaci\xF3n, header) pero los WIDGETS/COMPONENTES quedaron en blanco.`);
3473
3927
  console.error(` >> Evidencia guardada: ${blankEvidencePath}`);
3474
- console.error(` >> Tipo: portal_rendering_failure | Esto es un bug del portal, NO del agente.`);
3475
- response.thought = `\u{1F6D1} [BUG DEL PORTAL DETECTADO] La p\xE1gina ${blankUrl} carg\xF3 su estructura pero los componentes principales (widgets, tablas, listas) NO se renderizaron. El agente estuvo esperando ${consecutiveWaits} turnos sin mejora. Esto es un fallo de renderizado del portal.`;
3928
+ console.error(` >> Tipo: portal_rendering_failure | Esto es un bug del portal, NO de Arcality.`);
3929
+ response.thought = `\u{1F6D1} [BUG DEL PORTAL DETECTADO] La p\xE1gina ${blankUrl} carg\xF3 su estructura pero los componentes principales (widgets, tablas, listas) NO se renderizaron. Arcality estuvo esperando ${consecutiveWaits} turnos sin mejora. Esto es un fallo de renderizado del portal.`;
3476
3930
  response.finish = true;
3477
3931
  response.actions = [];
3478
3932
  aiMarkedSuccess = false;
@@ -3554,7 +4008,7 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
3554
4008
  const bodyText = await page.innerText("body").catch(() => "");
3555
4009
  if (bodyText.match(/ligado|depende|no se puede/i)) {
3556
4010
  console.log(`
3557
- \u2728 [ESTANCADO] El agente repite click pero el error ya es visible. Finalizando.`);
4011
+ \u2728 [ESTANCADO] Arcality repite click pero el error ya es visible. Finalizando.`);
3558
4012
  isFinished = true;
3559
4013
  aiMarkedSuccess = true;
3560
4014
  break;
@@ -3635,8 +4089,8 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
3635
4089
  lastSeenErrorToast = fbText.trim();
3636
4090
  captureValidationRule(fbText.trim(), `Acci\xF3n: click en "${desc}" en ${page.url()}`);
3637
4091
  const isDuplicateError = /repetido|duplicado|ya existe|already exists/i.test(fbText);
3638
- const correctionHint = isDuplicateError ? `CORRECCI\xD3N OBLIGATORIA: El valor que ingresaste ya existe. Ve al campo que lo caus\xF3 y c\xE1mbialo por uno \xFAnico (a\xF1ade _${Date.now().toString().slice(-4)} como sufijo).` : `CORRECCI\xD3N OBLIGATORIA: Corrige el campo se\xF1alado antes de intentar guardar de nuevo.`;
3639
- history.push(`Turno ${stepCount}: \u{1F6D1} ERROR POST-GUARDADO: "${fbText.trim().substring(0, 200)}". ${correctionHint}`);
4092
+ const correctionHint = isDuplicateError ? `ACCI\xD3N REQUERIDA: Cambia el valor duplicado por uno \xFAnico (agrega _${Date.now().toString().slice(-4)}).` : `ANOMAL\xCDA UI DETECTADA: Si este error previene que la misi\xF3n se complete, usa 'finish: true' y reporta el bug. De lo contrario, corrige el dato.`;
4093
+ history.push(`Turno ${stepCount}: \u{1F6D1} ERROR POST-ACCI\xD3N: "${fbText.trim().substring(0, 200)}". ${correctionHint}`);
3640
4094
  }
3641
4095
  }
3642
4096
  }
@@ -3734,11 +4188,11 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
3734
4188
  const panelCount = Math.min(panels.length, 6);
3735
4189
  if (panelCount >= 2 && emptyPanelCount >= panelCount * 0.6) {
3736
4190
  const blankUrl = page.url();
3737
- const blankEvidencePath = path2.join(contextDir, `portal-blank-dashboard-${Date.now()}.png`);
4191
+ const blankEvidencePath = path4.join(contextDir, `portal-blank-dashboard-${Date.now()}.png`);
3738
4192
  await page.screenshot({ path: blankEvidencePath, fullPage: true }).catch(() => {
3739
4193
  });
3740
4194
  console.error(`
3741
- \u{1F6D1} [BLANK_DASHBOARD] El portal carg\xF3 el shell pero los widgets est\xE1n vac\xEDos.`);
4195
+ \u26A0 [VOIDWATCH SUBSYSTEM: Blank Dashboard Anomaly] El portal carg\xF3 el shell pero los widgets est\xE1n vac\xEDos.`);
3742
4196
  console.error(` >> URL: ${blankUrl}`);
3743
4197
  console.error(` >> ${emptyPanelCount} de ${panelCount} paneles est\xE1n en blanco.`);
3744
4198
  console.error(` >> Tipo: portal_rendering_failure | Bug del portal confirmado.`);
@@ -3746,7 +4200,7 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
3746
4200
  hasCriticalError = true;
3747
4201
  failReason = "portal_rendering_failure";
3748
4202
  isFinished = true;
3749
- history.push(`Turno ${stepCount}: \u{1F6D1} [BLANK_DASHBOARD] El portal carg\xF3 la p\xE1gina ${blankUrl} pero los widgets/tablas NO se renderizaron (${emptyPanelCount}/${panelCount} paneles vac\xEDos). Esto es un BUG DEL PORTAL detectado autom\xE1ticamente por Arcality QA. El agente NO puede continuar. Evidencia guardada: ${blankEvidencePath}`);
4203
+ history.push(`Turno ${stepCount}: \u{1F6D1} [BLANK_DASHBOARD] El portal carg\xF3 la p\xE1gina ${blankUrl} pero los widgets/tablas NO se renderizaron (${emptyPanelCount}/${panelCount} paneles vac\xEDos). Esto es un BUG DEL PORTAL detectado autom\xE1ticamente por Arcality QA. Arcality NO puede continuar. Evidencia guardada: ${blankEvidencePath}`);
3750
4204
  saveMissionResults();
3751
4205
  break;
3752
4206
  }
@@ -3775,19 +4229,19 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
3775
4229
  const errMsg = e.message || "";
3776
4230
  if (errMsg.includes("[BLANK_STATE_CRASH]")) {
3777
4231
  console.error(`
3778
- \u{1F6D1} [BLANK_STATE] El portal carg\xF3 en blanco despu\xE9s de la acci\xF3n "${step.description || step.action}".`);
3779
- console.error(` >> Causa: Error de renderizado del portal (White Screen). El agente no puede continuar.`);
4232
+ \u26A0 [VOIDWATCH SUBSYSTEM: Blank State Crash] El portal carg\xF3 en blanco despu\xE9s de la acci\xF3n "${step.description || step.action}".`);
4233
+ console.error(` >> Causa: Error de renderizado del portal (White Screen). Arcality no puede continuar.`);
3780
4234
  hasCriticalError = true;
3781
4235
  failReason = "portal_render_failure";
3782
4236
  isFinished = true;
3783
- history.push(`Turno ${stepCount}: \u{1F6D1} [BLANK_STATE_CRASH] El portal qued\xF3 en blanco tras navegar. Esto es un bug del portal, no del agente.`);
4237
+ history.push(`Turno ${stepCount}: \u{1F6D1} [BLANK_STATE_CRASH] El portal qued\xF3 en blanco tras navegar. Esto es un bug del portal, no de Arcality.`);
3784
4238
  saveMissionResults();
3785
4239
  break;
3786
4240
  }
3787
4241
  const isPageClosed = errMsg.includes("Target page, context or browser has been closed") || errMsg.includes("page has been closed") || errMsg.includes("Session closed") || page.isClosed();
3788
4242
  if (isPageClosed) {
3789
4243
  console.error(`
3790
- \u{1F6D1} [PAGE CRASH] La pesta\xF1a colaps\xF3 durante la acci\xF3n "${step.description || step.action}"`);
4244
+ \u26A0 [LAZARUS SUBSYSTEM: Critical Crash] La pesta\xF1a colaps\xF3 durante la acci\xF3n "${step.description || step.action}"`);
3791
4245
  console.error(` >> Error: ${errMsg.substring(0, 150)}`);
3792
4246
  console.error(` >> Tipo: portal_page_crash | El portal cerr\xF3 el contexto del navegador inesperadamente.`);
3793
4247
  hasCriticalError = true;
@@ -3847,7 +4301,7 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
3847
4301
  }
3848
4302
  if (response.finish) {
3849
4303
  if (!containsError) {
3850
- console.log(">>ARCALITY_STATUS>> \u2705 El agente solicit\xF3 finalizar misi\xF3n.");
4304
+ console.log(">>ARCALITY_STATUS>> \u2705 Arcality solicit\xF3 finalizar misi\xF3n.");
3851
4305
  isFinished = true;
3852
4306
  if (!response.actions || response.actions.length === 0) {
3853
4307
  stepsData.push({
@@ -3890,13 +4344,13 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
3890
4344
  isFinished = true;
3891
4345
  saveMissionResults();
3892
4346
  } else {
3893
- console.log(`>>ARCALITY_STATUS>> \u26A0\uFE0F El agente intent\xF3 finalizar pero se detect\xF3 un ERROR en alert/toast.`);
4347
+ console.log(`>>ARCALITY_STATUS>> \u26A0\uFE0F Arcality intent\xF3 finalizar pero se detect\xF3 un ERROR en alert/toast.`);
3894
4348
  aiMarkedSuccess = false;
3895
4349
  isFinished = false;
3896
- history.push(`Turno ${stepCount}: El agente intent\xF3 finalizar pero el sistema detect\xF3 un error en un toast/alerta visible.`);
4350
+ history.push(`Turno ${stepCount}: Arcality intent\xF3 finalizar pero el sistema detect\xF3 un error en un toast/alerta visible.`);
3897
4351
  }
3898
4352
  } else {
3899
- console.warn(">>ARCALITY_STATUS>> \u26A0\uFE0F El agente intent\xF3 finalizar pero hubo fallos en el turno. Continuando para investigaci\xF3n...");
4353
+ console.warn(">>ARCALITY_STATUS>> \u26A0\uFE0F Arcality intent\xF3 finalizar pero hubo fallos en el turno. Continuando para investigaci\xF3n...");
3900
4354
  isFinished = false;
3901
4355
  }
3902
4356
  }
@@ -3916,7 +4370,7 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
3916
4370
  failReason = "portal_internal_error";
3917
4371
  isFinished = true;
3918
4372
  aiMarkedSuccess = false;
3919
- history.push(`Turno ${stepCount}: \u{1F6D1} [ERROR_SIST\xC9MICO] El portal devolvi\xF3 un error de servidor en la UI: "${rawTxt.trim().substring(0, 200)}". Este error no puede ser corregido por el agente. Requiere intervenci\xF3n en el backend.`);
4373
+ history.push(`Turno ${stepCount}: \u{1F6D1} [ERROR_SIST\xC9MICO] El portal devolvi\xF3 un error de servidor en la UI: "${rawTxt.trim().substring(0, 200)}". Este error no puede ser corregido por Arcality. Requiere intervenci\xF3n en el backend.`);
3920
4374
  saveMissionResults();
3921
4375
  break;
3922
4376
  } else if (failureKeywords.test(txt)) {
@@ -4015,8 +4469,8 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
4015
4469
  console.log("\u{1F916} [SMART-YAML] Generating mission card...");
4016
4470
  const smartYaml = await agent.summarizeMissionToYaml(prompt, history, target);
4017
4471
  if (smartYaml) {
4018
- const yamlPath = path2.join(contextDir, "last-mission-smart.yaml");
4019
- fs2.writeFileSync(yamlPath, smartYaml);
4472
+ const yamlPath = path4.join(contextDir, "last-mission-smart.yaml");
4473
+ fs4.writeFileSync(yamlPath, smartYaml);
4020
4474
  console.log(`>>ARCALITY_STATUS>> \u{1F4DD} Smart Mission Card generated.`);
4021
4475
  await testInfo.attach("smart_mission_card", {
4022
4476
  body: Buffer.from(smartYaml, "utf-8"),
@@ -4078,7 +4532,7 @@ Evidence: ${JSON.stringify(v.evidence).substring(0, 400)}`,
4078
4532
  finalErrorMsg += `\u{1F3AF} TIPO DE FALLO: ${failReason}
4079
4533
  `;
4080
4534
  if (failReasoning) {
4081
- finalErrorMsg += `\u{1F9E0} AN\xC1LISIS DEL AGENTE DE QA:
4535
+ finalErrorMsg += `\u{1F9E0} AN\xC1LISIS DEL ARCALITY QA:
4082
4536
  ${failReasoning}
4083
4537
  `;
4084
4538
  }
@@ -4088,15 +4542,15 @@ ${failReasoning}
4088
4542
  } else {
4089
4543
  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}`);
4090
4544
  }
4091
- if (fs2.existsSync(contextDir)) {
4545
+ if (fs4.existsSync(contextDir)) {
4092
4546
  try {
4093
- const files = fs2.readdirSync(contextDir);
4547
+ const files = fs4.readdirSync(contextDir);
4094
4548
  for (const file of files) {
4095
4549
  if (file.endsWith(".json") || file.endsWith(".png")) {
4096
- fs2.unlinkSync(path2.join(contextDir, file));
4550
+ fs4.unlinkSync(path4.join(contextDir, file));
4097
4551
  }
4098
4552
  }
4099
- fs2.rmSync(contextDir, { recursive: true, force: true });
4553
+ fs4.rmSync(contextDir, { recursive: true, force: true });
4100
4554
  console.log(`>>ARCALITY_STATUS>> \u{1F9F9} Carpeta context eliminada (Memoria ef\xEDmera completada).`);
4101
4555
  } catch (err) {
4102
4556
  console.warn(`No se pudo limpiar la carpeta context: ${err}`);