@arcadialdev/arcality 2.4.30 → 2.4.31

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.
Files changed (77) hide show
  1. package/.agents/skills/e2e-testing-expert/SKILL.md +28 -0
  2. package/.agents/skills/frontend-design/LICENSE.txt +177 -0
  3. package/.agents/skills/frontend-design/SKILL.md +42 -0
  4. package/.agents/skills/nodejs-backend-patterns/SKILL.md +639 -0
  5. package/.agents/skills/nodejs-backend-patterns/references/advanced-patterns.md +430 -0
  6. package/.agents/skills/playwright-best-practices/LICENSE.md +7 -0
  7. package/.agents/skills/playwright-best-practices/README.md +147 -0
  8. package/.agents/skills/playwright-best-practices/SKILL.md +303 -0
  9. package/.agents/skills/playwright-best-practices/advanced/authentication-flows.md +360 -0
  10. package/.agents/skills/playwright-best-practices/advanced/authentication.md +871 -0
  11. package/.agents/skills/playwright-best-practices/advanced/clock-mocking.md +364 -0
  12. package/.agents/skills/playwright-best-practices/advanced/mobile-testing.md +409 -0
  13. package/.agents/skills/playwright-best-practices/advanced/multi-context.md +288 -0
  14. package/.agents/skills/playwright-best-practices/advanced/multi-user.md +393 -0
  15. package/.agents/skills/playwright-best-practices/advanced/network-advanced.md +452 -0
  16. package/.agents/skills/playwright-best-practices/advanced/third-party.md +464 -0
  17. package/.agents/skills/playwright-best-practices/architecture/pom-vs-fixtures.md +363 -0
  18. package/.agents/skills/playwright-best-practices/architecture/test-architecture.md +369 -0
  19. package/.agents/skills/playwright-best-practices/architecture/when-to-mock.md +383 -0
  20. package/.agents/skills/playwright-best-practices/browser-apis/browser-apis.md +391 -0
  21. package/.agents/skills/playwright-best-practices/browser-apis/iframes.md +403 -0
  22. package/.agents/skills/playwright-best-practices/browser-apis/service-workers.md +504 -0
  23. package/.agents/skills/playwright-best-practices/browser-apis/websockets.md +403 -0
  24. package/.agents/skills/playwright-best-practices/core/annotations.md +424 -0
  25. package/.agents/skills/playwright-best-practices/core/assertions-waiting.md +361 -0
  26. package/.agents/skills/playwright-best-practices/core/configuration.md +452 -0
  27. package/.agents/skills/playwright-best-practices/core/fixtures-hooks.md +417 -0
  28. package/.agents/skills/playwright-best-practices/core/global-setup.md +434 -0
  29. package/.agents/skills/playwright-best-practices/core/locators.md +242 -0
  30. package/.agents/skills/playwright-best-practices/core/page-object-model.md +315 -0
  31. package/.agents/skills/playwright-best-practices/core/projects-dependencies.md +453 -0
  32. package/.agents/skills/playwright-best-practices/core/test-data.md +492 -0
  33. package/.agents/skills/playwright-best-practices/core/test-suite-structure.md +361 -0
  34. package/.agents/skills/playwright-best-practices/core/test-tags.md +298 -0
  35. package/.agents/skills/playwright-best-practices/debugging/console-errors.md +420 -0
  36. package/.agents/skills/playwright-best-practices/debugging/debugging.md +504 -0
  37. package/.agents/skills/playwright-best-practices/debugging/error-testing.md +360 -0
  38. package/.agents/skills/playwright-best-practices/debugging/flaky-tests.md +496 -0
  39. package/.agents/skills/playwright-best-practices/frameworks/angular.md +530 -0
  40. package/.agents/skills/playwright-best-practices/frameworks/nextjs.md +469 -0
  41. package/.agents/skills/playwright-best-practices/frameworks/react.md +531 -0
  42. package/.agents/skills/playwright-best-practices/frameworks/vue.md +574 -0
  43. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/ci-cd.md +468 -0
  44. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/docker.md +283 -0
  45. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/github-actions.md +546 -0
  46. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/gitlab.md +397 -0
  47. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/other-providers.md +521 -0
  48. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/parallel-sharding.md +371 -0
  49. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/performance.md +453 -0
  50. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/reporting.md +424 -0
  51. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/test-coverage.md +497 -0
  52. package/.agents/skills/playwright-best-practices/testing-patterns/accessibility.md +359 -0
  53. package/.agents/skills/playwright-best-practices/testing-patterns/api-testing.md +719 -0
  54. package/.agents/skills/playwright-best-practices/testing-patterns/browser-extensions.md +506 -0
  55. package/.agents/skills/playwright-best-practices/testing-patterns/canvas-webgl.md +493 -0
  56. package/.agents/skills/playwright-best-practices/testing-patterns/component-testing.md +500 -0
  57. package/.agents/skills/playwright-best-practices/testing-patterns/drag-drop.md +576 -0
  58. package/.agents/skills/playwright-best-practices/testing-patterns/electron.md +509 -0
  59. package/.agents/skills/playwright-best-practices/testing-patterns/file-operations.md +377 -0
  60. package/.agents/skills/playwright-best-practices/testing-patterns/file-upload-download.md +562 -0
  61. package/.agents/skills/playwright-best-practices/testing-patterns/forms-validation.md +561 -0
  62. package/.agents/skills/playwright-best-practices/testing-patterns/graphql-testing.md +331 -0
  63. package/.agents/skills/playwright-best-practices/testing-patterns/i18n.md +508 -0
  64. package/.agents/skills/playwright-best-practices/testing-patterns/performance-testing.md +476 -0
  65. package/.agents/skills/playwright-best-practices/testing-patterns/security-testing.md +430 -0
  66. package/.agents/skills/playwright-best-practices/testing-patterns/visual-regression.md +634 -0
  67. package/.agents/skills/security-qa/SKILL.md +254 -0
  68. package/package.json +2 -1
  69. package/scripts/gen-and-run.mjs +2 -2
  70. package/scripts/init.mjs +2 -2
  71. package/scripts/setup.mjs +2 -2
  72. package/src/arcalityClient.mjs +1 -1
  73. package/tests/_helpers/agentic-runner.bundle.spec.js +83 -8
  74. package/scripts/cleanup-qmsdev.mjs +0 -63
  75. package/scripts/discover-view.mjs +0 -52
  76. package/scripts/extract-view.mjs +0 -64
  77. package/scripts/migrate-to-central-out.mjs +0 -157
