@arcadialdev/arcality 3.0.2 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,6 +28,24 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  mod
29
29
  ));
30
30
 
31
+ // src/configManager.mjs
32
+ function isRealProjectId2(projectId) {
33
+ const id = String(projectId || "").trim();
34
+ if (!id)
35
+ return false;
36
+ if (id === "undefined" || id === "null")
37
+ return false;
38
+ if (/^(local|mock)_/i.test(id))
39
+ return false;
40
+ if (/^0{8}-0{4}-0{4}-0{4}-0{12}$/i.test(id))
41
+ return false;
42
+ return true;
43
+ }
44
+ var init_configManager = __esm({
45
+ "src/configManager.mjs"() {
46
+ }
47
+ });
48
+
31
49
  // src/configLoader.mjs
32
50
  function getApiUrl() {
33
51
  return "https://arcalityqadev.arcadial.lat";
@@ -63,6 +81,7 @@ var init_configLoader = __esm({
63
81
  import_node_path = __toESM(require("node:path"), 1);
64
82
  import_node_os = __toESM(require("node:os"), 1);
65
83
  import_dotenv = __toESM(require("dotenv"), 1);
84
+ init_configManager();
66
85
  import_dotenv.default.config();
67
86
  CONFIG_DIR = import_node_path.default.join(import_node_os.default.homedir(), ".arcality");
68
87
  CONFIG_FILE = import_node_path.default.join(CONFIG_DIR, "config.json");
@@ -80,44 +99,6 @@ __export(arcalityClient_exports, {
80
99
  startMission: () => startMission,
81
100
  validateApiKey: () => validateApiKey
82
101
  });
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
102
  function loadArcalityConfig() {
122
103
  try {
123
104
  const configPath = import_node_path2.default.join(process.cwd(), "arcality.config");
@@ -143,17 +124,16 @@ function getEffectiveApiKey() {
143
124
  }
144
125
  function loadProjectId() {
145
126
  const localConfig = loadArcalityConfig();
146
- if (localConfig?.projectId)
147
- return localConfig.projectId;
148
- return process.env.ARCALITY_PROJECT_ID || null;
127
+ const projectId = localConfig?.projectId || process.env.ARCALITY_PROJECT_ID || null;
128
+ return isRealProjectId2(projectId) ? projectId : null;
149
129
  }
150
130
  async function validateApiKey() {
151
131
  const key = getEffectiveApiKey();
152
132
  if (!key) {
153
- return { valid: false, error: "no_api_key", mode: "mock" };
133
+ return { valid: false, error: "no_api_key", mode: "live" };
154
134
  }
155
135
  if (!key.startsWith("arc_")) {
156
- return { valid: false, error: "invalid_format", mode: "mock" };
136
+ return { valid: false, error: "invalid_format", mode: "live" };
157
137
  }
158
138
  const apiBase = getEffectiveApiBase();
159
139
  if (apiBase) {
@@ -183,18 +163,11 @@ async function validateApiKey() {
183
163
  return { ...data, valid: true, mode: "live" };
184
164
  } catch (e) {
185
165
  const reason = e?.name === "AbortError" ? "timeout (5s)" : e?.message || "unknown";
186
- console.warn(`\u26A0\uFE0F Backend not available (${apiBase}): ${reason}. Using local mode.`);
166
+ console.warn(`Backend not available (${apiBase}): ${reason}.`);
167
+ return { valid: false, error: "backend_unavailable", mode: "live", reason };
187
168
  }
188
169
  }
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
- };
170
+ return { valid: false, error: "backend_unavailable", mode: "live" };
198
171
  }
199
172
  async function startMission(prompt, targetUrl) {
200
173
  const key = getEffectiveApiKey();
@@ -204,6 +177,9 @@ async function startMission(prompt, targetUrl) {
204
177
  if (apiBase) {
205
178
  try {
206
179
  const projectId = process.env.ARCALITY_PROJECT_ID || loadArcalityConfig()?.projectId;
180
+ if (!isRealProjectId2(projectId)) {
181
+ return { allowed: false, error: "no_project_id" };
182
+ }
207
183
  const controller = new AbortController();
208
184
  const timeout = setTimeout(() => controller.abort(), 5e3);
209
185
  const res = await fetch(`${apiBase}/api/v1/missions/start`, {
@@ -230,32 +206,11 @@ async function startMission(prompt, targetUrl) {
230
206
  return { allowed: true, ...await res.json() };
231
207
  } catch (e) {
232
208
  const reason = e?.name === "AbortError" ? "timeout (5s)" : e?.message || "unknown";
233
- console.warn(`\u26A0\uFE0F startMission failed (${apiBase}): ${reason}. Falling back to mock mode.`);
209
+ console.warn(`startMission failed (${apiBase}): ${reason}.`);
210
+ return { allowed: false, error: "backend_unavailable", reason };
234
211
  }
235
212
  }
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
- };
213
+ return { allowed: false, error: "backend_unavailable" };
259
214
  }
260
215
  async function fetchMissions(projectId, tags) {
261
216
  const key = getEffectiveApiKey();
@@ -263,7 +218,7 @@ async function fetchMissions(projectId, tags) {
263
218
  return { success: false, error: "no_api_key", missions: [] };
264
219
  const apiBase = getEffectiveApiBase();
265
220
  if (!apiBase) {
266
- return { success: true, mode: "mock", missions: [] };
221
+ return { success: false, error: "backend_unavailable", missions: [] };
267
222
  }
268
223
  try {
269
224
  const url = new URL(`${apiBase}/api/v1/projects/${projectId}/missions`);
@@ -295,8 +250,10 @@ async function fetchMissions(projectId, tags) {
295
250
  }
296
251
  async function endMission(missionId, result, usage = null, failReason = null, reportUrl = null, failReasoning = null) {
297
252
  const apiBase = getEffectiveApiBase();
298
- if (!apiBase || !missionId)
299
- return { ok: true };
253
+ if (!apiBase)
254
+ return { ok: false, error: "backend_unavailable" };
255
+ if (!missionId)
256
+ return { ok: false, error: "no_mission_id" };
300
257
  const key = getEffectiveApiKey();
301
258
  try {
302
259
  await fetch(`${apiBase}/api/v1/missions/${missionId}/end`, {
@@ -328,7 +285,7 @@ async function createAdoTask(title, description) {
328
285
  return { success: false, error: "no_api_url" };
329
286
  }
330
287
  const projectId = loadProjectId();
331
- if (!projectId) {
288
+ if (!isRealProjectId2(projectId)) {
332
289
  if (process.env.DEBUG)
333
290
  console.log(`[ADO] No hay Project ID configurado. Omitiendo creaci\xF3n de Task en Azure DevOps.`);
334
291
  return { success: false, error: "no_project_id" };
@@ -377,7 +334,7 @@ async function createAdoTask(title, description) {
377
334
  }
378
335
  async function getEvidenceSasToken(missionId, projectId) {
379
336
  const apiBase = getEffectiveApiBase();
380
- if (!apiBase || !missionId)
337
+ if (!apiBase || !missionId || !isRealProjectId2(projectId))
381
338
  return null;
382
339
  const key = getEffectiveApiKey();
383
340
  if (!key)
@@ -404,13 +361,13 @@ async function getEvidenceSasToken(missionId, projectId) {
404
361
  return null;
405
362
  }
406
363
  }
407
- var import_node_fs2, import_node_path2, DAILY_MISSION_LIMIT, USAGE_FILE, ArcalityClient;
364
+ var import_node_fs2, import_node_path2, USAGE_FILE, ArcalityClient;
408
365
  var init_arcalityClient = __esm({
409
366
  "src/arcalityClient.mjs"() {
410
367
  import_node_fs2 = __toESM(require("node:fs"), 1);
411
368
  import_node_path2 = __toESM(require("node:path"), 1);
412
369
  init_configLoader();
413
- DAILY_MISSION_LIMIT = 35;
370
+ init_configManager();
414
371
  USAGE_FILE = import_node_path2.default.join(CONFIG_DIR, "usage.json");
415
372
  ArcalityClient = class {
416
373
  constructor(apiKey) {
@@ -821,6 +778,18 @@ var path4 = __toESM(require("path"));
821
778
  // src/KnowledgeService.ts
822
779
  var crypto = __toESM(require("crypto"));
823
780
  var import_chalk = __toESM(require("chalk"));
781
+ function isRealProjectId(projectId) {
782
+ const id = String(projectId || "").trim();
783
+ if (!id)
784
+ return false;
785
+ if (id === "undefined" || id === "null")
786
+ return false;
787
+ if (/^(local|mock)_/i.test(id))
788
+ return false;
789
+ if (/^0{8}-0{4}-0{4}-0{4}-0{12}$/i.test(id))
790
+ return false;
791
+ return true;
792
+ }
824
793
  var KnowledgeService = class _KnowledgeService {
825
794
  static instance;
826
795
  apiBase;
@@ -859,10 +828,10 @@ var KnowledgeService = class _KnowledgeService {
859
828
  this.projectId = id;
860
829
  }
861
830
  getProjectId() {
862
- if (!this.projectId || this.projectId === "undefined" || this.projectId === "null") {
831
+ if (!isRealProjectId(this.projectId)) {
863
832
  this.projectId = process.env.ARCALITY_PROJECT_ID || null;
864
833
  }
865
- return this.projectId;
834
+ return isRealProjectId(this.projectId) ? this.projectId : null;
866
835
  }
867
836
  /**
868
837
  * 1. Gestión de Proyectos
@@ -877,7 +846,8 @@ var KnowledgeService = class _KnowledgeService {
877
846
  const data = await res.json();
878
847
  return data.projects || [];
879
848
  } catch (e) {
880
- console.warn(import_chalk.default.yellow(`[KnowledgeService] Error al obtener proyectos: ${e.message}`));
849
+ if (process.env.DEBUG)
850
+ console.warn(import_chalk.default.yellow(`[KnowledgeService] Error al obtener proyectos: ${e.message}`));
881
851
  return [];
882
852
  }
883
853
  }
@@ -895,7 +865,8 @@ var KnowledgeService = class _KnowledgeService {
895
865
  return null;
896
866
  return await res.json();
897
867
  } catch (e) {
898
- console.error(import_chalk.default.red(`[KnowledgeService] Fall\xF3 creaci\xF3n de proyecto: ${e.message}`));
868
+ if (process.env.DEBUG)
869
+ console.error(import_chalk.default.red(`[KnowledgeService] Fall\xF3 creaci\xF3n de proyecto: ${e.message}`));
899
870
  return null;
900
871
  }
901
872
  }
@@ -903,8 +874,9 @@ var KnowledgeService = class _KnowledgeService {
903
874
  * 2. Ingesta (Post-Percept)
904
875
  */
905
876
  async ingest(payload) {
906
- if (!payload.project_id || payload.project_id === "undefined") {
907
- console.warn(import_chalk.default.yellow(`[Knowledge] Ingesta cancelada: project_id es inv\xE1lido.`));
877
+ if (!isRealProjectId(payload.project_id)) {
878
+ if (process.env.DEBUG)
879
+ console.warn(import_chalk.default.yellow(`[Knowledge] Ingesta cancelada: project_id es inv\xE1lido.`));
908
880
  return;
909
881
  }
910
882
  try {
@@ -925,7 +897,8 @@ var KnowledgeService = class _KnowledgeService {
925
897
  }
926
898
  }
927
899
  } catch (e) {
928
- console.warn(import_chalk.default.yellow(`[Knowledge] Error en ingesta: ${e.message}`));
900
+ if (process.env.DEBUG)
901
+ console.warn(import_chalk.default.yellow(`[Knowledge] Error en ingesta: ${e.message}`));
929
902
  }
930
903
  }
931
904
  /**
@@ -948,7 +921,8 @@ var KnowledgeService = class _KnowledgeService {
948
921
  return null;
949
922
  return await res.json();
950
923
  } catch (e) {
951
- console.warn(import_chalk.default.yellow(`[Knowledge] Error al obtener contexto: ${e.message}`));
924
+ if (process.env.DEBUG)
925
+ console.warn(import_chalk.default.yellow(`[Knowledge] Error al obtener contexto: ${e.message}`));
952
926
  return null;
953
927
  }
954
928
  }
@@ -977,7 +951,8 @@ var KnowledgeService = class _KnowledgeService {
977
951
  severity
978
952
  })
979
953
  });
980
- console.log(import_chalk.default.green(`[Knowledge] Nueva regla registrada: ${title}`));
954
+ if (process.env.DEBUG)
955
+ console.log(import_chalk.default.green(`[Knowledge] Nueva regla registrada: ${title}`));
981
956
  } catch (e) {
982
957
  }
983
958
  }
@@ -1004,7 +979,8 @@ var KnowledgeService = class _KnowledgeService {
1004
979
  source: "agent_auto"
1005
980
  })
1006
981
  });
1007
- console.log(import_chalk.default.green(`[Knowledge] Nuevo campo catalogado: ${identifier}`));
982
+ if (process.env.DEBUG)
983
+ console.log(import_chalk.default.green(`[Knowledge] Nuevo campo catalogado: ${identifier}`));
1008
984
  } catch (e) {
1009
985
  }
1010
986
  }
@@ -1031,7 +1007,8 @@ var KnowledgeService = class _KnowledgeService {
1031
1007
  source: "agent_auto"
1032
1008
  })
1033
1009
  });
1034
- console.log(import_chalk.default.green(`[Knowledge] Nuevo conocimiento guardado: ${title}`));
1010
+ if (process.env.DEBUG)
1011
+ console.log(import_chalk.default.green(`[Knowledge] Nuevo conocimiento guardado: ${title}`));
1035
1012
  } catch (e) {
1036
1013
  }
1037
1014
  }
@@ -1052,23 +1029,33 @@ var getOrgId = () => process.env.ARCALITY_ORG_ID;
1052
1029
  var getMissionId = () => process.env.ARCALITY_MISSION_ID || EMPTY_GUID;
1053
1030
  var EMPTY_GUID = "00000000-0000-0000-0000-000000000000";
1054
1031
  var configWarningLogged = false;
1032
+ function hasRealProjectId(pid) {
1033
+ if (!pid || pid === EMPTY_GUID)
1034
+ return false;
1035
+ if (/^(local|mock)_/i.test(pid))
1036
+ return false;
1037
+ if (pid === "undefined" || pid === "null")
1038
+ return false;
1039
+ return true;
1040
+ }
1055
1041
  function isConfigured() {
1056
1042
  const base = getBase();
1057
1043
  const key = getKey();
1058
1044
  const pid = getPid();
1059
1045
  const orgId = getOrgId();
1060
- if (!base || !key || !pid || pid === EMPTY_GUID || !orgId) {
1046
+ if (!base || !key || !hasRealProjectId(pid) || !orgId) {
1061
1047
  if (!configWarningLogged) {
1062
1048
  const missing = [];
1063
1049
  if (!base)
1064
1050
  missing.push("ARCALITY_API_URL");
1065
1051
  if (!key)
1066
1052
  missing.push("ARCALITY_API_KEY");
1067
- if (!pid || pid === EMPTY_GUID)
1053
+ if (!hasRealProjectId(pid))
1068
1054
  missing.push("ARCALITY_PROJECT_ID");
1069
1055
  if (!orgId)
1070
1056
  missing.push("ARCALITY_ORG_ID");
1071
- console.warn(`[CollectiveMemory] Service disabled. Missing: ${missing.join(", ")}`);
1057
+ if (process.env.DEBUG)
1058
+ console.warn(`[CollectiveMemory] Service disabled. Missing: ${missing.join(", ")}`);
1072
1059
  configWarningLogged = true;
1073
1060
  }
1074
1061
  return false;
@@ -1188,8 +1175,10 @@ async function searchPromptPattern(prompt, limit = 5) {
1188
1175
  prompt,
1189
1176
  limit
1190
1177
  };
1191
- console.log(`[PatternSearch] \u{1F50D} Buscando patrones en: ${apiUrl}`);
1192
- console.log(`[PatternSearch] \u{1F4E6} Payload: ${JSON.stringify(payload, null, 2)}`);
1178
+ const debugPatternSearch = process.env.DEBUG === "true" || process.env.ARCALITY_DEBUG === "true";
1179
+ if (debugPatternSearch) {
1180
+ console.log(`[PatternSearch] POST ${apiUrl} | prompt_chars=${prompt.length} | limit=${limit}`);
1181
+ }
1193
1182
  const res = await fetch(apiUrl, {
1194
1183
  method: "POST",
1195
1184
  headers: {
@@ -1199,15 +1188,19 @@ async function searchPromptPattern(prompt, limit = 5) {
1199
1188
  body: JSON.stringify(payload)
1200
1189
  });
1201
1190
  if (!res.ok) {
1202
- console.warn(`[PatternSearch] HTTP ${res.status} from ${apiUrl}`);
1191
+ if (debugPatternSearch)
1192
+ console.warn(`[PatternSearch] HTTP ${res.status} from ${apiUrl}`);
1203
1193
  return [];
1204
1194
  }
1205
1195
  const data = await res.json();
1206
1196
  const results = Array.isArray(data) ? data : data.matches || data.results || [];
1207
- console.log(`[PatternSearch] \u2705 ${results.length} patrones encontrados.`);
1197
+ if (debugPatternSearch)
1198
+ console.log(`[PatternSearch] ${results.length} patrones encontrados.`);
1208
1199
  return results;
1209
1200
  } catch (err) {
1210
- console.warn(`[PatternSearch] \u26A0\uFE0F Error buscando patrones: ${err?.message || "unknown"}`);
1201
+ if (process.env.DEBUG === "true" || process.env.ARCALITY_DEBUG === "true") {
1202
+ console.warn(`[PatternSearch] Error buscando patrones: ${err?.message || "unknown"}`);
1203
+ }
1211
1204
  return [];
1212
1205
  }
1213
1206
  }
@@ -2261,10 +2254,12 @@ ${relevantFailures.map((m) => `- ${m.prompt}`).join("\n")}
2261
2254
  contextData.fields.forEach((f) => memoryBackendContext += `- ${f.field_identifier} (${f.field_type}) - Required: ${f.is_required}
2262
2255
  `);
2263
2256
  }
2264
- console.log(`>>ARCALITY_STATUS>> \u{1F9E0} Contexto de Memoria Colectiva inyectado (${contextData.rules?.length || 0} reglas, ${contextData.fields?.length || 0} campos).`);
2257
+ if (process.env.DEBUG)
2258
+ console.log(`>>ARCALITY_STATUS>> \u{1F9E0} Contexto de Memoria Colectiva inyectado (${contextData.rules?.length || 0} reglas, ${contextData.fields?.length || 0} campos).`);
2265
2259
  }
2266
2260
  } catch (e) {
2267
- console.log(`>>ARCALITY_STATUS>> \u26A0\uFE0F Error en hook de contexto: ${e.message}`);
2261
+ if (process.env.DEBUG)
2262
+ console.log(`>>ARCALITY_STATUS>> \u26A0\uFE0F Error en hook de contexto: ${e.message}`);
2268
2263
  }
2269
2264
  let customUserContext = "";
2270
2265
  try {
@@ -3445,6 +3440,31 @@ function captureValidationRule(errorMessage, context) {
3445
3440
  }).catch(() => {
3446
3441
  });
3447
3442
  }
3443
+ async function readLocatorText(locator) {
3444
+ const textContent = await locator.textContent().catch(() => null);
3445
+ if (typeof textContent === "string") {
3446
+ const normalized = textContent.trim();
3447
+ if (normalized)
3448
+ return normalized;
3449
+ }
3450
+ const innerText = await locator.innerText().catch(() => "");
3451
+ return typeof innerText === "string" ? innerText.trim() : "";
3452
+ }
3453
+ function writeBatchMissionResult(payload) {
3454
+ const runDomainDir = process.env.ARCALITY_RUN_DOMAIN_DIR;
3455
+ if (!runDomainDir)
3456
+ return;
3457
+ try {
3458
+ if (!fs5.existsSync(runDomainDir)) {
3459
+ fs5.mkdirSync(runDomainDir, { recursive: true });
3460
+ }
3461
+ fs5.writeFileSync(
3462
+ path5.join(runDomainDir, "mission-result.json"),
3463
+ JSON.stringify(payload, null, 2)
3464
+ );
3465
+ } catch {
3466
+ }
3467
+ }
3448
3468
  (0, import_test.test)("Arcality AI Runner", async ({ page }, testInfo) => {
3449
3469
  import_test.test.setTimeout(12e5);
3450
3470
  const contextDir = process.env.CONTEXT_DIR || "out";
@@ -3588,15 +3608,55 @@ function captureValidationRule(errorMessage, context) {
3588
3608
  };
3589
3609
  memories.push(missionData);
3590
3610
  fs5.writeFileSync(memoryFile2, JSON.stringify(memories, null, 2));
3591
- console.log(`>>ARCALITY_STATUS>> \u{1F9E0} Memoria guardada: ${cleanSteps.length} pasos limpios (${rawSteps.length - cleanSteps.length} corruptos filtrados)`);
3611
+ if (process.env.DEBUG)
3612
+ console.log(`>>ARCALITY_STATUS>> \u{1F9E0} Memoria guardada: ${cleanSteps.length} pasos limpios (${rawSteps.length - cleanSteps.length} corruptos filtrados)`);
3592
3613
  } catch (err) {
3593
3614
  console.error("\u274C Fall\xF3 al guardar memoria:", err);
3594
3615
  }
3595
3616
  try {
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));
3617
+ const timestampIso = (/* @__PURE__ */ new Date()).toISOString();
3618
+ const timestampMs = Date.now();
3619
+ const summaryPayload = {
3620
+ prompt,
3621
+ history,
3622
+ steps: stepCount,
3623
+ success: finalSuccess,
3624
+ error: hasCriticalError,
3625
+ fail_reason: failReason,
3626
+ fail_reasoning: failReasoning,
3627
+ usage: totalMissionUsage,
3628
+ target: process.env.BASE_URL || "",
3629
+ target_path: process.env.TARGET_PATH || "",
3630
+ report_dir: process.env.REPORTS_DIR || "",
3631
+ timestamp: timestampIso
3632
+ };
3633
+ const logPath = path5.join(contextDir, `agent-log-${timestampMs}.json`);
3634
+ fs5.writeFileSync(logPath, JSON.stringify(summaryPayload, null, 2));
3635
+ const runDomainDir = process.env.ARCALITY_RUN_DOMAIN_DIR || path5.dirname(contextDir);
3636
+ const durableLogsDir = path5.join(runDomainDir, "logs");
3637
+ fs5.mkdirSync(durableLogsDir, { recursive: true });
3638
+ fs5.writeFileSync(
3639
+ path5.join(durableLogsDir, `mission-log-${timestampMs}.json`),
3640
+ JSON.stringify(summaryPayload, null, 2)
3641
+ );
3642
+ writeBatchMissionResult(summaryPayload);
3598
3643
  try {
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");
3644
+ const historyLine = JSON.stringify({
3645
+ ts: timestampIso,
3646
+ mission: prompt.substring(0, 150),
3647
+ success: finalSuccess,
3648
+ steps: stepCount,
3649
+ fail_reason: failReason,
3650
+ report_dir: process.env.REPORTS_DIR || ""
3651
+ }) + "\n";
3652
+ fs5.appendFileSync(
3653
+ path5.join(contextDir, "arcality-history.log"),
3654
+ historyLine
3655
+ );
3656
+ fs5.appendFileSync(
3657
+ path5.join(durableLogsDir, "arcality-history.log"),
3658
+ historyLine
3659
+ );
3600
3660
  } catch (e) {
3601
3661
  }
3602
3662
  } catch (err) {
@@ -3618,7 +3678,8 @@ function captureValidationRule(errorMessage, context) {
3618
3678
  return;
3619
3679
  }
3620
3680
  if (!isFailed && stepsData.length === 0 && stepsDataBackup.length === 0) {
3621
- console.warn(`[CollectiveMemory] \u26A0\uFE0F stepsData vac\xEDo en path de \xE9xito \u2014 intentando usar backup...`);
3681
+ if (process.env.DEBUG)
3682
+ console.warn(`[CollectiveMemory] \u26A0\uFE0F stepsData vac\xEDo en path de \xE9xito \u2014 intentando usar backup...`);
3622
3683
  }
3623
3684
  const baseSteps = stepsData.length > 0 ? stepsData : stepsDataBackup;
3624
3685
  const effectiveSteps = baseSteps.filter((step) => {
@@ -3629,11 +3690,15 @@ function captureValidationRule(errorMessage, context) {
3629
3690
  }
3630
3691
  return true;
3631
3692
  });
3632
- console.log(`
3693
+ if (process.env.DEBUG)
3694
+ console.log(`
3633
3695
  ======================================================`);
3634
- console.log(`\u{1F9E0} [ARCHIVE SUBSYSTEM - KNOWLEDGE INGESTION]`);
3635
- console.log(`>>arcality>> Saving telemetry (project: ${pid}, steps: ${effectiveSteps.length}, isFailed: ${isFailed})...`);
3636
- console.log(`======================================================
3696
+ if (process.env.DEBUG)
3697
+ console.log(`\u{1F9E0} [ARCHIVE SUBSYSTEM - KNOWLEDGE INGESTION]`);
3698
+ if (process.env.DEBUG)
3699
+ console.log(`>>arcality>> Saving telemetry (project: ${pid}, steps: ${effectiveSteps.length}, isFailed: ${isFailed})...`);
3700
+ if (process.env.DEBUG)
3701
+ console.log(`======================================================
3637
3702
  `);
3638
3703
  try {
3639
3704
  const stepsNarrative = effectiveSteps.map((s) => `${s.action_data?.action || "action"} "${s.componentName}" en ${s.url}`).join(" \u2192 ").substring(0, 500);
@@ -3644,9 +3709,9 @@ function captureValidationRule(errorMessage, context) {
3644
3709
  title: `Flujo exitoso: ${prompt.substring(0, 100)}`,
3645
3710
  content: stepsNarrative || prompt.substring(0, 500)
3646
3711
  });
3647
- if (knowledgeId)
3712
+ if (process.env.DEBUG && knowledgeId)
3648
3713
  console.log(`\u{1F9E0} [COLLECTIVE MEMORY] \u2705 Knowledge guardado: ${knowledgeId}`);
3649
- else
3714
+ else if (process.env.DEBUG)
3650
3715
  console.warn(`[CollectiveMemory] \u26A0\uFE0F pushKnowledge retorn\xF3 null (revisar HTTP status arriba)`);
3651
3716
  const uniqueUrls = [...new Set(effectiveSteps.map((s) => s.url).filter(Boolean))];
3652
3717
  if (uniqueUrls.length > 1) {
@@ -3656,7 +3721,7 @@ function captureValidationRule(errorMessage, context) {
3656
3721
  description: `Arcality naveg\xF3 ${uniqueUrls.length} p\xE1ginas: ${uniqueUrls.join(" \u2192 ").substring(0, 400)}`,
3657
3722
  severity: "suggestion"
3658
3723
  });
3659
- if (ruleId)
3724
+ if (process.env.DEBUG && ruleId)
3660
3725
  console.log(`\u{1F9E0} [COLLECTIVE MEMORY] \u2705 Regla NAVIGATION guardada: ${ruleId}`);
3661
3726
  }
3662
3727
  const submitStep = effectiveSteps.find(
@@ -3669,7 +3734,7 @@ function captureValidationRule(errorMessage, context) {
3669
3734
  description: `Flujo exitoso incluy\xF3 guardado/creaci\xF3n. Pasos: ${stepsNarrative.substring(0, 400)}`,
3670
3735
  severity: "important"
3671
3736
  });
3672
- if (ruleId2)
3737
+ if (process.env.DEBUG && ruleId2)
3673
3738
  console.log(`\u{1F9E0} [COLLECTIVE MEMORY] \u2705 Regla UI_UX guardada: ${ruleId2}`);
3674
3739
  }
3675
3740
  } else {
@@ -3679,13 +3744,14 @@ function captureValidationRule(errorMessage, context) {
3679
3744
  description: `Arcality no pudo completar: "${prompt.substring(0, 200)}". \xDAltimos pasos: ${stepsNarrative.substring(0, 300)}`,
3680
3745
  severity: "important"
3681
3746
  });
3682
- if (failRuleId)
3747
+ if (process.env.DEBUG && failRuleId)
3683
3748
  console.log(`\u{1F9E0} [COLLECTIVE MEMORY] \u2705 Regla de fallo guardada: ${failRuleId}`);
3684
- else
3749
+ else if (process.env.DEBUG)
3685
3750
  console.warn(`[CollectiveMemory] \u26A0\uFE0F No se pudo persistir misi\xF3n fallida`);
3686
3751
  }
3687
3752
  } catch (err) {
3688
- console.warn(`[CollectiveMemory] \u274C Error inesperado en persistCollectiveMemory: ${err?.message}`);
3753
+ if (process.env.DEBUG)
3754
+ console.warn(`[CollectiveMemory] \u274C Error inesperado en persistCollectiveMemory: ${err?.message}`);
3689
3755
  }
3690
3756
  };
3691
3757
  console.log(`
@@ -3750,7 +3816,8 @@ function captureValidationRule(errorMessage, context) {
3750
3816
  const matches = promptKeywords.filter((kw) => patternKeywords.some((pkw) => pkw.includes(kw) || kw.includes(pkw))).length;
3751
3817
  const isVectorMatch = true;
3752
3818
  if (isVectorMatch) {
3753
- console.log(`>>ARCALITY_STATUS>> \u{1F9E0} Memoria Colectiva detectada (id: ${bestMatch.id}). Descargando a memoria local...`);
3819
+ if (process.env.DEBUG)
3820
+ console.log(`>>ARCALITY_STATUS>> \u{1F9E0} Memoria Colectiva detectada (id: ${bestMatch.id}). Descargando a memoria local...`);
3754
3821
  reusedPatternId = bestMatch.id || "unknown";
3755
3822
  let patternJsonObj = {};
3756
3823
  try {
@@ -3869,7 +3936,7 @@ ${readableHistory}`;
3869
3936
  let foundSuccessToast = false;
3870
3937
  for (const el of feedbackEls) {
3871
3938
  if (await el.isVisible().catch(() => false)) {
3872
- const txt = await el.innerText().catch(() => "");
3939
+ const txt = await readLocatorText(el);
3873
3940
  if (successKeywords.test(txt.toLowerCase())) {
3874
3941
  foundSuccessToast = true;
3875
3942
  console.log(`>>ARCALITY_STATUS>> \u2728 Auto-Success: Toast de \xE9xito detectado: "${txt.substring(0, 60)}"`);
@@ -3894,7 +3961,7 @@ ${readableHistory}`;
3894
3961
  if (btnCount > 0) {
3895
3962
  const firstBtn = finalSaveBtn.first();
3896
3963
  if (await firstBtn.isVisible().catch(() => false) && await firstBtn.isEnabled().catch(() => false)) {
3897
- const btnText = await firstBtn.innerText().catch(() => "");
3964
+ const btnText = await readLocatorText(firstBtn);
3898
3965
  const isModalOpen = await page.locator('[role="dialog"], .modal, .mat-dialog-container').first().isVisible().catch(() => false);
3899
3966
  if (!isModalOpen) {
3900
3967
  console.log(`
@@ -3907,7 +3974,7 @@ ${readableHistory}`;
3907
3974
  let proactiveSuccess = false;
3908
3975
  for (const fb of postSaveFb) {
3909
3976
  if (await fb.isVisible().catch(() => false)) {
3910
- const fbTxt = await fb.innerText().catch(() => "");
3977
+ const fbTxt = await readLocatorText(fb);
3911
3978
  if (successKeywords.test(fbTxt)) {
3912
3979
  console.log(`>>ARCALITY_STATUS>> \u2728 [PROACTIVE FINISH] \xC9xito confirmado por toast: "${fbTxt.substring(0, 60)}"`);
3913
3980
  proactiveSuccess = true;
@@ -3933,7 +4000,7 @@ ${readableHistory}`;
3933
4000
  const initialToasts = await page.locator('.toast, .alert, [role="status"], [role="alert"]').all();
3934
4001
  for (const t of initialToasts) {
3935
4002
  if (await t.isVisible().catch(() => false)) {
3936
- lastSeenSuccessToast = await t.innerText().catch(() => "");
4003
+ lastSeenSuccessToast = await readLocatorText(t);
3937
4004
  if (lastSeenSuccessToast)
3938
4005
  console.log(`>>ARCALITY_STATUS>> \u{1F4A1} Nota: Ignorando mensaje preexistente: "${lastSeenSuccessToast.substring(0, 30)}..."`);
3939
4006
  }
@@ -3967,7 +4034,7 @@ Detalles de la excepci\xF3n en consola: ${jsError}`;
3967
4034
  const systemToastEls = await page.locator('.toast, .alert, [role="alert"], [role="status"], .snackbar, .notification, .error-message, .mat-snack-bar-container').all();
3968
4035
  for (const el of systemToastEls) {
3969
4036
  if (await el.isVisible().catch(() => false)) {
3970
- const sysErrTxt = await el.innerText().catch(() => "");
4037
+ const sysErrTxt = await readLocatorText(el);
3971
4038
  if (sysErrTxt && systemErrorKeywords.test(sysErrTxt)) {
3972
4039
  console.error(`
3973
4040
  \u{1F6D1} [SYSTEM ERROR DETECTADO] El portal report\xF3 un error de sistema no recuperable en UI:`);
@@ -3992,7 +4059,7 @@ Detalles de la excepci\xF3n en consola: ${jsError}`;
3992
4059
  const errorEls = await page.locator('.toast, .alert, [role="alert"], [role="status"], .snackbar, .notification, .error-message, .mat-snack-bar-container, [class*="alert"], [class*="toast"]').all();
3993
4060
  for (const el of errorEls) {
3994
4061
  if (await el.isVisible().catch(() => false)) {
3995
- const errTxt = await el.innerText().catch(() => "");
4062
+ const errTxt = await readLocatorText(el);
3996
4063
  if (errTxt && failureKeywords.test(errTxt.toLowerCase())) {
3997
4064
  hasVisibleError = true;
3998
4065
  break;
@@ -4022,7 +4089,7 @@ Detalles de la excepci\xF3n en consola: ${jsError}`;
4022
4089
  const errorEls = await page.locator('.toast, .alert, [role="alert"], [role="status"], .snackbar, .notification, .error-message, .mat-snack-bar-container, [class*="alert"], [class*="toast"]').all();
4023
4090
  for (const el of errorEls) {
4024
4091
  if (await el.isVisible().catch(() => false)) {
4025
- const errTxt = await el.innerText().catch(() => "");
4092
+ const errTxt = await readLocatorText(el);
4026
4093
  if (errTxt && failureKeywords.test(errTxt.toLowerCase())) {
4027
4094
  const isDuplicateError = /repetido|duplicado|ya existe|already exists/i.test(errTxt);
4028
4095
  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.`;
@@ -4196,7 +4263,7 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
4196
4263
  const isCriticalSuccess = textLower.includes("exitosamente") || textLower.includes("guardado") || textLower.includes("creado") || textLower.includes("success") || textLower.includes("correctamente") || textLower.includes("misi\xF3n cumplida");
4197
4264
  const isCriticalFeedback = isCriticalFailure || isCriticalSuccess;
4198
4265
  if (!isCriticalFeedback && step.type && ["span", "p", "label", "i", "svg", "img"].includes(step.type)) {
4199
- const elementText = await loc.innerText().catch(() => "");
4266
+ const elementText = await readLocatorText(loc);
4200
4267
  const elementTextLower = elementText.toLowerCase();
4201
4268
  if (failureKeywords.test(elementTextLower)) {
4202
4269
  console.log(`>>ARCALITY_STATUS>> \u26A0\uFE0F Feedback de ERROR en elemento peque\xF1o: "${elementTextLower.substring(0, 50)}..."`);
@@ -4288,7 +4355,7 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
4288
4355
  let foundSuccessPost = false;
4289
4356
  for (const fb of postSubmitFeedback) {
4290
4357
  if (await fb.isVisible().catch(() => false)) {
4291
- const fbText = await fb.innerText().catch(() => "");
4358
+ const fbText = await readLocatorText(fb);
4292
4359
  const postSuccessKw = /exitosamente|creado correctamente|guardado correctamente|operación exitosa|registro guardado|saved successfully|created successfully/i;
4293
4360
  if (postSuccessKw.test(fbText)) {
4294
4361
  console.log(`>>ARCALITY_STATUS>> \u2728 \xC9xito visual detectado en toast: "${fbText.substring(0, 60)}"`);
@@ -4341,7 +4408,7 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
4341
4408
  let hasPostNavError = false;
4342
4409
  for (const alertEl of postNavAlerts) {
4343
4410
  if (await alertEl.isVisible().catch(() => false)) {
4344
- const alertTxt = await alertEl.innerText().catch(() => "");
4411
+ const alertTxt = await readLocatorText(alertEl);
4345
4412
  if (failureKeywords.test(alertTxt.toLowerCase())) {
4346
4413
  hasPostNavError = true;
4347
4414
  console.log(`>>ARCALITY_STATUS>> \u26A0\uFE0F Error en alerta post-guardado: "${alertTxt.substring(0, 60)}"`);
@@ -4413,7 +4480,7 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
4413
4480
  const panels = await page.locator("igc-dockmanager [slot]").all();
4414
4481
  let emptyPanelCount = 0;
4415
4482
  for (const panel of panels.slice(0, 6)) {
4416
- const txt = (await panel.innerText().catch(() => "")).trim();
4483
+ const txt = await readLocatorText(panel);
4417
4484
  const isVisible = await panel.isVisible().catch(() => false);
4418
4485
  if (isVisible && txt.length < 10)
4419
4486
  emptyPanelCount++;
@@ -4534,7 +4601,8 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
4534
4601
  }
4535
4602
  if (response.finish) {
4536
4603
  if (!containsError) {
4537
- console.log(">>ARCALITY_STATUS>> \u2705 Arcality solicit\xF3 finalizar misi\xF3n.");
4604
+ if (process.env.DEBUG)
4605
+ console.log(">>ARCALITY_STATUS>> \u2705 Arcality solicit\xF3 finalizar misi\xF3n.");
4538
4606
  isFinished = true;
4539
4607
  if (!response.actions || response.actions.length === 0) {
4540
4608
  stepsData.push({
@@ -4554,7 +4622,7 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
4554
4622
  let hasVisibleFailure = false;
4555
4623
  for (const el of finishFeedbackEls) {
4556
4624
  if (await el.isVisible().catch(() => false)) {
4557
- const txt = await el.innerText().catch(() => "");
4625
+ const txt = await readLocatorText(el);
4558
4626
  if (failureKeywords.test(txt.toLowerCase())) {
4559
4627
  hasVisibleFailure = true;
4560
4628
  console.log(`>>ARCALITY_STATUS>> \u26A0\uFE0F Error detectado en toast/alerta: "${txt.substring(0, 60)}"`);
@@ -4592,8 +4660,10 @@ Acci\xF3n bloqueada: Bucle de tipo ${loopType} sobre los elementos ${Array.from(
4592
4660
  if (stepCount > 2) {
4593
4661
  const allFeedback = await feedbackLocator.all();
4594
4662
  for (const fb of allFeedback) {
4595
- if (await fb.isVisible()) {
4596
- const rawTxt = await fb.innerText();
4663
+ if (await fb.isVisible().catch(() => false)) {
4664
+ const rawTxt = await readLocatorText(fb);
4665
+ if (!rawTxt)
4666
+ continue;
4597
4667
  const txt = rawTxt.toLowerCase();
4598
4668
  if (systemErrorKeywords.test(rawTxt)) {
4599
4669
  console.error(`
@@ -4735,7 +4805,8 @@ Evidence: ${JSON.stringify(v.evidence).substring(0, 400)}`,
4735
4805
  });
4736
4806
  }
4737
4807
  } catch (e) {
4738
- console.warn(">>ARCALITY_STATUS>> \u26A0\uFE0F Fall\xF3 persistir hallazgos de seguridad en Memoria Colectiva.");
4808
+ if (process.env.DEBUG)
4809
+ console.warn(">>ARCALITY_STATUS>> \u26A0\uFE0F Fall\xF3 persistir hallazgos de seguridad en Memoria Colectiva.");
4739
4810
  }
4740
4811
  } else {
4741
4812
  console.log(`>>ARCALITY_STATUS>> \u{1F6E1}\uFE0F Escaneo de seguridad completado. No se encontraron problemas.`);
@@ -4754,7 +4825,8 @@ Evidence: ${JSON.stringify(v.evidence).substring(0, 400)}`,
4754
4825
  }
4755
4826
  saveMissionResults();
4756
4827
  if (!aiMarkedSuccess) {
4757
- 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}`);
4828
+ if (process.env.DEBUG)
4829
+ 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}`);
4758
4830
  await persistCollectiveMemory(true).catch(() => {
4759
4831
  });
4760
4832
  let finalErrorMsg = `Misi\xF3n no completada o finalizada con errores.
@@ -4773,7 +4845,8 @@ ${failReasoning}
4773
4845
  `;
4774
4846
  throw new Error(finalErrorMsg);
4775
4847
  } else {
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}`);
4848
+ if (process.env.DEBUG)
4849
+ 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}`);
4777
4850
  }
4778
4851
  if (fs5.existsSync(contextDir)) {
4779
4852
  try {