@arcadialdev/arcality 4.0.1 → 4.1.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.
|
@@ -16,32 +16,32 @@ const getPid = () => process.env.ARCALITY_PROJECT_ID || '';
|
|
|
16
16
|
const getOrgId = () => process.env.ARCALITY_ORG_ID;
|
|
17
17
|
const getMissionId = () => process.env.ARCALITY_MISSION_ID || EMPTY_GUID;
|
|
18
18
|
|
|
19
|
-
const EMPTY_GUID = '00000000-0000-0000-0000-000000000000';
|
|
20
|
-
|
|
21
|
-
let configWarningLogged = false;
|
|
22
|
-
|
|
23
|
-
function hasRealProjectId(pid: string): boolean {
|
|
24
|
-
if (!pid || pid === EMPTY_GUID) return false;
|
|
25
|
-
if (/^(local|mock)_/i.test(pid)) return false;
|
|
26
|
-
if (pid === 'undefined' || pid === 'null') return false;
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function isConfigured(): boolean {
|
|
19
|
+
const EMPTY_GUID = '00000000-0000-0000-0000-000000000000';
|
|
20
|
+
|
|
21
|
+
let configWarningLogged = false;
|
|
22
|
+
|
|
23
|
+
function hasRealProjectId(pid: string): boolean {
|
|
24
|
+
if (!pid || pid === EMPTY_GUID) return false;
|
|
25
|
+
if (/^(local|mock)_/i.test(pid)) return false;
|
|
26
|
+
if (pid === 'undefined' || pid === 'null') return false;
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isConfigured(): boolean {
|
|
31
31
|
const base = getBase();
|
|
32
32
|
const key = getKey();
|
|
33
33
|
const pid = getPid();
|
|
34
34
|
const orgId = getOrgId();
|
|
35
35
|
|
|
36
|
-
if (!base || !key || !hasRealProjectId(pid) || !orgId) {
|
|
37
|
-
if (!configWarningLogged) {
|
|
38
|
-
const missing = [];
|
|
39
|
-
if (!base) missing.push('ARCALITY_API_URL');
|
|
40
|
-
if (!key) missing.push('ARCALITY_API_KEY');
|
|
41
|
-
if (!hasRealProjectId(pid)) missing.push('ARCALITY_PROJECT_ID');
|
|
36
|
+
if (!base || !key || !hasRealProjectId(pid) || !orgId) {
|
|
37
|
+
if (!configWarningLogged) {
|
|
38
|
+
const missing = [];
|
|
39
|
+
if (!base) missing.push('ARCALITY_API_URL');
|
|
40
|
+
if (!key) missing.push('ARCALITY_API_KEY');
|
|
41
|
+
if (!hasRealProjectId(pid)) missing.push('ARCALITY_PROJECT_ID');
|
|
42
42
|
if (!orgId) missing.push('ARCALITY_ORG_ID');
|
|
43
43
|
|
|
44
|
-
if (process.env.DEBUG) console.warn(`[CollectiveMemory] Service disabled. Missing: ${missing.join(', ')}`);
|
|
44
|
+
if (process.env.DEBUG) console.warn(`[CollectiveMemory] Service disabled. Missing: ${missing.join(', ')}`);
|
|
45
45
|
configWarningLogged = true;
|
|
46
46
|
}
|
|
47
47
|
return false;
|
|
@@ -49,7 +49,7 @@ function isConfigured(): boolean {
|
|
|
49
49
|
return true;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
//
|
|
52
|
+
// ������ 1. REGLAS DE UI / NEGOCIO ��������������������������������������������������������������������������������������������
|
|
53
53
|
|
|
54
54
|
export type RuleType = 'UI_UX' | 'VALIDATION' | 'NAVIGATION' | 'BUSINESS' | 'SECURITY';
|
|
55
55
|
export type Severity = 'critical' | 'important' | 'suggestion';
|
|
@@ -85,7 +85,7 @@ export async function pushRule(rule: {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
const data = await res.json();
|
|
88
|
-
if (process.env.DEBUG) console.log(`\x1b[32m[CollectiveMemory]
|
|
88
|
+
if (process.env.DEBUG) console.log(`\x1b[32m[CollectiveMemory] �S& Regla guardada: "${rule.title}"\x1b[0m`);
|
|
89
89
|
return data.id ?? null;
|
|
90
90
|
} catch (err: any) {
|
|
91
91
|
if (process.env.DEBUG) console.warn(`[CollectiveMemory] pushRule error: ${err?.message}`);
|
|
@@ -93,7 +93,7 @@ export async function pushRule(rule: {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
//
|
|
96
|
+
// ������ 2. CATÁLOGO DE CAMPOS ����������������������������������������������������������������������������������������������������
|
|
97
97
|
|
|
98
98
|
export type FieldType = 'email' | 'password' | 'text' | 'number' | 'select' | 'checkbox' | 'textarea' | 'date';
|
|
99
99
|
|
|
@@ -138,12 +138,12 @@ export async function pushField(field: {
|
|
|
138
138
|
const data = await res.json();
|
|
139
139
|
return data.id ?? null;
|
|
140
140
|
} catch (err: any) {
|
|
141
|
-
// Silencioso
|
|
141
|
+
// Silencioso � fire-and-forget, 'terminated' es esperado al finalizar el proceso
|
|
142
142
|
return null;
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
//
|
|
146
|
+
// ������ 3. CONOCIMIENTO / DOCUMENTACI�N ��������������������������������������������������������������������������������
|
|
147
147
|
|
|
148
148
|
export type DocType = 'PROCESS' | 'VALIDATION' | 'NAVIGATION' | 'COMPONENT' | 'GENERAL';
|
|
149
149
|
|
|
@@ -176,7 +176,7 @@ export async function pushKnowledge(knowledge: {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
const data = await res.json();
|
|
179
|
-
if (process.env.DEBUG) console.log(`\x1b[32m[CollectiveMemory]
|
|
179
|
+
if (process.env.DEBUG) console.log(`\x1b[32m[CollectiveMemory] �S& Conocimiento guardado: "${knowledge.title}"\x1b[0m`);
|
|
180
180
|
return data.id ?? null;
|
|
181
181
|
} catch (err: any) {
|
|
182
182
|
if (process.env.DEBUG) console.warn(`[CollectiveMemory] pushKnowledge error: ${err?.message}`);
|
|
@@ -184,10 +184,13 @@ export async function pushKnowledge(knowledge: {
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
//
|
|
187
|
+
// ������ 4. PROMPT EXECUTION PATTERNS ������������������������������������������������������������������������������������������
|
|
188
188
|
|
|
189
189
|
export async function searchPromptPattern(prompt: string, limit: number = 5): Promise<any[]> {
|
|
190
|
-
if (!isConfigured())
|
|
190
|
+
if (!isConfigured()) {
|
|
191
|
+
console.warn(`[PatternSearch] Omitido: configuraci�n incompleta para prompt-execution-patterns/search`);
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
191
194
|
try {
|
|
192
195
|
const apiUrl = process.env.ARCALITY_API_URL ? `${process.env.ARCALITY_API_URL}/api/v1/prompt-execution-patterns/search` : `${getBase()}/prompt-execution-patterns/search`;
|
|
193
196
|
|
|
@@ -198,10 +201,11 @@ export async function searchPromptPattern(prompt: string, limit: number = 5): Pr
|
|
|
198
201
|
limit: limit
|
|
199
202
|
};
|
|
200
203
|
|
|
201
|
-
const debugPatternSearch = process.env.DEBUG === 'true' || process.env.ARCALITY_DEBUG === 'true';
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
204
|
+
const debugPatternSearch = process.env.DEBUG === 'true' || process.env.ARCALITY_DEBUG === 'true';
|
|
205
|
+
console.log(`[PatternSearch] Iniciando b�squeda | prompt_chars=${prompt.length} | limit=${limit} | project_id=${getPid() || 'missing'} | org_id=${getOrgId() || 'missing'}`);
|
|
206
|
+
if (debugPatternSearch) {
|
|
207
|
+
console.log(`[PatternSearch] POST ${apiUrl} | prompt_preview="${prompt.substring(0, 80).replace(/\s+/g, ' ')}"`);
|
|
208
|
+
}
|
|
205
209
|
|
|
206
210
|
const res = await fetch(apiUrl, {
|
|
207
211
|
method: 'POST',
|
|
@@ -213,20 +217,23 @@ export async function searchPromptPattern(prompt: string, limit: number = 5): Pr
|
|
|
213
217
|
});
|
|
214
218
|
|
|
215
219
|
if (!res.ok) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
console.
|
|
226
|
-
}
|
|
227
|
-
return
|
|
228
|
-
}
|
|
229
|
-
}
|
|
220
|
+
const responseText = await res.text().catch(() => '');
|
|
221
|
+
console.warn(`[PatternSearch] Fall� b�squeda | HTTP ${res.status} | endpoint=${apiUrl}${responseText ? ` | body=${responseText.substring(0, 250)}` : ''}`);
|
|
222
|
+
return [];
|
|
223
|
+
}
|
|
224
|
+
const data = await res.json();
|
|
225
|
+
const results = Array.isArray(data) ? data : (data.matches || data.results || []);
|
|
226
|
+
console.log(`[PatternSearch] Resultado de b�squeda | matches=${results.length}`);
|
|
227
|
+
if (debugPatternSearch && results.length > 0) {
|
|
228
|
+
const bestMatch = results[0] || {};
|
|
229
|
+
console.log(`[PatternSearch] Mejor match | id=${bestMatch.id || 'unknown'} | prompt="${String(bestMatch.prompt || bestMatch.original_prompt || bestMatch.name || '').substring(0, 120)}"`);
|
|
230
|
+
}
|
|
231
|
+
return results;
|
|
232
|
+
} catch (err: any) {
|
|
233
|
+
console.warn(`[PatternSearch] Error buscando patrones: ${err?.message || 'unknown'}`);
|
|
234
|
+
return [];
|
|
235
|
+
}
|
|
236
|
+
}
|
|
230
237
|
|
|
231
238
|
export async function savePromptPattern(patternData: any): Promise<boolean> {
|
|
232
239
|
if (!isConfigured()) return false;
|
|
@@ -239,7 +246,7 @@ export async function savePromptPattern(patternData: any): Promise<boolean> {
|
|
|
239
246
|
mission_id: getMissionId(),
|
|
240
247
|
...patternData
|
|
241
248
|
};
|
|
242
|
-
if (process.env.DEBUG) console.log(`[PatternSave]
|
|
249
|
+
if (process.env.DEBUG) console.log(`[PatternSave] �x� POST ${apiUrl} | org=${payload.organization_id} proj=${payload.project_id} mission=${payload.mission_id}`);
|
|
243
250
|
|
|
244
251
|
const res = await fetch(apiUrl, {
|
|
245
252
|
method: 'POST',
|
|
@@ -251,18 +258,18 @@ export async function savePromptPattern(patternData: any): Promise<boolean> {
|
|
|
251
258
|
});
|
|
252
259
|
|
|
253
260
|
if (!res.ok) {
|
|
254
|
-
if (process.env.DEBUG) console.warn(`[PatternSave]
|
|
261
|
+
if (process.env.DEBUG) console.warn(`[PatternSave] �a�️ HTTP ${res.status} al guardar patrón en ${apiUrl}: ${await res.text().catch(() => '')}`);
|
|
255
262
|
} else {
|
|
256
|
-
if (process.env.DEBUG) console.log(`[PatternSave]
|
|
263
|
+
if (process.env.DEBUG) console.log(`[PatternSave] �S& Patrón guardado exitosamente.`);
|
|
257
264
|
}
|
|
258
265
|
return res.ok;
|
|
259
266
|
} catch (err: any) {
|
|
260
|
-
if (process.env.DEBUG) console.warn(`[PatternSave]
|
|
267
|
+
if (process.env.DEBUG) console.warn(`[PatternSave] �a�️ Error guardando patrón: ${err?.message || 'unknown'}`);
|
|
261
268
|
return false;
|
|
262
269
|
}
|
|
263
270
|
}
|
|
264
271
|
|
|
265
|
-
//
|
|
272
|
+
// ������ HELPER: Obtener campos ya conocidos para deduplicación ����������������������������������
|
|
266
273
|
|
|
267
274
|
export async function getKnownFieldIdentifiers(pathUrl: string): Promise<string[]> {
|
|
268
275
|
if (!isConfigured()) return [];
|
|
@@ -278,3 +285,4 @@ export async function getKnownFieldIdentifiers(pathUrl: string): Promise<string[
|
|
|
278
285
|
return [];
|
|
279
286
|
}
|
|
280
287
|
}
|
|
288
|
+
|
|
@@ -81,7 +81,7 @@ function buildMissionVariant(routeEntry, variant, options = {}) {
|
|
|
81
81
|
name: `${baseName}${variantCode}`,
|
|
82
82
|
prompt,
|
|
83
83
|
page: route,
|
|
84
|
-
page_path: routeEntry.
|
|
84
|
+
page_path: routeEntry.routeExamplePath || route,
|
|
85
85
|
expected_result: expectedResult,
|
|
86
86
|
tags: normalizeTags([...(routeEntry.tags || []), ...(variant.tags || []), ...((guidance.extraTags) || [])], promptFocus),
|
|
87
87
|
priority: guidance.priorityOverride || variant.priority || routeEntry.priority || 'normal',
|
|
@@ -95,6 +95,7 @@ function buildMissionVariant(routeEntry, variant, options = {}) {
|
|
|
95
95
|
context_status: guidance.contextStatus || { qaContext: 'missing', feasibility: 'missing' },
|
|
96
96
|
route_example_path: routeEntry.routeExamplePath || route,
|
|
97
97
|
route_params: routeEntry.routeParams || [],
|
|
98
|
+
source_file_path: routeEntry.pagePath,
|
|
98
99
|
route_hash: simpleHash(`${route}|${routeEntry.pagePath}`),
|
|
99
100
|
prompt_hash: simpleHash(prompt)
|
|
100
101
|
}
|
|
@@ -858,6 +858,25 @@ var assertUrlPatternTool = {
|
|
|
858
858
|
required: ["pattern"]
|
|
859
859
|
}
|
|
860
860
|
};
|
|
861
|
+
var navigateToUrlTool = {
|
|
862
|
+
name: "navigate_to_url",
|
|
863
|
+
description: "QA Skill: Navigate directly to a relative route or absolute URL. Use this whenever the mission mentions a concrete route like '/counter' or when there is no visible menu/link for the target screen. Relative paths are resolved against the current page origin.",
|
|
864
|
+
input_schema: {
|
|
865
|
+
type: "object",
|
|
866
|
+
properties: {
|
|
867
|
+
target_url: {
|
|
868
|
+
type: "string",
|
|
869
|
+
description: "Relative route or absolute URL to open. Examples: '/counter', '/users/123', 'https://example.com/dashboard'."
|
|
870
|
+
},
|
|
871
|
+
wait_until: {
|
|
872
|
+
enum: ["domcontentloaded", "load", "networkidle"],
|
|
873
|
+
description: "Desired Playwright waitUntil strategy after navigation.",
|
|
874
|
+
default: "domcontentloaded"
|
|
875
|
+
}
|
|
876
|
+
},
|
|
877
|
+
required: ["target_url"]
|
|
878
|
+
}
|
|
879
|
+
};
|
|
861
880
|
var measurePagePerformanceTool = {
|
|
862
881
|
name: "measure_page_performance",
|
|
863
882
|
description: "QA Skill: Measure page load time, network requests, and performance metrics (FCP, DOM loaded, total load). Use this to validate performance requirements or SLAs. Returns measured values and PASS/FAIL against optional thresholds.",
|
|
@@ -1058,6 +1077,7 @@ var qaAdvancedTools = [
|
|
|
1058
1077
|
extractTableDataTool,
|
|
1059
1078
|
captureConsoleErrorsTool,
|
|
1060
1079
|
assertUrlPatternTool,
|
|
1080
|
+
navigateToUrlTool,
|
|
1061
1081
|
measurePagePerformanceTool,
|
|
1062
1082
|
saveNavigationCheckpointTool,
|
|
1063
1083
|
restoreNavigationCheckpointTool,
|
|
@@ -1497,7 +1517,7 @@ async function pushRule(rule) {
|
|
|
1497
1517
|
}
|
|
1498
1518
|
const data = await res.json();
|
|
1499
1519
|
if (process.env.DEBUG)
|
|
1500
|
-
console.log(`\x1B[32m[CollectiveMemory] \
|
|
1520
|
+
console.log(`\x1B[32m[CollectiveMemory] \uFFFDS& Regla guardada: "${rule.title}"\x1B[0m`);
|
|
1501
1521
|
return data.id ?? null;
|
|
1502
1522
|
} catch (err) {
|
|
1503
1523
|
if (process.env.DEBUG)
|
|
@@ -1564,7 +1584,7 @@ async function pushKnowledge(knowledge) {
|
|
|
1564
1584
|
}
|
|
1565
1585
|
const data = await res.json();
|
|
1566
1586
|
if (process.env.DEBUG)
|
|
1567
|
-
console.log(`\x1B[32m[CollectiveMemory] \
|
|
1587
|
+
console.log(`\x1B[32m[CollectiveMemory] \uFFFDS& Conocimiento guardado: "${knowledge.title}"\x1B[0m`);
|
|
1568
1588
|
return data.id ?? null;
|
|
1569
1589
|
} catch (err) {
|
|
1570
1590
|
if (process.env.DEBUG)
|
|
@@ -1573,8 +1593,10 @@ async function pushKnowledge(knowledge) {
|
|
|
1573
1593
|
}
|
|
1574
1594
|
}
|
|
1575
1595
|
async function searchPromptPattern(prompt, limit = 5) {
|
|
1576
|
-
if (!isConfigured())
|
|
1596
|
+
if (!isConfigured()) {
|
|
1597
|
+
console.warn(`[PatternSearch] Omitido: configuraci\uFFFDn incompleta para prompt-execution-patterns/search`);
|
|
1577
1598
|
return [];
|
|
1599
|
+
}
|
|
1578
1600
|
try {
|
|
1579
1601
|
const apiUrl = process.env.ARCALITY_API_URL ? `${process.env.ARCALITY_API_URL}/api/v1/prompt-execution-patterns/search` : `${getBase()}/prompt-execution-patterns/search`;
|
|
1580
1602
|
const payload = {
|
|
@@ -1584,8 +1606,9 @@ async function searchPromptPattern(prompt, limit = 5) {
|
|
|
1584
1606
|
limit
|
|
1585
1607
|
};
|
|
1586
1608
|
const debugPatternSearch = process.env.DEBUG === "true" || process.env.ARCALITY_DEBUG === "true";
|
|
1609
|
+
console.log(`[PatternSearch] Iniciando b\uFFFDsqueda | prompt_chars=${prompt.length} | limit=${limit} | project_id=${getPid() || "missing"} | org_id=${getOrgId() || "missing"}`);
|
|
1587
1610
|
if (debugPatternSearch) {
|
|
1588
|
-
console.log(`[PatternSearch] POST ${apiUrl} |
|
|
1611
|
+
console.log(`[PatternSearch] POST ${apiUrl} | prompt_preview="${prompt.substring(0, 80).replace(/\s+/g, " ")}"`);
|
|
1589
1612
|
}
|
|
1590
1613
|
const res = await fetch(apiUrl, {
|
|
1591
1614
|
method: "POST",
|
|
@@ -1596,19 +1619,20 @@ async function searchPromptPattern(prompt, limit = 5) {
|
|
|
1596
1619
|
body: JSON.stringify(payload)
|
|
1597
1620
|
});
|
|
1598
1621
|
if (!res.ok) {
|
|
1599
|
-
|
|
1600
|
-
|
|
1622
|
+
const responseText = await res.text().catch(() => "");
|
|
1623
|
+
console.warn(`[PatternSearch] Fall\uFFFD b\uFFFDsqueda | HTTP ${res.status} | endpoint=${apiUrl}${responseText ? ` | body=${responseText.substring(0, 250)}` : ""}`);
|
|
1601
1624
|
return [];
|
|
1602
1625
|
}
|
|
1603
1626
|
const data = await res.json();
|
|
1604
1627
|
const results = Array.isArray(data) ? data : data.matches || data.results || [];
|
|
1605
|
-
|
|
1606
|
-
|
|
1628
|
+
console.log(`[PatternSearch] Resultado de b\uFFFDsqueda | matches=${results.length}`);
|
|
1629
|
+
if (debugPatternSearch && results.length > 0) {
|
|
1630
|
+
const bestMatch = results[0] || {};
|
|
1631
|
+
console.log(`[PatternSearch] Mejor match | id=${bestMatch.id || "unknown"} | prompt="${String(bestMatch.prompt || bestMatch.original_prompt || bestMatch.name || "").substring(0, 120)}"`);
|
|
1632
|
+
}
|
|
1607
1633
|
return results;
|
|
1608
1634
|
} catch (err) {
|
|
1609
|
-
|
|
1610
|
-
console.warn(`[PatternSearch] Error buscando patrones: ${err?.message || "unknown"}`);
|
|
1611
|
-
}
|
|
1635
|
+
console.warn(`[PatternSearch] Error buscando patrones: ${err?.message || "unknown"}`);
|
|
1612
1636
|
return [];
|
|
1613
1637
|
}
|
|
1614
1638
|
}
|
|
@@ -1624,7 +1648,7 @@ async function savePromptPattern(patternData) {
|
|
|
1624
1648
|
...patternData
|
|
1625
1649
|
};
|
|
1626
1650
|
if (process.env.DEBUG)
|
|
1627
|
-
console.log(`[PatternSave] \
|
|
1651
|
+
console.log(`[PatternSave] \uFFFDx\uFFFD POST ${apiUrl} | org=${payload.organization_id} proj=${payload.project_id} mission=${payload.mission_id}`);
|
|
1628
1652
|
const res = await fetch(apiUrl, {
|
|
1629
1653
|
method: "POST",
|
|
1630
1654
|
headers: {
|
|
@@ -1635,15 +1659,15 @@ async function savePromptPattern(patternData) {
|
|
|
1635
1659
|
});
|
|
1636
1660
|
if (!res.ok) {
|
|
1637
1661
|
if (process.env.DEBUG)
|
|
1638
|
-
console.warn(`[PatternSave] \
|
|
1662
|
+
console.warn(`[PatternSave] \uFFFDa\uFFFD\uFE0F HTTP ${res.status} al guardar patr\xF3n en ${apiUrl}: ${await res.text().catch(() => "")}`);
|
|
1639
1663
|
} else {
|
|
1640
1664
|
if (process.env.DEBUG)
|
|
1641
|
-
console.log(`[PatternSave] \
|
|
1665
|
+
console.log(`[PatternSave] \uFFFDS& Patr\xF3n guardado exitosamente.`);
|
|
1642
1666
|
}
|
|
1643
1667
|
return res.ok;
|
|
1644
1668
|
} catch (err) {
|
|
1645
1669
|
if (process.env.DEBUG)
|
|
1646
|
-
console.warn(`[PatternSave] \
|
|
1670
|
+
console.warn(`[PatternSave] \uFFFDa\uFFFD\uFE0F Error guardando patr\xF3n: ${err?.message || "unknown"}`);
|
|
1647
1671
|
return false;
|
|
1648
1672
|
}
|
|
1649
1673
|
}
|
|
@@ -3064,6 +3088,7 @@ These are NOT optional. You MUST use these tools in these situations:
|
|
|
3064
3088
|
| Before clicking SAVE/GUARDAR | \`validate_element_state\` | To confirm button is enabled |
|
|
3065
3089
|
| After successful SAVE | \`extract_table_data\` | To verify record was created |
|
|
3066
3090
|
| UI is frozen/unresponsive | \`capture_console_errors\` | To check for JS errors |
|
|
3091
|
+
| Mission mentions a concrete route like '/counter' and current URL is different, or the route has no visible menu item | \`navigate_to_url\` | IMMEDIATELY instead of improvising clicks |
|
|
3067
3092
|
| Bug found in application | \`create_test_evidence\` | To document with annotated screenshot |
|
|
3068
3093
|
| Mission complete | \`create_test_evidence\` | To document the final successful state |
|
|
3069
3094
|
| Modal/popup blocking page | \`send_keyboard_event\` (Escape) | To close the overlay |
|
|
@@ -3085,6 +3110,7 @@ These are NOT optional. You MUST use these tools in these situations:
|
|
|
3085
3110
|
|
|
3086
3111
|
3. **CURRENT STATE > MEMORY**: What you see in CURRENT COMPONENTS is the truth. SUCCESS MEMORY is a guide, not gospel.
|
|
3087
3112
|
4. **PAGE TRANSITIONS**: If URL contains '/edit' or you see a 'Save' button, you ARE in the edit view. Don't go back.
|
|
3113
|
+
5. **DIRECT ROUTE NAVIGATION (CRITICAL)**: If the mission mentions a concrete route such as '/counter', '/dashboard', '/users/123', or the reasoning says "navigate directly", you MUST use \`navigate_to_url\`. Do NOT simulate the browser address bar with clicks on logos, headers, or random navigation links.
|
|
3088
3114
|
5. **MENUS & DROPDOWNS**:
|
|
3089
3115
|
- After clicking a toggle/menu, WAIT for the next turn to see options. Never click the same menu icon twice in a row.
|
|
3090
3116
|
- **DROPDOWN COMPLETION RULE (CRITICAL)**: After clicking an option in a dropdown ('[li]', '[option]'), IF the form requires you to fill MORE subsequent fields in the same section, STOP YOUR TURN AFTER SELECTING the option! Do NOT group the select action with filling the next fields. React/Next.js will re-render the DOM, detaching your cached 'idx' references causing 10000ms timeouts. Wait for the next turn to fill the next fields.
|
|
@@ -3510,6 +3536,19 @@ ${history.slice(-25).join("\n") || "None"}`
|
|
|
3510
3536
|
const pass = should_match ? match : !match;
|
|
3511
3537
|
const result = `URL Assertion: ${pass ? "PASS" : "FAIL"} (Current URL: ${url} | Pattern: ${pattern} | Expected match: ${should_match})`;
|
|
3512
3538
|
toolResults.push({ type: "tool_result", tool_use_id: toolUse.id, content: result });
|
|
3539
|
+
} else if (toolName === "navigate_to_url") {
|
|
3540
|
+
const { target_url, wait_until = "domcontentloaded" } = toolInput;
|
|
3541
|
+
console.log(`>>ARCALITY_STATUS>> ?? Navegando directamente a: ${target_url}...`);
|
|
3542
|
+
try {
|
|
3543
|
+
const baseUrl = this.page.url() || process.env.BASE_URL || "http://localhost:3000";
|
|
3544
|
+
const fullUrl = new URL(target_url, baseUrl).toString();
|
|
3545
|
+
await this.page.goto(fullUrl, { waitUntil: wait_until, timeout: 15e3 });
|
|
3546
|
+
await this.page.waitForLoadState("networkidle", { timeout: 3e3 }).catch(() => {
|
|
3547
|
+
});
|
|
3548
|
+
toolResults.push({ type: "tool_result", tool_use_id: toolUse.id, content: `? Navigated to ${fullUrl}. Current URL: ${this.page.url()}` });
|
|
3549
|
+
} catch (e) {
|
|
3550
|
+
toolResults.push({ type: "tool_result", tool_use_id: toolUse.id, content: `Error navigating to URL: ${e.message}` });
|
|
3551
|
+
}
|
|
3513
3552
|
} else if (toolName === "measure_page_performance") {
|
|
3514
3553
|
const { thresholds = {} } = toolInput;
|
|
3515
3554
|
console.log(`>>ARCALITY_STATUS>> \u26A1 Midiendo rendimiento de p\xE1gina...`);
|
|
@@ -4234,6 +4273,30 @@ async function readHiddenPortalResult(page) {
|
|
|
4234
4273
|
}
|
|
4235
4274
|
return null;
|
|
4236
4275
|
}
|
|
4276
|
+
function resolveTargetUrl(baseUrl, currentUrl, targetPath) {
|
|
4277
|
+
if (!targetPath)
|
|
4278
|
+
return currentUrl || baseUrl;
|
|
4279
|
+
if (targetPath.startsWith("http://") || targetPath.startsWith("https://"))
|
|
4280
|
+
return targetPath;
|
|
4281
|
+
const referenceUrl = currentUrl || baseUrl;
|
|
4282
|
+
const parsedBase = new URL(baseUrl || referenceUrl);
|
|
4283
|
+
const parsedReference = new URL(referenceUrl);
|
|
4284
|
+
const normalizedTarget = targetPath.trim();
|
|
4285
|
+
const basePath = parsedBase.pathname.replace(/\/+$/, "");
|
|
4286
|
+
if (normalizedTarget.startsWith("?") || normalizedTarget.startsWith("#")) {
|
|
4287
|
+
return new URL(normalizedTarget, parsedReference).toString();
|
|
4288
|
+
}
|
|
4289
|
+
if (normalizedTarget.startsWith("/")) {
|
|
4290
|
+
const targetAlreadyIncludesBase = basePath && (normalizedTarget === basePath || normalizedTarget.startsWith(`${basePath}/`));
|
|
4291
|
+
const combinedPath = targetAlreadyIncludesBase ? normalizedTarget : `${basePath}${normalizedTarget}`.replace(/\/{2,}/g, "/");
|
|
4292
|
+
return new URL(`${parsedBase.origin}${combinedPath}`).toString();
|
|
4293
|
+
}
|
|
4294
|
+
if (normalizedTarget.startsWith("./") || normalizedTarget.startsWith("../")) {
|
|
4295
|
+
return new URL(normalizedTarget, parsedReference).toString();
|
|
4296
|
+
}
|
|
4297
|
+
const childPath = `${basePath}/${normalizedTarget}`.replace(/\/{2,}/g, "/");
|
|
4298
|
+
return new URL(`${parsedBase.origin}${childPath}`).toString();
|
|
4299
|
+
}
|
|
4237
4300
|
function writeBatchMissionResult(payload) {
|
|
4238
4301
|
const runDomainDir = process.env.ARCALITY_RUN_DOMAIN_DIR;
|
|
4239
4302
|
if (!runDomainDir)
|
|
@@ -4616,16 +4679,18 @@ function writeBatchMissionResult(payload) {
|
|
|
4616
4679
|
console.warn(` \u26A0\uFE0F Login autom\xE1tico omitido o ya autenticado.`);
|
|
4617
4680
|
}
|
|
4618
4681
|
if (target !== "/" && !page.url().includes(target)) {
|
|
4619
|
-
const tgtUrl =
|
|
4682
|
+
const tgtUrl = resolveTargetUrl(base, page.url(), target);
|
|
4620
4683
|
if (!tgtUrl.startsWith("http"))
|
|
4621
4684
|
throw new Error(`URL Inv\xE1lida: "${tgtUrl}"`);
|
|
4685
|
+
console.log(`>>ARCALITY_STATUS>> [Navigation] Navegando a ruta objetivo: ${tgtUrl}`);
|
|
4622
4686
|
await page.goto(tgtUrl).catch(() => {
|
|
4623
4687
|
});
|
|
4624
4688
|
}
|
|
4625
4689
|
} else {
|
|
4626
|
-
const tgtUrl =
|
|
4690
|
+
const tgtUrl = resolveTargetUrl(base, base, target);
|
|
4627
4691
|
if (!tgtUrl.startsWith("http"))
|
|
4628
4692
|
throw new Error(`URL Inv\xE1lida: "${tgtUrl}". Por favor proporciona una URL v\xE1lida incluyendo http:// o https://`);
|
|
4693
|
+
console.log(`>>ARCALITY_STATUS>> [Navigation] Abriendo ruta inicial: ${tgtUrl}`);
|
|
4629
4694
|
await page.goto(tgtUrl);
|
|
4630
4695
|
}
|
|
4631
4696
|
await page.waitForLoadState("networkidle");
|