@@ -0,0 +1,254 @@
1
+ # SKILL: Security QA Analyst
2
+
3
+ You are a Security QA Analyst. Your goal is to identify, validate, classify, and report security findings in the web application under test with precision and low ambiguity.
4
+
5
+ Adopt a hacker mindset, but do not overstate risk. Distinguish clearly between:
6
+ - confirmed vulnerabilities
7
+ - security misconfigurations
8
+ - potential issues
9
+ - informational findings
10
+
11
+ Your reports must help developers and security reviewers understand:
12
+ 1. what was detected,
13
+ 2. how certain the finding is,
14
+ 3. whether exploitation was confirmed,
15
+ 4. what evidence supports it,
16
+ 5. how to remediate it.
17
+
18
+ ## Core Objectives
19
+
20
+ 1. Identify security weaknesses in the application.
21
+ 2. Validate findings whenever possible before escalating severity.
22
+ 3. Classify each finding correctly.
23
+ 4. Report findings using a structured JSON attachment.
24
+ 5. Avoid false positives and avoid presenting unverified issues as confirmed vulnerabilities.
25
+
26
+ ---
27
+
28
+ ## Classification Rules
29
+
30
+ Every finding MUST be classified as one of the following:
31
+
32
+ ### 1. confirmed_vulnerability
33
+ Use this only when there is direct evidence of exploitable behavior or unauthorized access.
34
+
35
+ Examples:
36
+ - XSS payload executed
37
+ - SQL injection altered behavior or response
38
+ - IDOR allowed access to another user's resource
39
+ - Auth bypass allowed access to a protected page
40
+ - Open redirect successfully redirected to an external untrusted domain
41
+ - CSRF was successfully executed or clearly lacks protection in a state-changing action
42
+
43
+ ### 2. security_misconfiguration
44
+ Use this when a security control is missing, weak, or improperly configured, but exploitation was not directly demonstrated.
45
+
46
+ Examples:
47
+ - Missing Content-Security-Policy
48
+ - Missing Strict-Transport-Security
49
+ - Missing X-Frame-Options or missing frame-ancestors CSP directive
50
+ - Cookies missing Secure, HttpOnly, or SameSite
51
+ - Overly permissive CORS
52
+ - Sensitive pages cached improperly
53
+
54
+ ### 3. potential_issue
55
+ Use this when there is a suspicious condition, but the issue is not fully validated.
56
+
57
+ Examples:
58
+ - Input appears suspicious for XSS but payload execution not confirmed
59
+ - Parameter suggests open redirect but redirect not confirmed
60
+ - Sequential resource IDs suggest possible IDOR but unauthorized access not confirmed
61
+ - Error messages may leak internal info but impact is limited or unclear
62
+
63
+ ### 4. informational
64
+ Use this for low-impact observations that are useful but not actionable as vulnerabilities by themselves.
65
+
66
+ Examples:
67
+ - Server technology disclosure
68
+ - Minor header omissions with low direct impact
69
+ - Non-sensitive debug indicators
70
+
71
+ ---
72
+
73
+ ## Confidence Levels
74
+
75
+ Each finding MUST include a confidence field:
76
+
77
+ - high: direct evidence clearly supports the finding
78
+ - medium: evidence is strong but not fully conclusive
79
+ - low: weak signal or heuristic suspicion only
80
+
81
+ Never assign high confidence to a finding with speculative evidence.
82
+
83
+ ---
84
+
85
+ ## Exploitability Status
86
+
87
+ Each finding MUST include exploitability:
88
+
89
+ - confirmed
90
+ - likely
91
+ - unknown
92
+ - not_confirmed
93
+
94
+ Examples:
95
+ - Missing CSP => not_confirmed
96
+ - XSS payload executed => confirmed
97
+ - IDOR suspected from URL pattern only => unknown
98
+
99
+ ---
100
+
101
+ ## Severity Guidance
102
+
103
+ Severity must reflect actual impact and proof, not just category.
104
+
105
+ ### High
106
+ Use for:
107
+ - confirmed unauthorized access
108
+ - confirmed code/script execution
109
+ - sensitive data exposure with real impact
110
+ - auth bypass
111
+ - exploitable SQLi
112
+ - exploitable IDOR
113
+ - confirmed stored/reflected/DOM XSS with meaningful execution
114
+
115
+ ### Medium
116
+ Use for:
117
+ - meaningful security misconfigurations
118
+ - missing defense-in-depth controls
119
+ - insecure cookie attributes
120
+ - CSP missing
121
+ - weak CSRF protections not fully exploited
122
+ - permissive CORS with realistic risk
123
+
124
+ ### Low
125
+ Use for:
126
+ - low-impact disclosures
127
+ - minor hardening gaps
128
+ - findings with limited impact
129
+
130
+ ### Info
131
+ Use for:
132
+ - observations without direct security impact
133
+
134
+ Do not automatically mark missing security headers as High.
135
+ Missing CSP is usually security_misconfiguration and commonly Medium unless stronger contextual evidence justifies otherwise.
136
+
137
+ ---
138
+
139
+ ## Vulnerability Checklist
140
+
141
+ As you navigate and test the application, actively assess the following areas.
142
+
143
+ ### Input Fields & Forms
144
+ Check for:
145
+ - reflected XSS
146
+ - stored XSS
147
+ - DOM XSS
148
+ - SQL injection
149
+ - weak input handling
150
+ - insecure file upload behavior
151
+
152
+ When a field looks suspicious for script injection, test carefully.
153
+ Use available XSS testing tooling where applicable.
154
+
155
+ ### Navigation & URLs
156
+ Check for:
157
+ - open redirect
158
+ - IDOR
159
+ - auth bypass
160
+ - parameter tampering
161
+ - direct access to protected routes
162
+
163
+ ### Data Exposure
164
+ Check for:
165
+ - sensitive data in API responses
166
+ - excessive data exposure
167
+ - tokens or secrets in localStorage/sessionStorage
168
+ - verbose errors
169
+ - stack traces
170
+ - internal paths or implementation details
171
+ - source maps or debug artifacts in production when visible
172
+
173
+ ### Authentication & Session Management
174
+ Check for:
175
+ - CSRF protections on state-changing requests
176
+ - weak password policy
177
+ - session fixation
178
+ - insecure session cookies
179
+ - missing cookie flags
180
+ - session not rotated after login
181
+ - user enumeration signals
182
+
183
+ ### Security Headers & Browser Protections
184
+ Check for:
185
+ - Content-Security-Policy
186
+ - Strict-Transport-Security
187
+ - X-Frame-Options
188
+ - X-Content-Type-Options
189
+ - Referrer-Policy
190
+ - Permissions-Policy
191
+ - frame-ancestors via CSP where relevant
192
+
193
+ Remember:
194
+ Missing headers usually indicate misconfiguration, not automatically an exploitable vulnerability.
195
+
196
+ ---
197
+
198
+ ## Evidence Standards
199
+
200
+ Every reported finding MUST include evidence appropriate to the type of finding.
201
+
202
+ Evidence should include as many of these as possible:
203
+ - url
204
+ - method
205
+ - selector
206
+ - parameter
207
+ - payload
208
+ - request summary
209
+ - response summary
210
+ - observed behavior
211
+ - storage key
212
+ - cookie attributes
213
+ - screenshot reference if available
214
+ - reproduction steps
215
+
216
+ If exploitation was not confirmed, explicitly state that in the finding.
217
+
218
+ ---
219
+
220
+ ## Required Attachment Format
221
+
222
+ Whenever you identify a finding, you MUST attach it as JSON with the name `security_finding`.
223
+
224
+ Use this exact shape:
225
+
226
+ ```javascript
227
+ test.info().attach('security_finding', {
228
+ body: JSON.stringify({
229
+ type: 'security_misconfiguration',
230
+ category: 'Missing Security Header',
231
+ subcategory: 'Content-Security-Policy',
232
+ severity: 'Medium',
233
+ confidence: 'High',
234
+ exploitability: 'not_confirmed',
235
+ status: 'detected',
236
+ description: 'The response does not include a Content-Security-Policy header, reducing defense-in-depth against XSS and malicious script/resource injection.',
237
+ impact: 'If an injection issue exists elsewhere in the application, the lack of CSP may increase exploitability and impact.',
238
+ evidence: {
239
+ url: page.url(),
240
+ missingHeader: 'Content-Security-Policy'
241
+ },
242
+ reproductionSteps: [
243
+ 'Navigate to the target page.',
244
+ 'Inspect the response headers.',
245
+ 'Verify that Content-Security-Policy is absent.'
246
+ ],
247
+ remediation: 'Implement a restrictive Content-Security-Policy and avoid unsafe-inline and unsafe-eval where possible.',
248
+ classification: {
249
+ owaspTop10: 'A05:2021 Security Misconfiguration',
250
+ cwe: 'CWE-16'
251
+ }
252
+ }),
253
+ contentType: 'application/json'
254
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcadialdev/arcality",
3
- "version": "2.4.30",
3
+ "version": "2.4.31",
4
4
  "description": "AI-powered QA testing tool — Autonomous web testing agent by Arcadial",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -18,6 +18,7 @@
18
18
  "bin/",
19
19
  "scripts/",
20
20
  "src/",
21
+ ".agents/",
21
22
  "tests/_helpers/agentic-runner.bundle.spec.js",
22
23
  "tests/_helpers/ArcalityReporter.js",
23
24
  "playwright.config.js",
@@ -586,7 +586,7 @@ async function main() {
586
586
 
587
587
  if (!discoverPath) {
588
588
  const knownBaseUrl = projectConfig?.project?.baseUrl || process.env.BASE_URL;
589
- const promptMsg = knownBaseUrl
589
+ const promptMsg = knownBaseUrl
590
590
  ? `🌐 Initial navigation path (relative to ${knownBaseUrl}):`
591
591
  : '🌐 Full Navigation URL (e.g., http://localhost:3000/):';
592
592
 
@@ -623,7 +623,7 @@ async function main() {
623
623
  if (!validation.valid) {
624
624
  const errorMessages = {
625
625
  'no_api_key': '🔑 API Key not found.\n Configure it via `arcality init` or in .env (ARCALITY_API_KEY)',
626
- 'invalid_format': '🔑 Invalid API Key. It must start with "arc_k_"',
626
+ 'invalid_format': '🔑 Invalid API Key. It must start with "arc_"',
627
627
  'invalid_api_key': '🔑 API Key not recognized by the server',
628
628
  'plan_expired': '💳 Your plan has expired'
629
629
  };
package/scripts/init.mjs CHANGED
@@ -109,10 +109,10 @@ async function main() {
109
109
  // ── Step 1: API Key ──
110
110
  const apiKey = await text({
111
111
  message: chalk.cyan('🔑 Enter your Arcality API Key:'),
112
- placeholder: 'arc_k_live_xxxxx',
112
+ placeholder: 'arc_live_xxxxx',
113
113
  validate: (v) => {
114
114
  if (!v || !v.trim()) return 'API Key is required.';
115
- if (!v.startsWith('arc_k_')) return 'API Key must start with "arc_k_"';
115
+ if (!v.startsWith('arc_')) return 'API Key must start with "arc_"';
116
116
  },
117
117
  });
118
118
 
package/scripts/setup.mjs CHANGED
@@ -71,8 +71,8 @@ async function main() {
71
71
 
72
72
  const newKey = await question(' 👉 Ingresa Arcality API Key (Enter p. mantener): ');
73
73
  if (newKey) {
74
- if (!newKey.startsWith('arc_k_')) {
75
- console.log(' ❌ La API Key debe iniciar con "arc_k_"');
74
+ if (!newKey.startsWith('arc_')) {
75
+ console.log(' ❌ La API Key debe iniciar con "arc_"');
76
76
  process.exit(1);
77
77
  }
78
78
  config.api_key = newKey;
@@ -119,7 +119,7 @@ export async function validateApiKey() {
119
119
  return { valid: false, error: 'no_api_key', mode: 'mock' };
120
120
  }
121
121
 
122
- if (!key.startsWith('arc_k_')) {
122
+ if (!key.startsWith('arc_')) {
123
123
  return { valid: false, error: 'invalid_format', mode: 'mock' };
124
124
  }
125
125
 
@@ -939,7 +939,11 @@ ${relevantFailures.map((m) => `- ${m.prompt}`).join("\n")}
939
939
  const role = htmlEl.getAttribute("role") || "";
940
940
  const text = (htmlEl.innerText || htmlEl.getAttribute("aria-label") || htmlEl.getAttribute("title") || "").trim().replace(/\s+/g, " ");
941
941
  const textLower = text.toLowerCase();
942
- const isCriticalFailure = (textLower.includes("error") || textLower.includes("fall\xF3") || textLower.includes("no se puede") || textLower.includes("ya existe") || textLower.includes("existe") || textLower.includes("inv\xE1lido") || textLower.includes("incorrecto") || textLower.includes("ligado") || textLower.includes("depende") || textLower.includes("duplicado") || textLower.includes("repetido")) && text.length < 200;
942
+ const isCriticalFailure = (textLower.includes("error") || textLower.includes("fall\xF3") || textLower.includes("no se puede") || textLower.includes("ya existe") || textLower.includes("existe") || textLower.includes("inv\xE1lido") || textLower.includes("incorrecto") || textLower.includes("ligado") || textLower.includes("depende") || textLower.includes("duplicado") || textLower.includes("repetido") || /* Validaciones ZOD / Wizard de campos de formulario */
943
+ textLower.includes("debe ser mayor") || textLower.includes("debe ser menor") || textLower.includes("debe ser igual") || textLower.includes("debe ser mayor o igual") || textLower.includes("debe ser menor o igual") || /* ZOD date messages (Spanish) */
944
+ textLower.includes("debe ser posterior") || textLower.includes("debe ser anterior") || textLower.includes("debe ser una fecha") || textLower.includes("fecha debe ser") || textLower.includes("posterior o igual a la fecha") || textLower.includes("anterior o igual a la fecha") || textLower.includes("posterior a la fecha") || textLower.includes("anterior a la fecha") || textLower.includes("fecha") && (textLower.includes("actual") || textLower.includes("pasada") || textLower.includes("futura") || textLower.includes("requerida") || textLower.includes("inv\xE1lida") || textLower.includes("incorrecta") || textLower.includes("posterior") || textLower.includes("anterior")) || /* ZOD number/range messages */
945
+ textLower.includes("debe ser un n\xFAmero") || textLower.includes("debe ser positivo") || textLower.includes("debe ser negativo") || textLower.includes("m\xEDnimo") && textLower.includes("caracteres") || textLower.includes("m\xE1ximo") && textLower.includes("caracteres") || textLower.includes("al menos") || textLower.includes("como m\xE1ximo") || /* Generic ZOD/form required */
946
+ textLower.includes("campo requerido") || textLower.includes("campo obligatorio") || textLower.includes("requerido") || textLower.includes("obligatorio") || textLower.includes("no puede estar vac\xEDo") || textLower.includes("no puede ser") || textLower.includes("no es v\xE1lido") || textLower.includes("no es correcto") || textLower.includes("recuerda llenar") || textLower.includes("completa los campos") || textLower.includes("campos obligatorios") || textLower.includes("formato incorrecto") || textLower.includes("formato inv\xE1lido") || textLower.includes("valor no permitido")) && text.length < 250;
943
947
  const isCriticalSuccess = (textLower.includes("exitosamente") || textLower.includes("guardado") || textLower.includes("creado") || textLower.includes("success") || textLower.includes("correctamente") && text.length < 100) && text.length < 200;
944
948
  const isCriticalFeedback = isCriticalFailure || isCriticalSuccess;
945
949
  if (!isCriticalFeedback && (tag === "span" || tag === "p" || tag === "label" || tag === "i" || tag === "svg" || tag === "img" || tag === "div")) {
@@ -1445,6 +1449,13 @@ These are NOT optional. You MUST use these tools in these situations:
1445
1449
  4. **PAGE TRANSITIONS**: If URL contains '/edit' or you see a 'Save' button, you ARE in the edit view. Don't go back.
1446
1450
  5. **MENUS & DROPDOWNS**: After clicking a toggle/menu, WAIT for the next turn. Never click the same menu icon twice in a row.
1447
1451
  6. **ERROR HANDLING**: If you see '\u{1F6D1} [ERROR_CR\xCDTICO]', STOP everything. Analyze the error, fix the data, then retry.
1452
+ 6b. **DUPLICATE / REPEATED VALUE ERROR \u2014 MANDATORY PROTOCOL**:
1453
+ - If HISTORY contains a line with "ERROR VISIBLE EN PANTALLA" or "ERROR POST-GUARDADO" with words like "repetido", "duplicado", "ya existe", or "already exists":
1454
+ 1. You MUST identify WHICH field caused the duplication (usually the main name/title field).
1455
+ 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.
1456
+ 3. ONLY AFTER changing the value, attempt to save again.
1457
+ 4. **NEVER attempt to save with the same value that was already rejected.** This is FORBIDDEN.
1458
+ 5. If you cannot determine which field is the duplicate, use \`capture_console_errors\` to get hints from the application.
1448
1459
  7. **DATA UNIQUENESS**: When generating test data, ALWAYS include a unique suffix (timestamp, counter). Never use "Test123" or "Prueba" alone.
1449
1460
  8. **FINISH CORRECTLY**: Use \`finish: true\` ONLY when the mission objective is FULLY achieved.
1450
1461
  - **BEWARE**: If you see a record in a table, confirm it is the one you JUST created (e.g., check timestamp or log message). Do NOT assume an existing record is your success.
@@ -1453,7 +1464,34 @@ These are NOT optional. You MUST use these tools in these situations:
1453
1464
  10. **STUCK DETECTION**: If history shows 3+ similar actions without progress, use \`report_inability_to_proceed\`.
1454
1465
  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\`.
1455
1466
  12. **GUIDE CONFLICT**: If SUCCESS MEMORY disagrees with what you see on screen, ABANDON the memory and trust your eyes.
1456
- 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.`,
1467
+ 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.
1468
+
1469
+ # WIZARD & MULTI-STEP FORMS (CRITICAL)
1470
+ When a form is divided into numbered steps (e.g., Step 1 \u2192 Step 2 \u2192 Step 3):
1471
+
1472
+ 1. **VALIDATION ERROR ON SAVE = STEP HAS INVALID DATA**: If you see a \u{1F6D1} [ERROR_CR\xCDTICO] after clicking Save/Next and you are on step N, it means a PREVIOUS step contains bad data. You MUST navigate BACK to that step.
1473
+ - Look for a "Back" / "Anterior" / "Periodo" / step-number button and click it to return.
1474
+ - NEVER retry the Save button without first fixing the invalid data in the previous step.
1475
+
1476
+ 2. **DATE VALIDATION ERRORS \u2014 MANDATORY PROTOCOL (ZOD-AWARE)**:
1477
+ - ZOD generates Spanish validation messages such as:
1478
+ - "La fecha de inicio debe ser posterior o igual a la fecha actual."
1479
+ - "La fecha debe ser una fecha futura."
1480
+ - "Debe ser posterior a la fecha de inicio."
1481
+ - "El valor debe ser mayor o igual a X."
1482
+ - ANY \u{1F6D1} [ERROR_CR\xCDTICO] containing FECHA + (POSTERIOR/ANTERIOR/ACTUAL/PASADA/FUTURA) is a **ZOD DATE VALIDATION ERROR**.
1483
+ - **RECOVERY PROTOCOL** (follow in order, NEVER skip):
1484
+ 1. STOP \u2014 do NOT click Save/Siguiente again with the same data.
1485
+ 2. Identify which wizard step contains the failing date field (check HISTORY \u2014 usually Step 1 or Step 2).
1486
+ 3. Click the Back/Anterior/step-number button to return to that step.
1487
+ 4. Locate the date input using \`validate_element_state\` with 'has_value' to confirm its current (bad) value.
1488
+ 5. Clear and re-fill the date using \`interact_native_control\` with control_type \`'date'\` and a value \u2265 TODAY in YYYY-MM-DD format.
1489
+ 6. TODAY: \`${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}\` \u2014 use this exact expression to compute the date dynamically.
1490
+ 7. After fixing ALL date fields, advance through each wizard step again.
1491
+
1492
+ 3. **NEVER LOOP ON THE SAME STEP**: If you've been on the same wizard step for 3+ turns without advancing, go back one step and verify the data is correct before trying to advance again.
1493
+
1494
+ 4. **STEP VERIFICATION**: Before clicking Next/Save on any step, VERIFY that date fields have future-or-today dates. Use \`validate_element_state\` with 'has_value' to confirm.`,
1457
1495
  cache_control: { type: "ephemeral" }
1458
1496
  }
1459
1497
  ];
@@ -1536,7 +1574,7 @@ ${history.slice(-25).join("\n") || "None"}`
1536
1574
  headers,
1537
1575
  signal: controller.signal,
1538
1576
  body: JSON.stringify({
1539
- model: process.env.CLAUDE_MODEL || "claude-3-5-sonnet-20241022",
1577
+ model: process.env.CLAUDE_MODEL || "claude-4-5-sonnet-latest",
1540
1578
  max_tokens: 4096,
1541
1579
  system: systemPromptBlocks,
1542
1580
  tools: rawTools.map((t) => ({
@@ -1581,7 +1619,7 @@ ${history.slice(-25).join("\n") || "None"}`
1581
1619
  const textResponse = message.content.find((c) => c.type === "text")?.text || "";
1582
1620
  if (toolCalls.length === 0) {
1583
1621
  console.log(`(${((Date.now() - startTime) / 1e3).toFixed(1)}s)`);
1584
- return { thought: textResponse || "No action decided", actions: [] };
1622
+ return { thought: textResponse || "No action decided", actions: [], usage: data.usage };
1585
1623
  }
1586
1624
  const toolResults = [];
1587
1625
  let uiActionCall = null;
@@ -1640,7 +1678,8 @@ ${history.slice(-25).join("\n") || "None"}`
1640
1678
 
1641
1679
  \u{1F4A1} SUGERENCIA: ${toolInput.prompt_suggestion}`,
1642
1680
  actions: [],
1643
- finish: true
1681
+ finish: true,
1682
+ usage: data.usage
1644
1683
  };
1645
1684
  } else if (toolName === "validate_element_state") {
1646
1685
  const { idx, validations } = toolInput;
@@ -2147,7 +2186,8 @@ ${report}` });
2147
2186
  }
2148
2187
  return { ...a, value: finalValue, selector: c?.selector, frameIdx: c?.frameIdx, description: c?.name, type: c?.type };
2149
2188
  }),
2150
- finish: input.finish
2189
+ finish: input.finish,
2190
+ usage: data.usage
2151
2191
  };
2152
2192
  }
2153
2193
  }
@@ -2178,7 +2218,7 @@ ${report}` });
2178
2218
  method: "POST",
2179
2219
  headers,
2180
2220
  body: JSON.stringify({
2181
- model: process.env.CLAUDE_MODEL || "claude-3-5-sonnet-20241022",
2221
+ model: process.env.CLAUDE_MODEL || "claude-4-5-sonnet-latest",
2182
2222
  max_tokens: 300,
2183
2223
  system: "Eres un Senior QA personalizador de reportes. Tu tarea es resumir en una sola frase corta, profesional y amigable por qu\xE9 una misi\xF3n de testing fue exitosa bas\xE1ndote en el historial de pasos. Usa un tono de victoria. No menciones detalles t\xE9cnicos como 'selectores' o 'DOM'. El resumen debe empezar con algo como '\xA1Misi\xF3n cumplida! ...'",
2184
2224
  messages: [
@@ -2226,7 +2266,7 @@ ${history.join("\n")}` }
2226
2266
  method: "POST",
2227
2267
  headers,
2228
2268
  body: JSON.stringify({
2229
- model: process.env.CLAUDE_MODEL || "claude-3-5-sonnet-20241022",
2269
+ model: process.env.CLAUDE_MODEL || "claude-4-5-sonnet-latest",
2230
2270
  max_tokens: 1e3,
2231
2271
  system: `You are an elite QA Engineer. Your task is to transform a raw execution history into a clean, human-friendly English YAML Mission Card.
2232
2272
 
@@ -2409,6 +2449,7 @@ function captureValidationRule(errorMessage, context) {
2409
2449
  let stepsDataBackup = [];
2410
2450
  const maxSteps = 30;
2411
2451
  let lastSeenSuccessToast = "";
2452
+ let accumulatedCost = 0;
2412
2453
  const saveMissionResults = () => {
2413
2454
  if (resultsSaved)
2414
2455
  return;
@@ -2612,6 +2653,22 @@ function captureValidationRule(errorMessage, context) {
2612
2653
  stepsDataBackup = [...stepsData];
2613
2654
  stepsData.length = 0;
2614
2655
  }
2656
+ try {
2657
+ const errorEls = await page.locator('.toast, .alert, [role="alert"], [role="status"], .snackbar, .notification, .error-message, .mat-snack-bar-container').all();
2658
+ for (const el of errorEls) {
2659
+ if (await el.isVisible().catch(() => false)) {
2660
+ const errTxt = await el.innerText().catch(() => "");
2661
+ if (errTxt && failureKeywords.test(errTxt.toLowerCase())) {
2662
+ const isDuplicateError = /repetido|duplicado|ya existe|already exists/i.test(errTxt);
2663
+ 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.`;
2664
+ history.push(`Turno ${stepCount}: \u{1F6D1} ERROR VISIBLE EN PANTALLA: "${errTxt.trim().substring(0, 200)}". ${correctionHint}`);
2665
+ console.log(`>>ARCALITY_STATUS>> \u{1F6D1} Error inyectado en historial: "${errTxt.substring(0, 80)}"`);
2666
+ break;
2667
+ }
2668
+ }
2669
+ }
2670
+ } catch {
2671
+ }
2615
2672
  }
2616
2673
  if (response) {
2617
2674
  const guideActionDesc = response.actions?.[0] ? `${response.actions[0].action} en "${response.actions[0].description || ""}"`.toLowerCase() : "";
@@ -2629,6 +2686,21 @@ function captureValidationRule(errorMessage, context) {
2629
2686
  stepCount++;
2630
2687
  console.log(`>>ARCALITY_STATUS>> \u23F3 Turno IA ${stepCount} de ${maxSteps} (Gu\xEDa us\xF3 ${guideStepCount} turnos gratis)...`);
2631
2688
  response = await agent.askIA(prompt, history, stepCount);
2689
+ if (response.usage) {
2690
+ const inputs = response.usage.input_tokens || 0;
2691
+ const outputs = response.usage.output_tokens || 0;
2692
+ const cost = inputs / 1e6 * 3 + outputs / 1e6 * 15;
2693
+ accumulatedCost += cost;
2694
+ if (accumulatedCost >= 6) {
2695
+ console.warn(`
2696
+ \u{1F6D1} [AUTO-STOP] Capacidad m\xE1xima de exploraci\xF3n t\xE9cnica alcanzada para esta prueba.`);
2697
+ response.thought = `LIMIT REACHED: He llegado a la capacidad m\xE1xima de ciclos y turnos permitidos para analizar esta tarea (L\xEDmite de la prueba). Detendr\xE9 esta misi\xF3n para evitar sobrecarga del sistema.`;
2698
+ response.finish = true;
2699
+ response.actions = [];
2700
+ aiMarkedSuccess = false;
2701
+ hasCriticalError = true;
2702
+ }
2703
+ }
2632
2704
  }
2633
2705
  console.log(`\u{1F9E0} Pensamiento: ${response.thought}`);
2634
2706
  if (!response.finish && history.length >= 6) {
@@ -2771,6 +2843,9 @@ function captureValidationRule(errorMessage, context) {
2771
2843
  } else if (failureKeywords.test(fbText.toLowerCase())) {
2772
2844
  console.log(`>>ARCALITY_STATUS>> \u{1F4DA} [BUSINESS RULE] Error de validaci\xF3n capturado: "${fbText.substring(0, 80)}"`);
2773
2845
  captureValidationRule(fbText.trim(), `Acci\xF3n: click en "${desc}" en ${page.url()}`);
2846
+ const isDuplicateError = /repetido|duplicado|ya existe|already exists/i.test(fbText);
2847
+ 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.`;
2848
+ history.push(`Turno ${stepCount}: \u{1F6D1} ERROR POST-GUARDADO: "${fbText.trim().substring(0, 200)}". ${correctionHint}`);
2774
2849
  }
2775
2850
  }
2776
2851
  }
@@ -1,63 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
-
4
- const baseDir = 'c:/Users/apulido/OneDrive - Block Networks/Documentos/Arcadial Projects/Char.Dev/out/qmsDev';
5
- const nestedDir = path.join(baseDir, 'context', 'qmsDev');
6
- const targetContextDir = path.join(baseDir, 'context');
7
- const targetMissionsDir = path.join(baseDir, 'missions');
8
-
9
- function cleanup() {
10
- if (!fs.existsSync(nestedDir)) {
11
- console.log('Nested directory not found, skipping.');
12
- return;
13
- }
14
-
15
- const files = fs.readdirSync(nestedDir);
16
-
17
- files.forEach(file => {
18
- const filePath = path.join(nestedDir, file);
19
- if (file.startsWith('agent-log-')) {
20
- console.log(`Moving ${file} to context/`);
21
- fs.renameSync(filePath, path.join(targetContextDir, file));
22
- } else if (file === 'memoria-agente.json') {
23
- const targetMemory = path.join(targetContextDir, 'memoria-agente.json');
24
- if (fs.existsSync(targetMemory)) {
25
- console.log('Merging memoria-agente.json...');
26
- const mainMemory = JSON.parse(fs.readFileSync(targetMemory, 'utf8'));
27
- const nestedMemory = JSON.parse(fs.readFileSync(filePath, 'utf8'));
28
- const combined = [...mainMemory, ...nestedMemory];
29
- fs.writeFileSync(targetMemory, JSON.stringify(combined, null, 2));
30
- fs.unlinkSync(filePath);
31
- } else {
32
- console.log('Moving memoria-agente.json...');
33
- fs.renameSync(filePath, targetMemory);
34
- }
35
- } else if (file === 'saved_missions') {
36
- const missions = fs.readdirSync(filePath);
37
- missions.forEach(m => {
38
- if (m.endsWith('.yaml') || m.endsWith('.yml')) {
39
- console.log(`Moving mission ${m} to missions/`);
40
- fs.renameSync(path.join(filePath, m), path.join(targetMissionsDir, m));
41
- }
42
- });
43
- fs.rmdirSync(filePath, { recursive: true });
44
- } else if (fs.statSync(filePath).isFile()) {
45
- console.log(`Moving other file ${file} to context/`);
46
- fs.renameSync(filePath, path.join(targetContextDir, file));
47
- }
48
- });
49
-
50
- // Try to remove the empty nested dir
51
- try {
52
- if (fs.readdirSync(nestedDir).length === 0) {
53
- fs.rmdirSync(nestedDir);
54
- console.log('Removed empty nested directory.');
55
- } else {
56
- console.warn('Nested directory not empty, keeping it.');
57
- }
58
- } catch (e) {
59
- console.error('Error removing nested dir:', e.message);
60
- }
61
- }
62
-
63
- cleanup();
@@ -1,52 +0,0 @@
1
- #!/usr/bin/env node
2
- import 'dotenv/config';
3
- import fs from 'node:fs';
4
- import { spawn } from 'node:child_process';
5
- import path from 'node:path';
6
-
7
- const targetPath = process.argv[2] || '/';
8
- const isWin = process.platform === 'win32';
9
- const cmd = isWin ? 'npx.cmd' : 'npx';
10
- const testFile = 'tests/_helpers/discover-view.spec.ts';
11
-
12
- function parseDotEnvFile(p) {
13
- try {
14
- const raw = fs.readFileSync(p, 'utf8');
15
- const lines = raw.split(/\r?\n/);
16
- const out = {};
17
- for (const line of lines) {
18
- const trimmed = line.trim();
19
- if (!trimmed || trimmed.startsWith('#')) continue;
20
- const idx = trimmed.indexOf('=');
21
- if (idx === -1) continue;
22
- let k = trimmed.slice(0, idx).trim();
23
- let v = trimmed.slice(idx + 1).trim();
24
- if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
25
- out[k] = v;
26
- }
27
- return out;
28
- } catch {
29
- return {};
30
- }
31
- }
32
-
33
- const fileEnv = parseDotEnvFile(path.join(process.cwd(), '.env'));
34
- const env = { ...process.env, ...fileEnv, TARGET_PATH: targetPath };
35
-
36
- const args = ['playwright', 'test', testFile, '--project=chromium'];
37
- // Debug printing removed to avoid leaking placeholders; command is spawned directly
38
-
39
- const child = spawn(cmd, args, {
40
- stdio: 'inherit',
41
- shell: isWin,
42
- env,
43
- });
44
-
45
- child.on('exit', (code) => {
46
- process.exit(code ?? 0);
47
- });
48
-
49
- child.on('error', (err) => {
50
- console.error('Failed to run playwright:', err);
51
- process.exit(1);
52
- });