@ranger-testing/ranger-cli 2.0.5 → 2.0.6

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 (115) hide show
  1. package/build/cli.js +1 -914
  2. package/build/commands/addEnv.js +1 -1
  3. package/build/commands/authEncrypt.js +1 -36
  4. package/build/commands/clean.js +1 -1
  5. package/build/commands/config.js +1 -93
  6. package/build/commands/env.js +1 -98
  7. package/build/commands/feature.js +1 -653
  8. package/build/commands/hook.js +1 -33
  9. package/build/commands/hooks/autoPrompt.js +1 -32
  10. package/build/commands/hooks/disable.js +1 -33
  11. package/build/commands/hooks/enable.js +1 -58
  12. package/build/commands/hooks/exitPlanMode.js +1 -35
  13. package/build/commands/hooks/index.js +1 -12
  14. package/build/commands/hooks/output.js +1 -71
  15. package/build/commands/hooks/planReminder.js +1 -46
  16. package/build/commands/hooks/planStart.js +1 -30
  17. package/build/commands/hooks/postEdit.js +1 -43
  18. package/build/commands/hooks/preCompact.js +1 -30
  19. package/build/commands/hooks/sessionEnd.js +1 -25
  20. package/build/commands/hooks/sessionStart.js +1 -93
  21. package/build/commands/hooks/stopHook.js +1 -155
  22. package/build/commands/index.js +1 -12
  23. package/build/commands/login.js +1 -26
  24. package/build/commands/setupCi.js +1 -189
  25. package/build/commands/skillup.js +1 -80
  26. package/build/commands/start.js +1 -1
  27. package/build/commands/status.js +1 -198
  28. package/build/commands/update.js +1 -182
  29. package/build/commands/updateEnv.js +1 -1
  30. package/build/commands/useEnv.js +1 -1
  31. package/build/commands/utils/activeProfile.js +1 -76
  32. package/build/commands/utils/browserSessionsApi.js +1 -1
  33. package/build/commands/utils/claudeConfig.js +1 -73
  34. package/build/commands/utils/claudePlugin.js +1 -85
  35. package/build/commands/utils/crypto.js +1 -42
  36. package/build/commands/utils/desirePathLog.js +1 -139
  37. package/build/commands/utils/deviceAuth.js +1 -232
  38. package/build/commands/utils/environment.js +1 -65
  39. package/build/commands/utils/featureApi.js +1 -371
  40. package/build/commands/utils/featureReportGenerator.js +1 -204
  41. package/build/commands/utils/git.js +1 -44
  42. package/build/commands/utils/keychain.js +1 -1
  43. package/build/commands/utils/localAgentInstallationsApi.js +1 -1
  44. package/build/commands/utils/profileMessages.js +1 -8
  45. package/build/commands/utils/profileSetupBanner.js +1 -167
  46. package/build/commands/utils/rangerRoot.js +1 -60
  47. package/build/commands/utils/reportGenerator.js +1 -130
  48. package/build/commands/utils/retry.js +1 -25
  49. package/build/commands/utils/sessionCache.js +1 -299
  50. package/build/commands/utils/settings.js +1 -313
  51. package/build/commands/utils/skillContent.js +1 -28
  52. package/build/commands/utils/skills.js +1 -1
  53. package/build/commands/utils/telemetry.js +1 -254
  54. package/build/commands/utils/userApi.js +1 -32
  55. package/build/commands/utils/version.js +1 -62
  56. package/build/commands/verifyFeature.js +1 -1343
  57. package/build/commands/verifyInBrowser.js +1 -1
  58. package/package.json +1 -1
  59. package/build/cli.js.map +0 -1
  60. package/build/commands/addEnv.js.map +0 -1
  61. package/build/commands/authEncrypt.js.map +0 -1
  62. package/build/commands/clean.js.map +0 -1
  63. package/build/commands/config.js.map +0 -1
  64. package/build/commands/env.js.map +0 -1
  65. package/build/commands/feature.js.map +0 -1
  66. package/build/commands/hook.js.map +0 -1
  67. package/build/commands/hooks/autoPrompt.js.map +0 -1
  68. package/build/commands/hooks/disable.js.map +0 -1
  69. package/build/commands/hooks/enable.js.map +0 -1
  70. package/build/commands/hooks/exitPlanMode.js.map +0 -1
  71. package/build/commands/hooks/index.js.map +0 -1
  72. package/build/commands/hooks/output.js.map +0 -1
  73. package/build/commands/hooks/planReminder.js.map +0 -1
  74. package/build/commands/hooks/planStart.js.map +0 -1
  75. package/build/commands/hooks/postEdit.js.map +0 -1
  76. package/build/commands/hooks/preCompact.js.map +0 -1
  77. package/build/commands/hooks/sessionEnd.js.map +0 -1
  78. package/build/commands/hooks/sessionStart.js.map +0 -1
  79. package/build/commands/hooks/stopHook.js.map +0 -1
  80. package/build/commands/index.js.map +0 -1
  81. package/build/commands/login.js.map +0 -1
  82. package/build/commands/setupCi.js.map +0 -1
  83. package/build/commands/skillup.js.map +0 -1
  84. package/build/commands/start.js.map +0 -1
  85. package/build/commands/status.js.map +0 -1
  86. package/build/commands/update.js.map +0 -1
  87. package/build/commands/updateEnv.js.map +0 -1
  88. package/build/commands/useEnv.js.map +0 -1
  89. package/build/commands/utils/activeProfile.js.map +0 -1
  90. package/build/commands/utils/browserSessionsApi.js.map +0 -1
  91. package/build/commands/utils/claudeConfig.js.map +0 -1
  92. package/build/commands/utils/claudePlugin.js.map +0 -1
  93. package/build/commands/utils/crypto.js.map +0 -1
  94. package/build/commands/utils/desirePathLog.js.map +0 -1
  95. package/build/commands/utils/deviceAuth.js.map +0 -1
  96. package/build/commands/utils/environment.js.map +0 -1
  97. package/build/commands/utils/featureApi.js.map +0 -1
  98. package/build/commands/utils/featureReportGenerator.js.map +0 -1
  99. package/build/commands/utils/git.js.map +0 -1
  100. package/build/commands/utils/keychain.js.map +0 -1
  101. package/build/commands/utils/localAgentInstallationsApi.js.map +0 -1
  102. package/build/commands/utils/profileMessages.js.map +0 -1
  103. package/build/commands/utils/profileSetupBanner.js.map +0 -1
  104. package/build/commands/utils/rangerRoot.js.map +0 -1
  105. package/build/commands/utils/reportGenerator.js.map +0 -1
  106. package/build/commands/utils/retry.js.map +0 -1
  107. package/build/commands/utils/sessionCache.js.map +0 -1
  108. package/build/commands/utils/settings.js.map +0 -1
  109. package/build/commands/utils/skillContent.js.map +0 -1
  110. package/build/commands/utils/skills.js.map +0 -1
  111. package/build/commands/utils/telemetry.js.map +0 -1
  112. package/build/commands/utils/userApi.js.map +0 -1
  113. package/build/commands/utils/version.js.map +0 -1
  114. package/build/commands/verifyFeature.js.map +0 -1
  115. package/build/commands/verifyInBrowser.js.map +0 -1
@@ -1,1343 +1 @@
1
- import { query, } from '@anthropic-ai/claude-agent-sdk';
2
- import { createTelemetryCollector, } from './utils/telemetry.js';
3
- import { join, dirname } from 'path';
4
- import { readFile, readdir, appendFile, mkdir, rm, stat } from 'fs/promises';
5
- import { existsSync } from 'fs';
6
- import { execSync } from 'child_process';
7
- import { tmpdir } from 'os';
8
- import inquirer from 'inquirer';
9
- import { loadSettings, resolveEnvVars, buildPlaywrightConfig, cleanupTempFiles, getEnvDir, } from './utils/settings.js';
10
- import { createBrowserSession, updateBrowserSession, getUploadUrls, uploadTrace, uploadConversation, uploadScreenshot, uploadVideo, buildTraceViewerUrl, getProxySessionToken, createVerificationStep, createStepAsset, } from './utils/browserSessionsApi.js';
11
- import { getAiProxyUrl } from './utils/environment.js';
12
- import { getToken } from './utils/keychain.js';
13
- import { getActiveFeatureId } from './feature.js';
14
- import { readActiveProfileName } from './utils/activeProfile.js';
15
- import { getEnvNames } from './env.js';
16
- import { formatProfileRequiredMessage } from './utils/profileMessages.js';
17
- import { getFeature, updateFeature, updateChecklistItem, startSession, getActionItems, getItemFeedback, markCommentsAddressed, } from './utils/featureApi.js';
18
- import { getRangerDir } from './utils/rangerRoot.js';
19
- const bold = (text) => `\x1b[1m${text}\x1b[0m`;
20
- /**
21
- * Get the current git branch
22
- */
23
- function getGitBranch() {
24
- try {
25
- return execSync('git rev-parse --abbrev-ref HEAD', {
26
- encoding: 'utf-8',
27
- stdio: ['pipe', 'pipe', 'pipe'],
28
- }).trim();
29
- }
30
- catch {
31
- return undefined;
32
- }
33
- }
34
- /**
35
- * Zip a directory and return the buffer
36
- */
37
- async function zipDirectory(dirPath) {
38
- const outputPath = join(dirPath, '..', 'trace-upload.zip');
39
- execSync(`cd "${dirPath}" && zip -r "${outputPath}" .`, {
40
- stdio: 'pipe',
41
- });
42
- const buffer = await readFile(outputPath);
43
- execSync(`rm "${outputPath}"`, { stdio: 'pipe' });
44
- return buffer;
45
- }
46
- /**
47
- * Find the trace directory for a session
48
- */
49
- function getTraceDirectory(sessionId) {
50
- return join(getRangerDir(), 'sessions', sessionId);
51
- }
52
- /**
53
- * Get the conversation file path for a session
54
- */
55
- function getConversationFilePath(sessionId) {
56
- return join(tmpdir(), 'ranger-browser-sessions', sessionId, 'conversation.jsonl');
57
- }
58
- /**
59
- * Load videos from a session's videos directory
60
- */
61
- async function loadSessionVideos(sessionDir) {
62
- const videosDir = join(sessionDir, 'videos');
63
- if (!existsSync(videosDir)) {
64
- return [];
65
- }
66
- const files = await readdir(videosDir);
67
- const videoFiles = files.filter((f) => f.toLowerCase().endsWith('.webm'));
68
- return videoFiles.map((filename) => ({
69
- filename,
70
- path: join(videosDir, filename),
71
- }));
72
- }
73
- /**
74
- * Get mock evaluation data for debug mode
75
- */
76
- function getMockEvaluation(outcome) {
77
- const mockEvaluations = {
78
- verified: {
79
- success: true,
80
- summary: '[DEBUG] Mock verification completed successfully.',
81
- evaluation: 'verified',
82
- evaluationReason: 'All scenario requirements were met.',
83
- },
84
- partial: {
85
- success: false,
86
- summary: '[DEBUG] Mock partial verification.',
87
- evaluation: 'partial',
88
- evaluationReason: 'Some requirements were not fully verified.',
89
- issues: [
90
- {
91
- severity: 'MINOR',
92
- type: 'OTHER',
93
- description: 'Secondary feature not fully implemented',
94
- },
95
- ],
96
- },
97
- incomplete: {
98
- success: false,
99
- summary: '[DEBUG] Mock incomplete verification.',
100
- evaluation: 'incomplete',
101
- evaluationReason: 'Implementation is incomplete and needs additional work.',
102
- issues: [
103
- {
104
- severity: 'MAJOR',
105
- type: 'OTHER',
106
- description: 'Feature is partially implemented but missing key functionality',
107
- },
108
- {
109
- severity: 'MINOR',
110
- type: 'OTHER',
111
- description: 'UI elements present but not fully functional',
112
- },
113
- ],
114
- },
115
- blocked: {
116
- success: false,
117
- summary: '[DEBUG] Mock blocked verification.',
118
- evaluation: 'blocked',
119
- evaluationReason: 'HTTP 404 - Page not found.',
120
- issues: [
121
- {
122
- severity: 'BLOCKER',
123
- type: 'HTTP_404',
124
- description: 'Target page returns 404 Not Found',
125
- },
126
- {
127
- severity: 'MAJOR',
128
- type: 'NAVIGATION_ERROR',
129
- description: 'Unable to proceed due to missing page',
130
- },
131
- ],
132
- },
133
- failed: {
134
- success: false,
135
- summary: '[DEBUG] Mock failed verification.',
136
- evaluation: 'failed',
137
- evaluationReason: 'Browser automation failed with timeout error.',
138
- issues: [
139
- {
140
- severity: 'BLOCKER',
141
- type: 'OTHER',
142
- description: 'Timeout waiting for element',
143
- },
144
- ],
145
- },
146
- };
147
- return mockEvaluations[outcome];
148
- }
149
- /**
150
- * Get the debug mode prompt for minimal browser interaction
151
- */
152
- function getDebugPrompt() {
153
- return `You are testing browser automation. Your task is simple:
154
-
155
- 1. Navigate to https://www.mozilla.org using browser_navigate
156
- 2. Take a snapshot with browser_snapshot to see the page
157
- 3. Take a screenshot named "01_mozilla-homepage.png" using browser_take_screenshot
158
- 4. Click the "Learn More" link on the page
159
- 5. Take a snapshot with browser_snapshot to see the new page
160
- 6. Take a screenshot named "02_learn-more.png" using browser_take_screenshot
161
- 7. Return immediately with the structured output
162
-
163
- Return your findings in the structured output format.`;
164
- }
165
- /**
166
- * Prompt user to select a scenario
167
- */
168
- async function selectChecklistItem(items) {
169
- if (items.length === 0) {
170
- return null;
171
- }
172
- const choices = items.map((item) => {
173
- const emoji = item.status === 'closed' && item.terminalReason === 'approved'
174
- ? '\u2705'
175
- : item.status === 'verified'
176
- ? '\ud83d\udfe2' // green circle
177
- : item.status === 'incomplete'
178
- ? '\ud83d\udfe0' // orange circle
179
- : item.status === 'blocked'
180
- ? '\ud83d\uded1'
181
- : item.status === 'closed'
182
- ? '\u26d4'
183
- : item.status === 'verification_in_progress'
184
- ? '\u23f3'
185
- : '\u2b1c';
186
- const commentBadge = item.unaddressedCommentCount > 0
187
- ? ` [${item.unaddressedCommentCount} comments]`
188
- : '';
189
- const disabledReason = item.actionable
190
- ? false
191
- : item.status === 'closed' && item.terminalReason
192
- ? item.terminalReason
193
- : 'not actionable';
194
- return {
195
- name: `${item.displayIndex + 1}. ${emoji} ${item.description}${commentBadge}`,
196
- value: item.id,
197
- disabled: disabledReason,
198
- };
199
- });
200
- const { selected } = await inquirer.prompt([
201
- {
202
- type: 'list',
203
- name: 'selected',
204
- message: 'Which scenario does this verify?',
205
- choices,
206
- },
207
- ]);
208
- return items.find((i) => i.id === selected) || null;
209
- }
210
- /**
211
- * Handle incomplete verification - check if all other scenarios are terminal and prompt user
212
- */
213
- async function handleIncompleteItem(featureId, incompleteItem, result) {
214
- // Get action items to check if there are other items to work on
215
- const { items: actionItems } = await getActionItems(featureId);
216
- const otherItems = actionItems.filter((i) => i.id !== incompleteItem.id);
217
- const allOthersTerminal = otherItems.every((i) => i.status === 'verified' ||
218
- i.status === 'blocked' ||
219
- i.status === 'closed' ||
220
- i.status === 'incomplete');
221
- console.log(`\n${'='.repeat(60)}`);
222
- console.log(`INCOMPLETE - Verification found issues`);
223
- console.log(`${'='.repeat(60)}`);
224
- // Display structured list of issues
225
- if (result.issues && result.issues.length > 0) {
226
- console.log(`\nIssues found:`);
227
- for (const issue of result.issues) {
228
- console.log(` • ${issue.description}`);
229
- }
230
- }
231
- else if (result.evaluationReason) {
232
- console.log(`\nReason: ${result.evaluationReason}`);
233
- }
234
- console.log(`\nNext steps:`);
235
- console.log(` 1. Fix the issues above in your code`);
236
- console.log(` 2. Run 'ranger go' again to re-verify`);
237
- if (allOthersTerminal && otherItems.length > 0) {
238
- console.log(`\nAll other scenarios are complete.`);
239
- console.log(`If you're done for now, you can stop and resume later with 'ranger resume'.`);
240
- }
241
- console.log(`${'='.repeat(60)}\n`);
242
- }
243
- /**
244
- * Create a PostToolUse hook that logs browser tool calls to stdout and tracks
245
- * all tool calls via telemetry with per-call timing.
246
- */
247
- function createToolCallTrackingHook(telemetry) {
248
- const toolCallCounts = new Map();
249
- const hook = async (input) => {
250
- if (input.hook_event_name !== 'PostToolUse')
251
- return {};
252
- const postInput = input;
253
- const toolInput = postInput.tool_input;
254
- const shortName = postInput.tool_name.replace('mcp__ranger-browser__', '');
255
- // Track count
256
- toolCallCounts.set(shortName, (toolCallCounts.get(shortName) || 0) + 1);
257
- // Log tool call as telemetry event
258
- telemetry.trackPhaseStart('tool_call', { toolName: shortName });
259
- telemetry.trackPhaseEnd('tool_call', {
260
- toolName: shortName,
261
- toolInput: summarizeToolInput(shortName, toolInput),
262
- });
263
- // Console log
264
- switch (postInput.tool_name) {
265
- case 'mcp__ranger-browser__browser_navigate':
266
- console.log(`[browser] Navigate → ${toolInput.url}`);
267
- break;
268
- case 'mcp__ranger-browser__browser_click':
269
- console.log(`[browser] Click → "${toolInput.element}"`);
270
- break;
271
- case 'mcp__ranger-browser__browser_type':
272
- console.log(`[browser] Type → "${toolInput.text}" into "${toolInput.element}"`);
273
- break;
274
- case 'mcp__ranger-browser__browser_press_key':
275
- console.log(`[browser] Press key → ${toolInput.key}`);
276
- break;
277
- case 'mcp__ranger-browser__browser_wait_for':
278
- console.log(`[browser] Wait → ${toolInput.time ? `${toolInput.time}ms` : toolInput.text || 'condition'}`);
279
- break;
280
- }
281
- return {};
282
- };
283
- return { hook, toolCallCounts };
284
- }
285
- /**
286
- * Create a PostToolUseFailure hook that tracks tool failures via telemetry.
287
- */
288
- function createToolFailureHook(telemetry) {
289
- return async (input) => {
290
- if (input.hook_event_name !== 'PostToolUseFailure')
291
- return {};
292
- const failInput = input;
293
- const shortName = failInput.tool_name.replace('mcp__ranger-browser__', '');
294
- await telemetry.trackPhaseError('tool_failure', failInput.error, {
295
- toolName: shortName,
296
- isInterrupt: failInput.is_interrupt,
297
- });
298
- return {};
299
- };
300
- }
301
- /**
302
- * Summarize tool input for telemetry (avoid logging sensitive/large data).
303
- */
304
- function summarizeToolInput(toolName, input) {
305
- switch (toolName) {
306
- case 'browser_navigate':
307
- return { url: input.url };
308
- case 'browser_click':
309
- return { element: input.element };
310
- case 'browser_type':
311
- return { element: input.element };
312
- case 'browser_take_screenshot':
313
- return { filename: input.filename };
314
- case 'browser_press_key':
315
- return { key: input.key };
316
- case 'browser_wait_for':
317
- return { time: input.time, text: input.text };
318
- default:
319
- return {};
320
- }
321
- }
322
- /**
323
- * Create a PostToolUse hook that uploads screenshots immediately after they're taken.
324
- * Returns the hook callback and a set of filenames that were successfully uploaded,
325
- * so the post-hoc fallback can skip them.
326
- */
327
- function createScreenshotUploadHook(sessionId, checklistItemId, traceDir, telemetry) {
328
- const uploadedFiles = new Set();
329
- let position = 1;
330
- const hook = async (input) => {
331
- if (input.hook_event_name !== 'PostToolUse')
332
- return {};
333
- const postInput = input;
334
- if (postInput.tool_name !==
335
- 'mcp__ranger-browser__browser_take_screenshot')
336
- return {};
337
- // Extract filename from tool_input
338
- const toolInput = postInput.tool_input;
339
- const filename = toolInput?.filename;
340
- if (!filename)
341
- return {};
342
- const isKeyFrame = filename.toLowerCase().startsWith('key_');
343
- telemetry.trackPhaseStart('hook_screenshot_upload', {
344
- filename,
345
- isKeyFrame,
346
- });
347
- try {
348
- const pngPath = join(traceDir, filename);
349
- const pngBuffer = await readFile(pngPath);
350
- const pngStat = await stat(pngPath);
351
- const displayName = filename
352
- .replace(/\.png$/i, '')
353
- .replace(/^key_/i, '')
354
- .replace(/^\d+_/, '')
355
- .replace(/-/g, ' ');
356
- const currentPosition = position++;
357
- // Create verification step
358
- console.log(`[hook] Creating step #${currentPosition}: "${displayName}"${isKeyFrame ? ' (key frame)' : ''}`);
359
- const { step } = await createVerificationStep(sessionId, {
360
- checklistItemId,
361
- position: currentPosition,
362
- stepType: 'screenshot',
363
- stepName: displayName,
364
- description: isKeyFrame
365
- ? 'Key moment captured during verification'
366
- : 'Screenshot captured during verification',
367
- isKeyStep: isKeyFrame,
368
- status: 'success',
369
- metadata: {
370
- filename,
371
- timestamp: pngStat.mtime.toISOString(),
372
- },
373
- });
374
- // Create step asset
375
- const assetResponse = await createStepAsset(sessionId, step.id, {
376
- filename,
377
- assetType: 'screenshot',
378
- timing: 'after',
379
- position: 0,
380
- capturedAt: pngStat.mtime.toISOString(),
381
- metadata: {
382
- name: displayName,
383
- highPriority: isKeyFrame,
384
- },
385
- });
386
- // Upload
387
- await uploadScreenshot(assetResponse.uploadUrl, pngBuffer);
388
- // Track as uploaded
389
- uploadedFiles.add(filename);
390
- telemetry.trackPhaseEnd('hook_screenshot_upload', {
391
- filename,
392
- bytes: pngBuffer.length,
393
- });
394
- }
395
- catch (err) {
396
- await telemetry.trackPhaseError('hook_screenshot_upload', err, {
397
- filename,
398
- });
399
- }
400
- return {};
401
- };
402
- return { hook, uploadedFiles };
403
- }
404
- /**
405
- * Phase 1: Setup through agent completion.
406
- * Returns context for processVerificationResult.
407
- */
408
- async function runVerification(options, telemetry) {
409
- const isDebugMode = !!options.debugOutcome;
410
- if (isDebugMode) {
411
- console.log(`\n[DEBUG MODE] Running minimal browser test with outcome: ${options.debugOutcome}`);
412
- }
413
- // --- Phase: feature_load ---
414
- telemetry.trackPhaseStart('feature_load');
415
- const featureId = await getActiveFeatureId();
416
- if (!featureId) {
417
- throw new Error('No active feature review. Run: ranger resume <id> or ranger create');
418
- }
419
- const feature = await getFeature(featureId);
420
- telemetry.setContext({ featureId });
421
- const currentBranch = getGitBranch();
422
- if (currentBranch && currentBranch !== feature.gitBranch) {
423
- await updateFeature(featureId, { gitBranch: currentBranch });
424
- console.log(` Updated branch to: ${currentBranch}`);
425
- }
426
- console.log(`\nActive feature review: ${feature.name} (${feature.dashboardUrl})`);
427
- const { items: actionItems } = await getActionItems(featureId);
428
- const actionItemsById = new Map(actionItems.map((item) => [item.id, item]));
429
- const displayItems = feature.checklistItems.map((item, index) => {
430
- const actionItem = actionItemsById.get(item.id);
431
- return {
432
- ...item,
433
- unaddressedCommentCount: actionItem?.unaddressedCommentCount ?? 0,
434
- displayIndex: index,
435
- actionable: !!actionItem && item.status !== 'closed',
436
- };
437
- });
438
- telemetry.trackPhaseEnd('feature_load', {
439
- itemCount: actionItems.length,
440
- });
441
- // --- Phase: scenario_select ---
442
- telemetry.trackPhaseStart('scenario_select');
443
- let checklistItem = null;
444
- let taskDescription = options.notes;
445
- if (options.scenario !== undefined) {
446
- // Use specified item index (1-based)
447
- const itemIndex = options.scenario - 1; // 1-based to 0-based
448
- if (itemIndex < 0 || itemIndex >= displayItems.length) {
449
- throw new Error(`Invalid scenario index: ${options.scenario}. Feature review has ${displayItems.length} scenarios.`);
450
- }
451
- const displayItem = displayItems[itemIndex];
452
- if (!displayItem.actionable) {
453
- const reason = displayItem.status === 'closed' && displayItem.terminalReason
454
- ? displayItem.terminalReason
455
- : 'not actionable';
456
- throw new Error(`Scenario ${options.scenario} is ${reason} and cannot be verified. Choose a different scenario.`);
457
- }
458
- const actionItem = actionItemsById.get(displayItem.id);
459
- if (!actionItem) {
460
- throw new Error(`Scenario ${options.scenario} is not currently actionable. Try another scenario.`);
461
- }
462
- checklistItem = actionItem;
463
- if (!taskDescription) {
464
- taskDescription = checklistItem.description;
465
- }
466
- }
467
- else {
468
- const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
469
- if (!isInteractive) {
470
- // Non-TTY mode: require --scenario flag, show available scenarios
471
- console.log('\nNon-interactive mode detected. The --scenario flag is required.');
472
- console.log('\nAvailable scenarios to verify:');
473
- displayItems.forEach((item) => {
474
- const emoji = item.status === 'closed' &&
475
- item.terminalReason === 'approved'
476
- ? '\u2705'
477
- : item.status === 'verified'
478
- ? '\ud83d\udfe2'
479
- : item.status === 'incomplete'
480
- ? '\ud83d\udfe0'
481
- : item.status === 'blocked'
482
- ? '\ud83d\uded1'
483
- : item.status === 'closed'
484
- ? '\u26d4'
485
- : item.status === 'verification_in_progress'
486
- ? '\u23f3'
487
- : '\u2b1c';
488
- const commentBadge = item.unaddressedCommentCount > 0
489
- ? ` [${item.unaddressedCommentCount} comments]`
490
- : '';
491
- const actionHint = item.actionable ? '' : ' [not actionable]';
492
- console.log(` ${item.displayIndex + 1}. ${emoji} ${item.description}${commentBadge}${actionHint}`);
493
- });
494
- console.log('\nUsage: ranger go --scenario <number>');
495
- console.log('Example: ranger go --scenario 1');
496
- throw new Error('The --scenario flag is required in non-interactive mode. See available scenarios above.');
497
- }
498
- const selectedItem = await selectChecklistItem(displayItems);
499
- if (selectedItem) {
500
- const actionItem = actionItemsById.get(selectedItem.id);
501
- if (!actionItem) {
502
- throw new Error('Selected scenario is not currently actionable. Choose another scenario.');
503
- }
504
- checklistItem = actionItem;
505
- if (!taskDescription) {
506
- taskDescription = checklistItem.description;
507
- }
508
- }
509
- }
510
- if (!checklistItem) {
511
- throw new Error('No scenario selected. Create scenarios when creating the feature review with -c or --scenario flags.');
512
- }
513
- if (checklistItem.status === 'closed') {
514
- throw new Error(`Cannot verify scenario "${checklistItem.description}" — it is concluded (${checklistItem.terminalReason || 'unknown reason'}).`);
515
- }
516
- if (!taskDescription) {
517
- throw new Error('No notes provided');
518
- }
519
- telemetry.setContext({ checklistItemId: checklistItem.id });
520
- telemetry.trackPhaseEnd('scenario_select', {
521
- selectionMethod: options.scenario !== undefined ? 'flag' : 'interactive',
522
- itemStatus: checklistItem.status,
523
- });
524
- console.log(`\nVerifying scenario: ${checklistItem.description}`);
525
- console.log(`Notes: ${taskDescription}`);
526
- // Fetch reviewer feedback if item has parent or unaddressed comments
527
- let itemFeedback = null;
528
- if (checklistItem.parentItemId ||
529
- checklistItem.unaddressedCommentCount > 0) {
530
- try {
531
- itemFeedback = await getItemFeedback(featureId, checklistItem.id);
532
- if (itemFeedback.unaddressedComments.length > 0) {
533
- console.log(`Reviewer feedback: ${itemFeedback.unaddressedComments.length} comment(s) to verify`);
534
- }
535
- }
536
- catch (err) {
537
- // Non-fatal - continue without feedback, but log it
538
- await telemetry.trackPhaseError('feedback_fetch', err);
539
- }
540
- }
541
- // Start the session if it's in ready status
542
- if (feature.currentSession &&
543
- feature.currentSession.status === 'ready' &&
544
- feature.currentSessionId) {
545
- try {
546
- await startSession(featureId, feature.currentSessionId);
547
- }
548
- catch (error) {
549
- const message = error instanceof Error ? error.message : String(error);
550
- if (!message.includes('already')) {
551
- throw error;
552
- }
553
- }
554
- }
555
- // Update scenario status to verification_in_progress
556
- await updateChecklistItem(featureId, checklistItem.id, {
557
- status: 'verification_in_progress',
558
- });
559
- // --- Phase: profile_resolution ---
560
- telemetry.trackPhaseStart('profile_resolution');
561
- let activeProfile = null;
562
- if (options.profile) {
563
- activeProfile = options.profile;
564
- }
565
- else {
566
- activeProfile = await readActiveProfileName();
567
- }
568
- if (!activeProfile) {
569
- throw new Error('No active profile. Run: ranger profile use <profile-name>');
570
- }
571
- const envDir = getEnvDir(activeProfile);
572
- if (!existsSync(envDir)) {
573
- throw new Error(`Profile "${activeProfile}" not found. Run: ranger profile add ${activeProfile}`);
574
- }
575
- const settings = await loadSettings(activeProfile);
576
- const resolvedSettings = resolveEnvVars(settings);
577
- let url = resolvedSettings.baseUrl;
578
- if (!url) {
579
- throw new Error(`No baseUrl configured for profile "${activeProfile}". Run: ranger profile config set ${activeProfile} baseUrl <url>`);
580
- }
581
- if (options.startPath) {
582
- const base = url.endsWith('/') ? url.slice(0, -1) : url;
583
- const path = options.startPath.startsWith('/')
584
- ? options.startPath
585
- : '/' + options.startPath;
586
- url = base + path;
587
- }
588
- telemetry.trackPhaseEnd('profile_resolution', {
589
- profileName: activeProfile,
590
- });
591
- // --- Phase: browser_session_create ---
592
- telemetry.trackPhaseStart('browser_session_create');
593
- const token = await getToken();
594
- if (!token) {
595
- throw new Error('No API token configured. Run: ranger setup [token]');
596
- }
597
- const browserSession = await createBrowserSession({
598
- environmentName: activeProfile,
599
- settings: resolvedSettings,
600
- task: taskDescription,
601
- url,
602
- featureId,
603
- checklistItemId: checklistItem.id,
604
- });
605
- console.log(`Browser session created: ${browserSession.id}`);
606
- telemetry.setContext({ browserSessionId: browserSession.id });
607
- // Link the browser session to the scenario immediately so steps
608
- // are visible in the dashboard while verification is in progress
609
- await updateChecklistItem(featureId, checklistItem.id, {
610
- browserSessionId: browserSession.id,
611
- });
612
- telemetry.trackPhaseEnd('browser_session_create');
613
- // --- Phase: playwright_config ---
614
- telemetry.trackPhaseStart('playwright_config');
615
- let configResult;
616
- let sessionToken;
617
- try {
618
- sessionToken = await getProxySessionToken();
619
- }
620
- catch (error) {
621
- const message = error instanceof Error ? error.message : String(error);
622
- const errorMsg = `Failed to fetch proxy session token: ${message}`;
623
- try {
624
- await updateBrowserSession(browserSession.id, {
625
- status: 'failed',
626
- durationMs: 0,
627
- errorMessage: errorMsg,
628
- });
629
- }
630
- catch (updateErr) {
631
- await telemetry.trackPhaseError('session_error_update', updateErr);
632
- }
633
- throw new Error(errorMsg);
634
- }
635
- configResult = await buildPlaywrightConfig(resolvedSettings, activeProfile, browserSession?.id);
636
- telemetry.trackPhaseEnd('playwright_config');
637
- const startTime = Date.now();
638
- const rangerBrowserMcp = {
639
- command: 'npx',
640
- args: [
641
- '@ranger-testing/playwright',
642
- 'run-mcp-server',
643
- '--config',
644
- configResult.configPath,
645
- ],
646
- };
647
- // Build verifier prompt
648
- let verifierPrompt;
649
- if (isDebugMode) {
650
- let debugFeedbackSection = '';
651
- if (itemFeedback && itemFeedback.unaddressedComments.length > 0) {
652
- const commentLines = itemFeedback.unaddressedComments
653
- .map((c) => `- [${c.id}] "${c.content}"`)
654
- .join('\n');
655
- const instruction = options.debugAddressComments
656
- ? 'For debug purposes, mark ALL of the following comments as addressed by including every ID in addressedCommentIds.'
657
- : 'For debug purposes, do NOT mark any comments as addressed. Return an empty addressedCommentIds array.';
658
- debugFeedbackSection = `\n\n## Debug: Reviewer Comments\n${instruction}\n\n${commentLines}`;
659
- }
660
- verifierPrompt = getDebugPrompt() + debugFeedbackSection;
661
- }
662
- else {
663
- const notesSection = checklistItem.notes
664
- ? `\n\n## Additional Notes\n${checklistItem.notes}`
665
- : '';
666
- let feedbackSection = '';
667
- if (itemFeedback && itemFeedback.unaddressedComments.length > 0) {
668
- const commentLines = itemFeedback.unaddressedComments
669
- .map((c) => {
670
- const date = new Date(c.createdAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
671
- const author = c.authorName || c.authorEmail || 'Reviewer';
672
- return `- [${c.id}] **${author}** (${date}): "${c.content}"`;
673
- })
674
- .join('\n');
675
- feedbackSection = `\n\n## Reviewer Feedback to Address
676
- The following reviewer comments were left on the previous version of this item.
677
- Verify that each concern has been addressed in the current implementation.
678
- For each comment you believe has been addressed, include its ID (the bracketed value) in the addressedCommentIds array in your output.
679
-
680
- ${commentLines}`;
681
- }
682
- let canonicalFlowSection = '';
683
- if (itemFeedback?.canonicalFlow) {
684
- canonicalFlowSection = `\n\n## Expected Flow (from previous verification)
685
- ${itemFeedback.canonicalFlow}`;
686
- }
687
- verifierPrompt = `You are a Feature Review Verifier. Your job is to verify a scenario by executing a UI flow and evaluating whether it adequately completes the scenario.
688
-
689
- ## Scenario to Verify
690
- ${checklistItem.description}${notesSection}${feedbackSection}${canonicalFlowSection}
691
-
692
- ## Task to Execute
693
- ${taskDescription}
694
-
695
- CRITICAL URL REQUIREMENT:
696
- Your base URL is: ${url}
697
- - You may ONLY navigate to paths under this base URL (same protocol, host, and port)
698
- - For example, if the base URL is "http://localhost:3000", you can navigate to "http://localhost:3000/home", "http://localhost:3000/settings", etc.
699
- - DO NOT navigate to any different domain, host, or port under any circumstances
700
- - IGNORE any URLs from product documentation (mcp__ranger__get_product_docs) that have a different base URL
701
- - If documentation or code diffs suggest a path exists (e.g., "/dashboard"), you may navigate to that path ONLY under the base URL above
702
- - The base URL above is the ONLY authorized profile for this verification
703
-
704
- ## Instructions
705
- 1. Navigate to the URL above using browser_navigate
706
- 2. Take a snapshot with browser_snapshot to see the page
707
- 3. **IMMEDIATELY check for blocking HTTP errors before proceeding**
708
- 4. Execute the task step-by-step using browser tools
709
- 5. **Take screenshots at key moments** (see Screenshot Guidelines below)
710
- 6. Document any issues found (bugs, errors, unexpected behavior)
711
- 7. After completing the verification, evaluate whether the result adequately verifies the scenario
712
-
713
- ## Screenshot Guidelines - IMPORTANT
714
- Take screenshots throughout the verification flow so a human can review it for completeness. Screenshots are your evidence trail.
715
-
716
- **When to take screenshots (use browser_take_screenshot):**
717
- - After initial page load (capture starting state)
718
- - Before and after clicking buttons or submitting forms
719
- - When important UI elements appear (modals, notifications, loading states)
720
- - After navigation to new pages
721
- - When verifying specific elements exist
722
- - At the final state showing the completed action
723
-
724
- **Screenshot naming:**
725
- - Use descriptive filenames: "01_login-page-loaded.png", "02_form-filled.png", "03_dashboard-visible.png"
726
- - Number prefixes (01_, 02_, etc.) help maintain chronological order
727
- - For KEY MOMENTS that prove the scenario is complete, prefix with "key_": "key_04_success-message.png", "key_05_final-state.png"
728
- - The "key_" prefix marks screenshots as high-priority evidence for human reviewers
729
-
730
- **Aim for 3-6 screenshots per verification** to document the complete flow. Mark 1-2 of the most important ones with the "key_" prefix.
731
-
732
- ## Critical: Early Error Detection
733
- After step 2 (taking initial snapshot), IMMEDIATELY check for blocking HTTP errors:
734
-
735
- **Blocking errors to detect:**
736
- - HTTP 404: "404", "Not Found", "Page not found", "does not exist"
737
- - HTTP 500: "500", "Internal Server Error", "Server Error", "Something went wrong"
738
- - HTTP 400: "400", "Bad Request", "Invalid request"
739
-
740
- **Also check for:**
741
- - Framework error pages (Next.js error boundary, React error page, "Application error")
742
- - Completely blank/empty pages with no content
743
- - "Cannot GET /path" messages
744
-
745
- **If ANY blocking error is detected:**
746
- 1. DO NOT continue with the task
747
- 2. Return IMMEDIATELY with evaluation: "blocked"
748
- 3. Set evaluationReason to describe the specific error (e.g., "HTTP 404 - Page not found at /dashboard")
749
- 4. Include the error in issues array with severity: "BLOCKER" and appropriate type (HTTP_404, HTTP_500, HTTP_400, or NAVIGATION_ERROR)
750
-
751
- This early exit prevents wasting time on tasks that cannot succeed due to fundamental errors.
752
-
753
- ## Evaluation Criteria
754
- - VERIFIED: The task completed successfully and the scenario requirements are fully met
755
- - PARTIAL: The task partially completed but some aspects of the scenario are not verified
756
- - BLOCKED: A blocking issue (bug, error, missing feature) prevents completion
757
- - FAILED: The task could not be completed due to errors
758
-
759
- Return your findings in the structured output format with your evaluation.`;
760
- }
761
- const outputSchema = {
762
- type: 'object',
763
- properties: {
764
- success: { type: 'boolean' },
765
- summary: { type: 'string' },
766
- evaluation: {
767
- type: 'string',
768
- enum: ['verified', 'partial', 'blocked', 'failed'],
769
- },
770
- evaluationReason: { type: 'string' },
771
- issues: {
772
- type: 'array',
773
- items: {
774
- type: 'object',
775
- properties: {
776
- severity: {
777
- type: 'string',
778
- enum: ['BLOCKER', 'MAJOR', 'MINOR'],
779
- },
780
- type: {
781
- type: 'string',
782
- enum: [
783
- 'HTTP_404',
784
- 'HTTP_500',
785
- 'HTTP_400',
786
- 'NAVIGATION_ERROR',
787
- 'OTHER',
788
- ],
789
- },
790
- description: { type: 'string' },
791
- screenshot: { type: 'string' },
792
- },
793
- required: ['severity', 'description'],
794
- },
795
- },
796
- addressedCommentIds: {
797
- type: 'array',
798
- description: 'IDs of reviewer comments that have been addressed in the current implementation',
799
- items: { type: 'string' },
800
- },
801
- },
802
- required: ['success', 'summary', 'evaluation', 'evaluationReason'],
803
- };
804
- // --- Phase: agent_execution ---
805
- telemetry.trackPhaseStart('agent_execution');
806
- const traceDir = getTraceDirectory(browserSession.id);
807
- const screenshotHook = createScreenshotUploadHook(browserSession.id, checklistItem.id, traceDir, telemetry);
808
- const toolCallHook = createToolCallTrackingHook(telemetry);
809
- const toolFailureHook = createToolFailureHook(telemetry);
810
- const result = query({
811
- prompt: verifierPrompt,
812
- options: {
813
- cwd: process.cwd(),
814
- model: 'claude-opus-4-6',
815
- mcpServers: {
816
- 'ranger-browser': rangerBrowserMcp,
817
- },
818
- tools: ['mcp__ranger-browser__*'],
819
- permissionMode: 'acceptEdits',
820
- allowedTools: ['mcp__ranger-browser__*', 'Read', 'Glob', 'Grep'],
821
- outputFormat: {
822
- type: 'json_schema',
823
- schema: outputSchema,
824
- },
825
- hooks: {
826
- PostToolUse: [
827
- {
828
- hooks: [toolCallHook.hook, screenshotHook.hook],
829
- },
830
- ],
831
- PostToolUseFailure: [
832
- {
833
- hooks: [toolFailureHook],
834
- },
835
- ],
836
- },
837
- env: {
838
- ...process.env,
839
- ANTHROPIC_API_KEY: sessionToken,
840
- ANTHROPIC_BASE_URL: getAiProxyUrl(),
841
- },
842
- persistSession: false,
843
- },
844
- });
845
- // Collect messages
846
- let finalResult = null;
847
- let agentError = null;
848
- let lastStructuredOutputInput = null;
849
- let resultMeta = {};
850
- const conversationFilePath = getConversationFilePath(browserSession.id);
851
- const conversationDir = dirname(conversationFilePath);
852
- await mkdir(conversationDir, { recursive: true });
853
- const TIMEOUT_MS = 59 * 60 * 1000;
854
- const timeoutPromise = new Promise((_, reject) => {
855
- setTimeout(() => {
856
- reject(new Error('Agent execution timed out after 59 minutes'));
857
- }, TIMEOUT_MS);
858
- });
859
- try {
860
- await Promise.race([
861
- (async () => {
862
- for await (const message of result) {
863
- try {
864
- const jsonLine = JSON.stringify(message) + '\n';
865
- await appendFile(conversationFilePath, jsonLine, 'utf-8');
866
- }
867
- catch {
868
- // Ignore
869
- }
870
- const msg = message;
871
- // Capture StructuredOutput tool call input as fallback
872
- if (msg.type === 'assistant' && msg.message?.content) {
873
- for (const block of msg.message.content) {
874
- if (block.type === 'tool_use' &&
875
- block.name === 'StructuredOutput' &&
876
- block.input) {
877
- lastStructuredOutputInput =
878
- block.input;
879
- }
880
- }
881
- }
882
- if (msg.error) {
883
- let errorText = msg.error;
884
- if (msg.message?.content &&
885
- Array.isArray(msg.message.content)) {
886
- const texts = msg.message.content
887
- .filter((c) => c.type === 'text')
888
- .map((c) => c.text || '')
889
- .filter(Boolean);
890
- if (texts.length > 0) {
891
- errorText = texts.join(' ');
892
- }
893
- }
894
- agentError = errorText;
895
- }
896
- if (msg.type === 'result') {
897
- // Capture SDK result metadata
898
- resultMeta = {
899
- numTurns: msg.num_turns,
900
- totalCostUsd: msg.total_cost_usd,
901
- durationApiMs: msg.duration_api_ms,
902
- sdkDurationMs: msg.duration_ms,
903
- inputTokens: msg.usage?.input_tokens,
904
- outputTokens: msg.usage?.output_tokens,
905
- cacheReadTokens: msg.usage?.cache_read_input_tokens,
906
- cacheCreationTokens: msg.usage?.cache_creation_input_tokens,
907
- };
908
- if (msg.subtype === 'success' &&
909
- message.structured_output) {
910
- finalResult = message.structured_output;
911
- }
912
- else if (msg.subtype !== 'success') {
913
- if (lastStructuredOutputInput &&
914
- msg.errors?.length === 0) {
915
- finalResult = lastStructuredOutputInput;
916
- agentError = null;
917
- }
918
- else if (!agentError) {
919
- agentError =
920
- msg.errors?.join(', ') || 'Unknown error';
921
- }
922
- }
923
- }
924
- }
925
- })(),
926
- timeoutPromise,
927
- ]);
928
- }
929
- catch (error) {
930
- agentError = error instanceof Error ? error.message : String(error);
931
- }
932
- const durationMs = Date.now() - startTime;
933
- telemetry.trackPhaseEnd('agent_execution', {
934
- ...resultMeta,
935
- toolCallCounts: Object.fromEntries(toolCallHook.toolCallCounts),
936
- hasResult: !!finalResult,
937
- hasError: !!agentError,
938
- });
939
- return {
940
- featureId,
941
- checklistItem,
942
- browserSession,
943
- finalResult,
944
- agentError,
945
- lastStructuredOutputInput,
946
- screenshotHook,
947
- toolCallCounts: toolCallHook.toolCallCounts,
948
- configResult,
949
- startTime,
950
- durationMs,
951
- conversationFilePath,
952
- conversationDir,
953
- isDebugMode,
954
- debugOutcome: options.debugOutcome,
955
- debugAddressComments: options.debugAddressComments,
956
- resultMeta,
957
- telemetry,
958
- feedbackCommentIds: itemFeedback
959
- ? itemFeedback.unaddressedComments.map((c) => c.id)
960
- : [],
961
- };
962
- }
963
- /**
964
- * Phase 2: Upload artifacts, evaluate result, update scenario.
965
- */
966
- async function processVerificationResult(ctx) {
967
- const { featureId, checklistItem, browserSession, screenshotHook, durationMs, conversationFilePath, isDebugMode, debugOutcome, debugAddressComments, telemetry, feedbackCommentIds, } = ctx;
968
- const { finalResult, agentError } = ctx;
969
- let traceDownloadUrl;
970
- // --- Upload trace ---
971
- try {
972
- const traceDir = getTraceDirectory(browserSession.id);
973
- if (existsSync(traceDir)) {
974
- const files = await readdir(traceDir);
975
- if (files.length > 0) {
976
- telemetry.trackPhaseStart('upload_trace');
977
- try {
978
- const traceUrls = await getUploadUrls(browserSession.id, 'trace.zip', 'zip');
979
- const traceBuffer = await zipDirectory(traceDir);
980
- await uploadTrace(traceUrls.uploadUrl, traceBuffer);
981
- traceDownloadUrl = traceUrls.downloadUrl;
982
- telemetry.trackPhaseEnd('upload_trace', {
983
- bytes: traceBuffer.length,
984
- });
985
- }
986
- catch (err) {
987
- await telemetry.trackPhaseError('upload_trace', err);
988
- }
989
- // --- Upload videos ---
990
- const videos = await loadSessionVideos(traceDir);
991
- for (const video of videos) {
992
- telemetry.trackPhaseStart('upload_video', {
993
- filename: video.filename,
994
- });
995
- try {
996
- const videoBuffer = await readFile(video.path);
997
- const videoUrls = await getUploadUrls(browserSession.id, video.filename, 'webm');
998
- await uploadVideo(videoUrls.uploadUrl, videoBuffer);
999
- telemetry.trackPhaseEnd('upload_video', {
1000
- filename: video.filename,
1001
- bytes: videoBuffer.length,
1002
- });
1003
- }
1004
- catch (err) {
1005
- await telemetry.trackPhaseError('upload_video', err, {
1006
- filename: video.filename,
1007
- });
1008
- }
1009
- }
1010
- // --- Upload remaining screenshots ---
1011
- const pngFiles = files
1012
- .filter((f) => f.toLowerCase().endsWith('.png'))
1013
- .filter((f) => !screenshotHook.uploadedFiles.has(f))
1014
- .sort();
1015
- const positionOffset = screenshotHook.uploadedFiles.size + 1;
1016
- for (let i = 0; i < pngFiles.length; i++) {
1017
- const pngFile = pngFiles[i];
1018
- const isKeyFrame = pngFile.toLowerCase().startsWith('key_');
1019
- telemetry.trackPhaseStart('upload_screenshot', {
1020
- filename: pngFile,
1021
- isKeyFrame,
1022
- });
1023
- try {
1024
- const pngPath = join(traceDir, pngFile);
1025
- const pngBuffer = await readFile(pngPath);
1026
- const pngStat = await stat(pngPath);
1027
- const displayName = pngFile
1028
- .replace(/\.png$/i, '')
1029
- .replace(/^key_/i, '')
1030
- .replace(/^\d+_/, '')
1031
- .replace(/-/g, ' ');
1032
- const { step } = await createVerificationStep(browserSession.id, {
1033
- checklistItemId: checklistItem.id,
1034
- position: positionOffset + i,
1035
- stepType: 'screenshot',
1036
- stepName: displayName,
1037
- description: isKeyFrame
1038
- ? 'Key moment captured during verification'
1039
- : 'Screenshot captured during verification',
1040
- isKeyStep: isKeyFrame,
1041
- status: 'success',
1042
- metadata: {
1043
- filename: pngFile,
1044
- timestamp: pngStat.mtime.toISOString(),
1045
- },
1046
- });
1047
- const assetResponse = await createStepAsset(browserSession.id, step.id, {
1048
- filename: pngFile,
1049
- assetType: 'screenshot',
1050
- timing: 'after',
1051
- position: 0,
1052
- capturedAt: pngStat.mtime.toISOString(),
1053
- metadata: {
1054
- name: displayName,
1055
- highPriority: isKeyFrame,
1056
- },
1057
- });
1058
- await uploadScreenshot(assetResponse.uploadUrl, pngBuffer);
1059
- telemetry.trackPhaseEnd('upload_screenshot', {
1060
- filename: pngFile,
1061
- bytes: pngBuffer.length,
1062
- });
1063
- }
1064
- catch (err) {
1065
- await telemetry.trackPhaseError('upload_screenshot', err, { filename: pngFile });
1066
- }
1067
- }
1068
- }
1069
- }
1070
- // --- Upload conversation ---
1071
- if (existsSync(conversationFilePath)) {
1072
- telemetry.trackPhaseStart('upload_conversation');
1073
- try {
1074
- const conversationUrls = await getUploadUrls(browserSession.id, 'conversation.jsonl', 'jsonl');
1075
- const conversationBuffer = await readFile(conversationFilePath);
1076
- await uploadConversation(conversationUrls.uploadUrl, conversationBuffer);
1077
- telemetry.trackPhaseEnd('upload_conversation', {
1078
- bytes: conversationBuffer.length,
1079
- });
1080
- }
1081
- catch (err) {
1082
- await telemetry.trackPhaseError('upload_conversation', err);
1083
- }
1084
- }
1085
- // --- Update browser session ---
1086
- telemetry.trackPhaseStart('update_session');
1087
- try {
1088
- const typedResult = finalResult;
1089
- const updateData = {
1090
- status: (agentError ? 'failed' : 'completed'),
1091
- durationMs,
1092
- agentResponse: typedResult?.summary || agentError || undefined,
1093
- errorMessage: agentError || undefined,
1094
- };
1095
- await updateBrowserSession(browserSession.id, updateData);
1096
- if (typedResult && traceDownloadUrl) {
1097
- typedResult.traceViewerUrl =
1098
- buildTraceViewerUrl(traceDownloadUrl);
1099
- typedResult.sessionId = browserSession.id;
1100
- typedResult.sessionDir = getTraceDirectory(browserSession.id);
1101
- typedResult.durationMs = durationMs;
1102
- typedResult.checklistItemId = checklistItem.id;
1103
- }
1104
- telemetry.trackPhaseEnd('update_session');
1105
- }
1106
- catch (err) {
1107
- await telemetry.trackPhaseError('update_session', err);
1108
- }
1109
- }
1110
- catch {
1111
- // Ignore upload errors
1112
- }
1113
- // --- Phase: evaluation ---
1114
- telemetry.trackPhaseStart('evaluation');
1115
- let resultForEval;
1116
- if (isDebugMode && debugOutcome) {
1117
- const mockEval = getMockEvaluation(debugOutcome);
1118
- const typedResult = finalResult;
1119
- resultForEval = {
1120
- ...mockEval,
1121
- sessionId: browserSession.id,
1122
- sessionDir: getTraceDirectory(browserSession.id),
1123
- durationMs,
1124
- traceViewerUrl: traceDownloadUrl
1125
- ? buildTraceViewerUrl(traceDownloadUrl)
1126
- : undefined,
1127
- checklistItemId: checklistItem.id,
1128
- addressedCommentIds: typedResult?.addressedCommentIds ?? [],
1129
- };
1130
- console.log(`\n[DEBUG MODE] Using mock evaluation: ${debugOutcome}`);
1131
- }
1132
- else {
1133
- const typedResult = finalResult;
1134
- if (agentError && !typedResult) {
1135
- throw new Error(`Verification failed: ${agentError}`);
1136
- }
1137
- if (!typedResult) {
1138
- throw new Error('No result received from agent');
1139
- }
1140
- resultForEval = typedResult;
1141
- }
1142
- telemetry.trackPhaseEnd('evaluation', {
1143
- evaluation: resultForEval.evaluation,
1144
- issueCount: resultForEval.issues?.length ?? 0,
1145
- });
1146
- // --- Phase: scenario_update ---
1147
- telemetry.trackPhaseStart('scenario_update');
1148
- const evaluation = resultForEval.evaluation;
1149
- if (evaluation === 'verified') {
1150
- await updateChecklistItem(featureId, checklistItem.id, {
1151
- status: 'verified',
1152
- browserSessionId: browserSession.id,
1153
- });
1154
- console.log(`\n\u2705 Scenario verified!`);
1155
- }
1156
- else if (evaluation === 'blocked') {
1157
- await updateChecklistItem(featureId, checklistItem.id, {
1158
- status: 'blocked',
1159
- browserSessionId: browserSession.id,
1160
- blockedReason: resultForEval.evaluationReason,
1161
- });
1162
- // Enhanced output for Claude Code
1163
- console.log(`\n${'='.repeat(60)}`);
1164
- console.log(`BLOCKING ISSUE DETECTED - Debug Required`);
1165
- console.log(`${'='.repeat(60)}`);
1166
- console.log(`\nIssue: ${resultForEval.evaluationReason}`);
1167
- if (resultForEval.issues?.length) {
1168
- console.log(`\nDetails:`);
1169
- for (const issue of resultForEval.issues) {
1170
- const typeStr = issue.type ? ` (${issue.type})` : '';
1171
- console.log(` - [${issue.severity}]${typeStr} ${issue.description}`);
1172
- }
1173
- }
1174
- if (resultForEval.traceViewerUrl) {
1175
- console.log(`\nTrace: ${resultForEval.traceViewerUrl}`);
1176
- }
1177
- console.log(`\nSuggested action: Debug this issue in your code, then run go again.`);
1178
- console.log(`${'='.repeat(60)}\n`);
1179
- }
1180
- else if (evaluation === 'partial' ||
1181
- evaluation === 'failed' ||
1182
- evaluation === 'incomplete') {
1183
- // Mark as incomplete - verification happened but requirements not fully met
1184
- await updateChecklistItem(featureId, checklistItem.id, {
1185
- status: 'incomplete',
1186
- browserSessionId: browserSession.id,
1187
- incompleteReason: resultForEval.evaluationReason,
1188
- });
1189
- // Check if other items are terminal and prompt user
1190
- await handleIncompleteItem(featureId, checklistItem, resultForEval);
1191
- }
1192
- telemetry.trackPhaseEnd('scenario_update', { newStatus: evaluation });
1193
- // --- Phase: comment_addressing ---
1194
- telemetry.trackPhaseStart('comment_addressing', {
1195
- totalFeedbackComments: feedbackCommentIds.length,
1196
- agentAddressedCount: resultForEval.addressedCommentIds?.length ?? 0,
1197
- });
1198
- const addressedIds = resultForEval.addressedCommentIds?.filter((id) => feedbackCommentIds.includes(id));
1199
- if (addressedIds && addressedIds.length > 0) {
1200
- try {
1201
- await markCommentsAddressed(featureId, checklistItem.id, addressedIds);
1202
- console.log(`Marked ${addressedIds.length} comment(s) as addressed`);
1203
- telemetry.trackPhaseEnd('comment_addressing', {
1204
- addressedCount: addressedIds.length,
1205
- });
1206
- }
1207
- catch (err) {
1208
- await telemetry.trackPhaseError('comment_addressing', err);
1209
- }
1210
- }
1211
- else {
1212
- telemetry.trackPhaseEnd('comment_addressing', {
1213
- addressedCount: 0,
1214
- });
1215
- }
1216
- return resultForEval;
1217
- }
1218
- /**
1219
- * Verify a scenario in the browser.
1220
- * Orchestrates runVerification -> processVerificationResult with telemetry.
1221
- */
1222
- export async function verifyFeature(options) {
1223
- const telemetry = createTelemetryCollector('go');
1224
- await telemetry.trackCommandStart({
1225
- hasProfile: !!options.profile,
1226
- hasScenario: options.scenario !== undefined,
1227
- hasNotes: !!options.notes,
1228
- isDebugMode: !!options.debugOutcome,
1229
- });
1230
- const { envNames } = await getEnvNames();
1231
- if (envNames.length === 0) {
1232
- throw new Error(formatProfileRequiredMessage((text) => bold(text)));
1233
- }
1234
- let ctx;
1235
- let interrupted = false;
1236
- let checklistItemResolved = false;
1237
- const handleInterrupt = async () => {
1238
- if (interrupted)
1239
- return;
1240
- interrupted = true;
1241
- console.log('\nVerification interrupted. Cleaning up...');
1242
- await telemetry.trackCommandEnd('interrupted', {
1243
- durationMs: ctx ? Date.now() - ctx.startTime : 0,
1244
- });
1245
- if (ctx) {
1246
- try {
1247
- await updateBrowserSession(ctx.browserSession.id, {
1248
- status: 'interrupted',
1249
- durationMs: Date.now() - ctx.startTime,
1250
- });
1251
- }
1252
- catch {
1253
- // Best effort
1254
- }
1255
- try {
1256
- await updateChecklistItem(ctx.featureId, ctx.checklistItem.id, {
1257
- status: 'pending',
1258
- });
1259
- }
1260
- catch {
1261
- // Best effort
1262
- }
1263
- if (ctx.configResult) {
1264
- try {
1265
- await cleanupTempFiles(ctx.configResult);
1266
- }
1267
- catch {
1268
- // Best effort
1269
- }
1270
- }
1271
- }
1272
- console.log('Scenario reset to pending. Partial steps are preserved.');
1273
- process.exit(0);
1274
- };
1275
- process.on('SIGINT', handleInterrupt);
1276
- process.on('SIGTERM', handleInterrupt);
1277
- try {
1278
- // Phase 1: Setup + agent execution
1279
- ctx = await runVerification(options, telemetry);
1280
- // Boundary flush — all agent execution telemetry is now safe
1281
- await telemetry.flush();
1282
- // Phase 2: Uploads + evaluation + status update
1283
- const resultForEval = await processVerificationResult(ctx);
1284
- checklistItemResolved = true;
1285
- await telemetry.trackCommandEnd('success', {
1286
- evaluation: resultForEval.evaluation,
1287
- durationMs: ctx.durationMs,
1288
- ...ctx.resultMeta,
1289
- });
1290
- return resultForEval;
1291
- }
1292
- catch (error) {
1293
- await telemetry.trackCommandError(error);
1294
- throw error;
1295
- }
1296
- finally {
1297
- process.removeListener('SIGINT', handleInterrupt);
1298
- process.removeListener('SIGTERM', handleInterrupt);
1299
- // If the scenario was never resolved (agent error, throw, etc.),
1300
- // reset it to pending so it doesn't stay stuck in verification_in_progress.
1301
- if (ctx && !interrupted && !checklistItemResolved) {
1302
- try {
1303
- await updateChecklistItem(ctx.featureId, ctx.checklistItem.id, {
1304
- status: 'pending',
1305
- });
1306
- console.log('Scenario reset to pending after unexpected error.');
1307
- }
1308
- catch (resetErr) {
1309
- await telemetry.trackPhaseError('scenario_reset', resetErr);
1310
- }
1311
- }
1312
- // --- Phase: cleanup ---
1313
- if (ctx) {
1314
- telemetry.trackPhaseStart('cleanup');
1315
- if (ctx.configResult) {
1316
- await cleanupTempFiles(ctx.configResult);
1317
- }
1318
- try {
1319
- const traceDir = getTraceDirectory(ctx.browserSession.id);
1320
- if (existsSync(traceDir)) {
1321
- await rm(traceDir, { recursive: true, force: true });
1322
- }
1323
- }
1324
- catch {
1325
- // Ignore
1326
- }
1327
- try {
1328
- if (ctx.conversationDir && existsSync(ctx.conversationDir)) {
1329
- await rm(ctx.conversationDir, {
1330
- recursive: true,
1331
- force: true,
1332
- });
1333
- }
1334
- }
1335
- catch {
1336
- // Ignore
1337
- }
1338
- telemetry.trackPhaseEnd('cleanup');
1339
- }
1340
- await telemetry.flush();
1341
- }
1342
- }
1343
- //# sourceMappingURL=verifyFeature.js.map
1
+ const _0x50bbd8=_0x2c84;(function(_0x2d4af0,_0x8ee58e){const _0xd41fb7=_0x2c84,_0x1af1ce=_0x2d4af0();while(!![]){try{const _0x55799f=-parseInt(_0xd41fb7(0x29c))/0x1+-parseInt(_0xd41fb7(0x254))/0x2+parseInt(_0xd41fb7(0x1cf))/0x3+-parseInt(_0xd41fb7(0x2a4))/0x4*(-parseInt(_0xd41fb7(0x271))/0x5)+parseInt(_0xd41fb7(0x20a))/0x6*(-parseInt(_0xd41fb7(0x1a9))/0x7)+-parseInt(_0xd41fb7(0x201))/0x8*(-parseInt(_0xd41fb7(0x287))/0x9)+-parseInt(_0xd41fb7(0x258))/0xa*(-parseInt(_0xd41fb7(0x24a))/0xb);if(_0x55799f===_0x8ee58e)break;else _0x1af1ce['push'](_0x1af1ce['shift']());}catch(_0x40838b){_0x1af1ce['push'](_0x1af1ce['shift']());}}}(_0x4ba3,0x40629));import{query}from'@anthropic-ai/claude-agent-sdk';import{createTelemetryCollector}from'./utils/telemetry.js';import{join,dirname}from'path';import{readFile,readdir,appendFile,mkdir,rm,stat}from'fs/promises';import{existsSync}from'fs';import{execSync}from'child_process';import{tmpdir}from'os';import _0x55dd70 from'inquirer';import{loadSettings,resolveEnvVars,buildPlaywrightConfig,cleanupTempFiles,getEnvDir}from'./utils/settings.js';import{createBrowserSession,updateBrowserSession,getUploadUrls,uploadTrace,uploadConversation,uploadScreenshot,uploadVideo,buildTraceViewerUrl,getProxySessionToken,createVerificationStep,createStepAsset}from'./utils/browserSessionsApi.js';import{getAiProxyUrl}from'./utils/environment.js';import{getToken}from'./utils/keychain.js';import{getActiveFeatureId}from'./feature.js';import{readActiveProfileName}from'./utils/activeProfile.js';import{getEnvNames}from'./env.js';import{formatProfileRequiredMessage}from'./utils/profileMessages.js';import{getFeature,updateFeature,updateChecklistItem,startSession,getActionItems,getItemFeedback,markCommentsAddressed}from'./utils/featureApi.js';import{getRangerDir}from'./utils/rangerRoot.js';const bold=_0x1ce33a=>_0x50bbd8(0x1c2)+_0x1ce33a+'\x1b[0m';function getGitBranch(){const _0x3b2a58=_0x50bbd8,_0x39806b={'sDYvm':function(_0x3aa731,_0x6b6253,_0x1d9886){return _0x3aa731(_0x6b6253,_0x1d9886);},'DDjPV':_0x3b2a58(0x2b2)};try{return _0x39806b['sDYvm'](execSync,'git\x20rev-parse\x20--abbrev-ref\x20HEAD',{'encoding':'utf-8','stdio':[_0x39806b['DDjPV'],_0x39806b[_0x3b2a58(0x1d7)],_0x39806b[_0x3b2a58(0x1d7)]]})[_0x3b2a58(0x1c0)]();}catch{return undefined;}}function _0x2c84(_0x571735,_0x34a6fb){_0x571735=_0x571735-0x187;const _0x4ba313=_0x4ba3();let _0x2c84f7=_0x4ba313[_0x571735];return _0x2c84f7;}async function zipDirectory(_0x111159){const _0x554d91=_0x50bbd8,_0x3461a2={'VFIji':_0x554d91(0x2a9),'LmdUs':'pipe'},_0x28b79d=join(_0x111159,'..',_0x3461a2['VFIji']);execSync('cd\x20\x22'+_0x111159+'\x22\x20&&\x20zip\x20-r\x20\x22'+_0x28b79d+_0x554d91(0x216),{'stdio':_0x3461a2['LmdUs']});const _0x4d58c3=await readFile(_0x28b79d);return execSync(_0x554d91(0x1aa)+_0x28b79d+'\x22',{'stdio':_0x554d91(0x2b2)}),_0x4d58c3;}function getTraceDirectory(_0x145bd0){const _0x27d8ec=_0x50bbd8,_0x1e337b={'rBSYQ':function(_0x43a433){return _0x43a433();},'EIQiS':'sessions'};return join(_0x1e337b['rBSYQ'](getRangerDir),_0x1e337b[_0x27d8ec(0x25c)],_0x145bd0);}function getConversationFilePath(_0x59dbb0){const _0x1f5b41=_0x50bbd8,_0x52f1d7={'mrZkM':function(_0x59917a){return _0x59917a();},'VXcuO':'conversation.jsonl'};return join(_0x52f1d7[_0x1f5b41(0x2af)](tmpdir),_0x1f5b41(0x193),_0x59dbb0,_0x52f1d7['VXcuO']);}async function loadSessionVideos(_0x43e53e){const _0x16d141=_0x50bbd8,_0x5c1626={'xLvkB':function(_0x398b42,_0x12967e,_0x59ddc2){return _0x398b42(_0x12967e,_0x59ddc2);},'iBzJD':_0x16d141(0x249)},_0x5f2e91=_0x5c1626['xLvkB'](join,_0x43e53e,_0x5c1626[_0x16d141(0x27d)]);if(!existsSync(_0x5f2e91))return[];const _0x21d1b5=await readdir(_0x5f2e91),_0x6986af=_0x21d1b5[_0x16d141(0x192)](_0x5787ba=>_0x5787ba[_0x16d141(0x27b)]()[_0x16d141(0x1f7)]('.webm'));return _0x6986af[_0x16d141(0x209)](_0x22d57f=>({'filename':_0x22d57f,'path':join(_0x5f2e91,_0x22d57f)}));}function getMockEvaluation(_0x2e0e46){const _0x593a72=_0x50bbd8,_0x2f7117={'fLJRg':'[DEBUG]\x20Mock\x20verification\x20completed\x20successfully.','eCqfM':'[DEBUG]\x20Mock\x20partial\x20verification.','CjQju':_0x593a72(0x2ba),'KKfsX':'incomplete','qnQap':'Implementation\x20is\x20incomplete\x20and\x20needs\x20additional\x20work.','Uwihl':_0x593a72(0x1d1),'PSVzy':_0x593a72(0x1c3),'gBcRO':'blocked','wMjDl':_0x593a72(0x1bd),'hJiLr':'HTTP_404','DbuTZ':'Target\x20page\x20returns\x20404\x20Not\x20Found','efBUn':'MAJOR','yFpVB':'Unable\x20to\x20proceed\x20due\x20to\x20missing\x20page','Btujv':_0x593a72(0x215)},_0x1421dc={'verified':{'success':!![],'summary':_0x2f7117[_0x593a72(0x268)],'evaluation':'verified','evaluationReason':_0x593a72(0x294)},'partial':{'success':![],'summary':_0x2f7117[_0x593a72(0x27e)],'evaluation':_0x593a72(0x29a),'evaluationReason':'Some\x20requirements\x20were\x20not\x20fully\x20verified.','issues':[{'severity':_0x593a72(0x1af),'type':_0x2f7117['CjQju'],'description':'Secondary\x20feature\x20not\x20fully\x20implemented'}]},'incomplete':{'success':![],'summary':'[DEBUG]\x20Mock\x20incomplete\x20verification.','evaluation':_0x2f7117['KKfsX'],'evaluationReason':_0x2f7117[_0x593a72(0x22a)],'issues':[{'severity':_0x593a72(0x22c),'type':_0x2f7117['CjQju'],'description':_0x2f7117['Uwihl']},{'severity':'MINOR','type':_0x593a72(0x2ba),'description':_0x593a72(0x1a5)}]},'blocked':{'success':![],'summary':_0x2f7117['PSVzy'],'evaluation':_0x2f7117[_0x593a72(0x21e)],'evaluationReason':'HTTP\x20404\x20-\x20Page\x20not\x20found.','issues':[{'severity':_0x2f7117[_0x593a72(0x1da)],'type':_0x2f7117['hJiLr'],'description':_0x2f7117[_0x593a72(0x1bb)]},{'severity':_0x2f7117['efBUn'],'type':'NAVIGATION_ERROR','description':_0x2f7117['yFpVB']}]},'failed':{'success':![],'summary':'[DEBUG]\x20Mock\x20failed\x20verification.','evaluation':_0x593a72(0x206),'evaluationReason':_0x2f7117[_0x593a72(0x22e)],'issues':[{'severity':'BLOCKER','type':_0x2f7117[_0x593a72(0x196)],'description':'Timeout\x20waiting\x20for\x20element'}]}};return _0x1421dc[_0x2e0e46];}function getDebugPrompt(){const _0x4cdbe9=_0x50bbd8;return _0x4cdbe9(0x2b7);}function _0x4ba3(){const _0xa2eeb8=['990462CrmbgH','join','Feature\x20is\x20partially\x20implemented\x20but\x20missing\x20key\x20functionality','createdAt','\x0a\x0aCRITICAL\x20URL\x20REQUIREMENT:\x0aYour\x20base\x20URL\x20is:\x20','hook_event_name','lZAJB','Example:\x20ranger\x20go\x20--scenario\x201','DDjPV','update_session','num_turns','wMjDl','sGQro','pending','SIGTERM','EwJJP','env','scenario_update',']\x20**','\x20\x201.\x20Fix\x20the\x20issues\x20above\x20in\x20your\x20code','unaddressedCommentCount','trackCommandEnd','wRvBl','PostToolUseFailure','\x20and\x20cannot\x20be\x20verified.\x20Choose\x20a\x20different\x20scenario.','result','profile_resolution','JmIOl','hgjrn','deJcI','LLCaF','evaluationReason','\x0aVerifying\x20scenario:\x20','2|4|0|3|1','time','No\x20active\x20feature\x20review.\x20Run:\x20ranger\x20resume\x20<id>\x20or\x20ranger\x20create','content','mcp__ranger-browser__*','FrPTm','addressedCommentIds','endsWith','string','kNZQm','\x20is\x20not\x20currently\x20actionable.\x20Try\x20another\x20scenario.','\x22.\x20Run:\x20ranger\x20profile\x20config\x20set\x20','No\x20notes\x20provided','flag','agnKT','\x0a[DEBUG\x20MODE]\x20Using\x20mock\x20evaluation:\x20','For\x20debug\x20purposes,\x20mark\x20ALL\x20of\x20the\x20following\x20comments\x20as\x20addressed\x20by\x20including\x20every\x20ID\x20in\x20addressedCommentIds.','56wnLfnm','array','structured_output','\x0aAll\x20other\x20scenarios\x20are\x20complete.','YYAvU','failed','after','MdhXG','map','512748IQlygC','ready','zzyCW','now','PWTsf','trace.zip','uploadUrl','@ranger-testing/playwright','YPUMA','fmWcW','description','Browser\x20automation\x20failed\x20with\x20timeout\x20error.','\x22\x20.','mUEpr','browser_session_create','KiJKr','BGbiZ','\x0a-\x20You\x20may\x20ONLY\x20navigate\x20to\x20paths\x20under\x20this\x20base\x20URL\x20(same\x20protocol,\x20host,\x20and\x20port)\x0a-\x20For\x20example,\x20if\x20the\x20base\x20URL\x20is\x20\x22http://localhost:3000\x22,\x20you\x20can\x20navigate\x20to\x20\x22http://localhost:3000/home\x22,\x20\x22http://localhost:3000/settings\x22,\x20etc.\x0a-\x20DO\x20NOT\x20navigate\x20to\x20any\x20different\x20domain,\x20host,\x20or\x20port\x20under\x20any\x20circumstances\x0a-\x20IGNORE\x20any\x20URLs\x20from\x20product\x20documentation\x20(mcp__ranger__get_product_docs)\x20that\x20have\x20a\x20different\x20base\x20URL\x0a-\x20If\x20documentation\x20or\x20code\x20diffs\x20suggest\x20a\x20path\x20exists\x20(e.g.,\x20\x22/dashboard\x22),\x20you\x20may\x20navigate\x20to\x20that\x20path\x20ONLY\x20under\x20the\x20base\x20URL\x20above\x0a-\x20The\x20base\x20URL\x20above\x20is\x20the\x20ONLY\x20authorized\x20profile\x20for\x20this\x20verification\x0a\x0a##\x20Instructions\x0a1.\x20Navigate\x20to\x20the\x20URL\x20above\x20using\x20browser_navigate\x0a2.\x20Take\x20a\x20snapshot\x20with\x20browser_snapshot\x20to\x20see\x20the\x20page\x0a3.\x20**IMMEDIATELY\x20check\x20for\x20blocking\x20HTTP\x20errors\x20before\x20proceeding**\x0a4.\x20Execute\x20the\x20task\x20step-by-step\x20using\x20browser\x20tools\x0a5.\x20**Take\x20screenshots\x20at\x20key\x20moments**\x20(see\x20Screenshot\x20Guidelines\x20below)\x0a6.\x20Document\x20any\x20issues\x20found\x20(bugs,\x20errors,\x20unexpected\x20behavior)\x0a7.\x20After\x20completing\x20the\x20verification,\x20evaluate\x20whether\x20the\x20result\x20adequately\x20verifies\x20the\x20scenario\x0a\x0a##\x20Screenshot\x20Guidelines\x20-\x20IMPORTANT\x0aTake\x20screenshots\x20throughout\x20the\x20verification\x20flow\x20so\x20a\x20human\x20can\x20review\x20it\x20for\x20completeness.\x20Screenshots\x20are\x20your\x20evidence\x20trail.\x0a\x0a**When\x20to\x20take\x20screenshots\x20(use\x20browser_take_screenshot):**\x0a-\x20After\x20initial\x20page\x20load\x20(capture\x20starting\x20state)\x0a-\x20Before\x20and\x20after\x20clicking\x20buttons\x20or\x20submitting\x20forms\x0a-\x20When\x20important\x20UI\x20elements\x20appear\x20(modals,\x20notifications,\x20loading\x20states)\x0a-\x20After\x20navigation\x20to\x20new\x20pages\x0a-\x20When\x20verifying\x20specific\x20elements\x20exist\x0a-\x20At\x20the\x20final\x20state\x20showing\x20the\x20completed\x20action\x0a\x0a**Screenshot\x20naming:**\x0a-\x20Use\x20descriptive\x20filenames:\x20\x2201_login-page-loaded.png\x22,\x20\x2202_form-filled.png\x22,\x20\x2203_dashboard-visible.png\x22\x0a-\x20Number\x20prefixes\x20(01_,\x2002_,\x20etc.)\x20help\x20maintain\x20chronological\x20order\x0a-\x20For\x20KEY\x20MOMENTS\x20that\x20prove\x20the\x20scenario\x20is\x20complete,\x20prefix\x20with\x20\x22key_\x22:\x20\x22key_04_success-message.png\x22,\x20\x22key_05_final-state.png\x22\x0a-\x20The\x20\x22key_\x22\x20prefix\x20marks\x20screenshots\x20as\x20high-priority\x20evidence\x20for\x20human\x20reviewers\x0a\x0a**Aim\x20for\x203-6\x20screenshots\x20per\x20verification**\x20to\x20document\x20the\x20complete\x20flow.\x20Mark\x201-2\x20of\x20the\x20most\x20important\x20ones\x20with\x20the\x20\x22key_\x22\x20prefix.\x0a\x0a##\x20Critical:\x20Early\x20Error\x20Detection\x0aAfter\x20step\x202\x20(taking\x20initial\x20snapshot),\x20IMMEDIATELY\x20check\x20for\x20blocking\x20HTTP\x20errors:\x0a\x0a**Blocking\x20errors\x20to\x20detect:**\x0a-\x20HTTP\x20404:\x20\x22404\x22,\x20\x22Not\x20Found\x22,\x20\x22Page\x20not\x20found\x22,\x20\x22does\x20not\x20exist\x22\x0a-\x20HTTP\x20500:\x20\x22500\x22,\x20\x22Internal\x20Server\x20Error\x22,\x20\x22Server\x20Error\x22,\x20\x22Something\x20went\x20wrong\x22\x0a-\x20HTTP\x20400:\x20\x22400\x22,\x20\x22Bad\x20Request\x22,\x20\x22Invalid\x20request\x22\x0a\x0a**Also\x20check\x20for:**\x0a-\x20Framework\x20error\x20pages\x20(Next.js\x20error\x20boundary,\x20React\x20error\x20page,\x20\x22Application\x20error\x22)\x0a-\x20Completely\x20blank/empty\x20pages\x20with\x20no\x20content\x0a-\x20\x22Cannot\x20GET\x20/path\x22\x20messages\x0a\x0a**If\x20ANY\x20blocking\x20error\x20is\x20detected:**\x0a1.\x20DO\x20NOT\x20continue\x20with\x20the\x20task\x0a2.\x20Return\x20IMMEDIATELY\x20with\x20evaluation:\x20\x22blocked\x22\x0a3.\x20Set\x20evaluationReason\x20to\x20describe\x20the\x20specific\x20error\x20(e.g.,\x20\x22HTTP\x20404\x20-\x20Page\x20not\x20found\x20at\x20/dashboard\x22)\x0a4.\x20Include\x20the\x20error\x20in\x20issues\x20array\x20with\x20severity:\x20\x22BLOCKER\x22\x20and\x20appropriate\x20type\x20(HTTP_404,\x20HTTP_500,\x20HTTP_400,\x20or\x20NAVIGATION_ERROR)\x0a\x0aThis\x20early\x20exit\x20prevents\x20wasting\x20time\x20on\x20tasks\x20that\x20cannot\x20succeed\x20due\x20to\x20fundamental\x20errors.\x0a\x0a##\x20Evaluation\x20Criteria\x0a-\x20VERIFIED:\x20The\x20task\x20completed\x20successfully\x20and\x20the\x20scenario\x20requirements\x20are\x20fully\x20met\x0a-\x20PARTIAL:\x20The\x20task\x20partially\x20completed\x20but\x20some\x20aspects\x20of\x20the\x20scenario\x20are\x20not\x20verified\x0a-\x20BLOCKED:\x20A\x20blocking\x20issue\x20(bug,\x20error,\x20missing\x20feature)\x20prevents\x20completion\x0a-\x20FAILED:\x20The\x20task\x20could\x20not\x20be\x20completed\x20due\x20to\x20errors\x0a\x0aReturn\x20your\x20findings\x20in\x20the\x20structured\x20output\x20format\x20with\x20your\x20evaluation.',']\x20\x22','blocked','gBcRO','find','trackPhaseEnd','already','iypHE','Verification\x20failed:\x20','SQLKD','kKOzv','DrQrN','OSppU','HSHGT','HTTP_400','qnQap','ebLfZ','MAJOR','TQnPh','Btujv','profile','gHDVV','PwVlf','input_tokens','ljDII','removeListener','trackCommandStart','IvbIi','\x22\x20—\x20it\x20is\x20concluded\x20(','startTime','errors','summary','isArray','terminalReason','element','oJOOC','No\x20API\x20token\x20configured.\x20Run:\x20ranger\x20setup\x20[token]','qvlKF','en-US','INCOMPLETE\x20-\x20Verification\x20found\x20issues','Mlcwd','DDwmO','mcp__ranger-browser__browser_type','qnDEL','repeat','browser_wait_for','videos','253EFPlKz','webm','GTgQK','OqkEW','KmZiF','traceViewerUrl','\x20comment(s)\x20to\x20verify','subtype','length','twoUm','503850MTuduS','debugAddressComments','\x0aNext\x20steps:','[browser]\x20Wait\x20→\x20','110540QpZuVp','zFSsF','VhjSD','toISOString','EIQiS','jmFhU','trackCommandError','type','trackPhaseError','notes','utf-8','run-mcp-server','includes','tool_name','No\x20baseUrl\x20configured\x20for\x20profile\x20\x22','message','fLJRg','\x22\x20into\x20\x22','upload_conversation','BLOCKING\x20ISSUE\x20DETECTED\x20-\x20Debug\x20Required','rIrFR','mkIUz','xsRFT','scenario_select','Screenshot\x20captured\x20during\x20verification','340205SjFBgJ','\x0aActive\x20feature\x20review:\x20','GSnwP','.\x20Feature\x20review\x20has\x20','configResult','pVKjr','asBjw','No\x20result\x20received\x20from\x20agent','NvHKc','FVbuk','toLowerCase','Grep','iBzJD','eCqfM','EGcwg','input','mcp__ranger-browser__browser_take_screenshot','\x0aTrace:\x20','vesrT','scenario_reset','stdin','verified','10242YVROFj','flush','authorName','\x20scenarios.','ouQHv','mtime','fromEntries','sessionId','seBqN','status','oSuWt','ByLhf','durationMs','All\x20scenario\x20requirements\x20were\x20met.','scenario','screenshot','mcp__ranger-browser__browser_navigate','text','list','partial','checklistItem','160547RtyANT','trackPhaseStart','NmJKX','WRlYg','log','hHohq','LZWkl','replace','20UmYlQD','[browser]\x20Click\x20→\x20\x22','DYLDv','stringify','featureId','trace-upload.zip','incomplete','upload_screenshot','isTTY','[browser]\x20Press\x20key\x20→\x20','debugOutcome','mrZkM','UadWm','vneVB','pipe','boolean','agent_execution','race','peayx','You\x20are\x20testing\x20browser\x20automation.\x20Your\x20task\x20is\x20simple:\x0a\x0a1.\x20Navigate\x20to\x20https://www.mozilla.org\x20using\x20browser_navigate\x0a2.\x20Take\x20a\x20snapshot\x20with\x20browser_snapshot\x20to\x20see\x20the\x20page\x0a3.\x20Take\x20a\x20screenshot\x20named\x20\x2201_mozilla-homepage.png\x22\x20using\x20browser_take_screenshot\x0a4.\x20Click\x20the\x20\x22Learn\x20More\x22\x20link\x20on\x20the\x20page\x0a5.\x20Take\x20a\x20snapshot\x20with\x20browser_snapshot\x20to\x20see\x20the\x20new\x20page\x0a6.\x20Take\x20a\x20screenshot\x20named\x20\x2202_learn-more.png\x22\x20using\x20browser_take_screenshot\x0a7.\x20Return\x20immediately\x20with\x20the\x20structured\x20output\x0a\x0aReturn\x20your\x20findings\x20in\x20the\x20structured\x20output\x20format.','split','\x0aReason:\x20','OTHER','completed','sLVxc','get','success','cache_read_input_tokens','is_interrupt','fKoCv','yMWFl','key','zip','lOSKr','stdout','add','interrupted','qscXK','hook_screenshot_upload','iPqOy','checklistItemId','evaluation','currentSessionId','Scenario\x20','\x0aVerification\x20interrupted.\x20Cleaning\x20up...','Mjkfa','UkdBx','upload_video','AuNkG','comment_addressing','feature_load','conversationDir','aeHJL','object','hyPXa','filter','ranger-browser-sessions','ResXE','kiDlI','CjQju','uploadedFiles','The\x20--scenario\x20flag\x20is\x20required\x20in\x20non-interactive\x20mode.\x20See\x20available\x20scenarios\x20above.','resultMeta','HTTP_404','CAFkO','lyiRZ','FShuq','browser_press_key','afjGZ','playwright_config','severity','browser_navigate','\x0a\x0a##\x20Reviewer\x20Feedback\x20to\x20Address\x0aThe\x20following\x20reviewer\x20comments\x20were\x20left\x20on\x20the\x20previous\x20version\x20of\x20this\x20item.\x0aVerify\x20that\x20each\x20concern\x20has\x20been\x20addressed\x20in\x20the\x20current\x20implementation.\x0aFor\x20each\x20comment\x20you\x20believe\x20has\x20been\x20addressed,\x20include\x20its\x20ID\x20(the\x20bracketed\x20value)\x20in\x20the\x20addressedCommentIds\x20array\x20in\x20your\x20output.\x0a\x0a','verification_in_progress','UI\x20elements\x20present\x20but\x20not\x20fully\x20functional','uoDty','\x20comments]','hook','21hIoBEq','rm\x20\x22','fjlZp','CaqAD','browser_type','\x20is\x20','MINOR','aOiMK','toolCallCounts','Browser\x20session\x20created:\x20','\x20comment(s)\x20as\x20addressed','startPath','issues','error','filename','mcp__ranger-browser__browser_click','Pwpmn','closed','DbuTZ','Unknown\x20error','BLOCKER','KsbFB','mcp__ranger-browser__','trim','RgMML','\x1b[1m','[DEBUG]\x20Mock\x20blocked\x20verification.','UaVJE','claude-opus-4-6','PutyX','unaddressedComments','actionable','setContext','sort','QtKum','duration_ms','\x20[not\x20actionable]','cHftm'];_0x4ba3=function(){return _0xa2eeb8;};return _0x4ba3();}async function selectChecklistItem(_0x16d4ed){const _0x28662b=_0x50bbd8,_0x2f5a1b={'gxjoq':'closed','EGcwg':function(_0x24d198,_0x55408d){return _0x24d198===_0x55408d;},'CrIse':function(_0x2db3f1,_0x283daa){return _0x2db3f1===_0x283daa;},'vesrT':'incomplete','izlPP':'not\x20actionable','uYGfC':function(_0x33d04a,_0x16a0b8){return _0x33d04a+_0x16a0b8;}};if(_0x16d4ed['length']===0x0)return null;const _0x444253=_0x16d4ed['map'](_0x1dd899=>{const _0x341795=_0x2c84,_0x2e6870=_0x1dd899[_0x341795(0x290)]===_0x2f5a1b['gxjoq']&&_0x2f5a1b[_0x341795(0x27f)](_0x1dd899['terminalReason'],'approved')?'✅':_0x1dd899['status']==='verified'?'🟢':_0x2f5a1b['CrIse'](_0x1dd899['status'],_0x2f5a1b[_0x341795(0x283)])?'🟠':_0x1dd899['status']===_0x341795(0x21d)?'🛑':_0x1dd899[_0x341795(0x290)]===_0x2f5a1b['gxjoq']?'⛔':_0x2f5a1b[_0x341795(0x27f)](_0x1dd899['status'],'verification_in_progress')?'⏳':'⬜',_0x2a4f67=_0x1dd899['unaddressedCommentCount']>0x0?'\x20['+_0x1dd899['unaddressedCommentCount']+'\x20comments]':'',_0x1d0c75=_0x1dd899[_0x341795(0x1c8)]?![]:_0x1dd899[_0x341795(0x290)]===_0x341795(0x1ba)&&_0x1dd899[_0x341795(0x23c)]?_0x1dd899[_0x341795(0x23c)]:_0x2f5a1b['izlPP'];return{'name':_0x2f5a1b['uYGfC'](_0x1dd899['displayIndex'],0x1)+'.\x20'+_0x2e6870+'\x20'+_0x1dd899['description']+_0x2a4f67,'value':_0x1dd899['id'],'disabled':_0x1d0c75};}),{selected:_0x4c42db}=await _0x55dd70['prompt']([{'type':_0x28662b(0x299),'name':'selected','message':'Which\x20scenario\x20does\x20this\x20verify?','choices':_0x444253}]);return _0x16d4ed[_0x28662b(0x21f)](_0x53acb7=>_0x53acb7['id']===_0x4c42db)||null;}async function handleIncompleteItem(_0x3c19e5,_0x341100,_0x2092e4){const _0x4aedc2=_0x50bbd8,_0x452794={'TQnPh':function(_0x21b091,_0x1cb322){return _0x21b091>_0x1cb322;}},{items:_0x2906e1}=await getActionItems(_0x3c19e5),_0x435b87=_0x2906e1[_0x4aedc2(0x192)](_0x1bf379=>_0x1bf379['id']!==_0x341100['id']),_0x2080cb=_0x435b87['every'](_0x49120f=>_0x49120f[_0x4aedc2(0x290)]===_0x4aedc2(0x286)||_0x49120f[_0x4aedc2(0x290)]==='blocked'||_0x49120f['status']===_0x4aedc2(0x1ba)||_0x49120f['status']===_0x4aedc2(0x2aa));console[_0x4aedc2(0x2a0)]('\x0a'+'='['repeat'](0x3c)),console[_0x4aedc2(0x2a0)](_0x4aedc2(0x242)),console['log'](''+'='[_0x4aedc2(0x247)](0x3c));if(_0x2092e4['issues']&&_0x452794['TQnPh'](_0x2092e4['issues'][_0x4aedc2(0x252)],0x0)){console['log']('\x0aIssues\x20found:');for(const _0x362fb7 of _0x2092e4[_0x4aedc2(0x1b5)]){console['log']('\x20\x20•\x20'+_0x362fb7['description']);}}else _0x2092e4[_0x4aedc2(0x1ee)]&&console['log'](_0x4aedc2(0x2b9)+_0x2092e4[_0x4aedc2(0x1ee)]);console['log'](_0x4aedc2(0x256)),console['log'](_0x4aedc2(0x1e2)),console['log']('\x20\x202.\x20Run\x20\x27ranger\x20go\x27\x20again\x20to\x20re-verify'),_0x2080cb&&_0x452794[_0x4aedc2(0x22d)](_0x435b87['length'],0x0)&&(console['log'](_0x4aedc2(0x204)),console[_0x4aedc2(0x2a0)]('If\x20you\x27re\x20done\x20for\x20now,\x20you\x20can\x20stop\x20and\x20resume\x20later\x20with\x20\x27ranger\x20resume\x27.')),console[_0x4aedc2(0x2a0)]('='['repeat'](0x3c)+'\x0a');}function createToolCallTrackingHook(_0x5ae45c){const _0x1f16f3=_0x50bbd8,_0x2608c1={'VhjSD':'PostToolUse','peayx':_0x1f16f3(0x1bf),'bRFPl':function(_0x18c816,_0x137da0){return _0x18c816+_0x137da0;},'EwJJP':_0x1f16f3(0x297),'QAdJY':_0x1f16f3(0x245)},_0x1cd6d7=new Map(),_0x350099=async _0xc8c255=>{const _0x30b8cd=_0x1f16f3;if(_0xc8c255[_0x30b8cd(0x1d4)]!==_0x2608c1[_0x30b8cd(0x25a)])return{};const _0x3b73ea=_0xc8c255,_0x12f9a6=_0x3b73ea['tool_input'],_0x43e813=_0x3b73ea['tool_name']['replace'](_0x2608c1[_0x30b8cd(0x2b6)],'');_0x1cd6d7['set'](_0x43e813,_0x2608c1['bRFPl'](_0x1cd6d7['get'](_0x43e813)||0x0,0x1)),_0x5ae45c[_0x30b8cd(0x29d)]('tool_call',{'toolName':_0x43e813}),_0x5ae45c[_0x30b8cd(0x220)]('tool_call',{'toolName':_0x43e813,'toolInput':summarizeToolInput(_0x43e813,_0x12f9a6)});switch(_0x3b73ea['tool_name']){case _0x2608c1[_0x30b8cd(0x1de)]:console['log']('[browser]\x20Navigate\x20→\x20'+_0x12f9a6['url']);break;case _0x30b8cd(0x1b8):console['log'](_0x30b8cd(0x2a5)+_0x12f9a6[_0x30b8cd(0x23d)]+'\x22');break;case _0x2608c1['QAdJY']:console['log']('[browser]\x20Type\x20→\x20\x22'+_0x12f9a6['text']+_0x30b8cd(0x269)+_0x12f9a6[_0x30b8cd(0x23d)]+'\x22');break;case'mcp__ranger-browser__browser_press_key':console['log'](_0x30b8cd(0x2ad)+_0x12f9a6['key']);break;case'mcp__ranger-browser__browser_wait_for':console['log'](_0x30b8cd(0x257)+(_0x12f9a6[_0x30b8cd(0x1f1)]?_0x12f9a6['time']+'ms':_0x12f9a6['text']||'condition'));break;}return{};};return{'hook':_0x350099,'toolCallCounts':_0x1cd6d7};}function createToolFailureHook(_0x472502){const _0x4477d6=_0x50bbd8,_0x17546d={'GTgQK':function(_0x39b0,_0x1045ec){return _0x39b0!==_0x1045ec;},'aeHJL':_0x4477d6(0x1e6)};return async _0x11be15=>{const _0x235200=_0x4477d6;if(_0x17546d[_0x235200(0x24c)](_0x11be15[_0x235200(0x1d4)],_0x17546d[_0x235200(0x18f)]))return{};const _0x442430=_0x11be15,_0x39bae4=_0x442430['tool_name']['replace'](_0x235200(0x1bf),'');return await _0x472502['trackPhaseError']('tool_failure',_0x442430[_0x235200(0x1b6)],{'toolName':_0x39bae4,'isInterrupt':_0x442430[_0x235200(0x2c0)]}),{};};}function summarizeToolInput(_0x1185cc,_0x2264b3){const _0x28324=_0x50bbd8,_0x4d3d40={'QtKum':'browser_click','tiYcE':_0x28324(0x1ad),'LZWkl':'browser_take_screenshot'};switch(_0x1185cc){case _0x28324(0x1a2):return{'url':_0x2264b3['url']};case _0x4d3d40[_0x28324(0x1cb)]:return{'element':_0x2264b3['element']};case _0x4d3d40['tiYcE']:return{'element':_0x2264b3['element']};case _0x4d3d40[_0x28324(0x2a2)]:return{'filename':_0x2264b3['filename']};case _0x28324(0x19e):return{'key':_0x2264b3[_0x28324(0x2c3)]};case _0x28324(0x248):return{'time':_0x2264b3[_0x28324(0x1f1)],'text':_0x2264b3[_0x28324(0x298)]};default:return{};}}function createScreenshotUploadHook(_0x3567ae,_0x2d7169,_0x18fe22,_0x1ea866){const _0x338a25=_0x50bbd8,_0x379a1d={'hHohq':_0x338a25(0x2ca),'PutyX':function(_0x558370,_0x56c1ab,_0x3a24ef){return _0x558370(_0x56c1ab,_0x3a24ef);},'ebLfZ':function(_0x46d817,_0xfeff94){return _0x46d817(_0xfeff94);},'QYhdN':'\x20(key\x20frame)','sGQro':function(_0x177b1e,_0x247b00,_0xe0fb9f){return _0x177b1e(_0x247b00,_0xe0fb9f);},'XeQZS':'Key\x20moment\x20captured\x20during\x20verification','ubMWk':_0x338a25(0x270),'hgjrn':function(_0x38fa74,_0x4d5bb4,_0x2cfd60,_0x49caf6){return _0x38fa74(_0x4d5bb4,_0x2cfd60,_0x49caf6);},'hyPXa':'screenshot','kHzRZ':_0x338a25(0x207)},_0xd22c68=new Set();let _0x20f465=0x1;const _0x88a21=async _0x2a63e4=>{const _0x2aba4b=_0x338a25;if(_0x2a63e4[_0x2aba4b(0x1d4)]!=='PostToolUse')return{};const _0x96f05b=_0x2a63e4;if(_0x96f05b[_0x2aba4b(0x265)]!==_0x2aba4b(0x281))return{};const _0x49af67=_0x96f05b['tool_input'],_0x1ee8b2=_0x49af67?.['filename'];if(!_0x1ee8b2)return{};const _0x150137=_0x1ee8b2[_0x2aba4b(0x27b)]()['startsWith']('key_');_0x1ea866[_0x2aba4b(0x29d)](_0x379a1d[_0x2aba4b(0x2a1)],{'filename':_0x1ee8b2,'isKeyFrame':_0x150137});try{const _0x23aaa8=_0x379a1d[_0x2aba4b(0x1c6)](join,_0x18fe22,_0x1ee8b2),_0x5ba5b2=await _0x379a1d[_0x2aba4b(0x22b)](readFile,_0x23aaa8),_0xefeb24=await stat(_0x23aaa8),_0x3fafe7=_0x1ee8b2['replace'](/\.png$/i,'')['replace'](/^key_/i,'')[_0x2aba4b(0x2a3)](/^\d+_/,'')[_0x2aba4b(0x2a3)](/-/g,'\x20'),_0x4f5652=_0x20f465++;console[_0x2aba4b(0x2a0)]('[hook]\x20Creating\x20step\x20#'+_0x4f5652+':\x20\x22'+_0x3fafe7+'\x22'+(_0x150137?_0x379a1d['QYhdN']:''));const {step:_0x3f3da3}=await _0x379a1d[_0x2aba4b(0x1db)](createVerificationStep,_0x3567ae,{'checklistItemId':_0x2d7169,'position':_0x4f5652,'stepType':_0x2aba4b(0x296),'stepName':_0x3fafe7,'description':_0x150137?_0x379a1d['XeQZS']:_0x379a1d['ubMWk'],'isKeyStep':_0x150137,'status':'success','metadata':{'filename':_0x1ee8b2,'timestamp':_0xefeb24[_0x2aba4b(0x28c)][_0x2aba4b(0x25b)]()}}),_0x471777=await _0x379a1d[_0x2aba4b(0x1eb)](createStepAsset,_0x3567ae,_0x3f3da3['id'],{'filename':_0x1ee8b2,'assetType':_0x379a1d[_0x2aba4b(0x191)],'timing':_0x379a1d['kHzRZ'],'position':0x0,'capturedAt':_0xefeb24[_0x2aba4b(0x28c)]['toISOString'](),'metadata':{'name':_0x3fafe7,'highPriority':_0x150137}});await _0x379a1d[_0x2aba4b(0x1c6)](uploadScreenshot,_0x471777[_0x2aba4b(0x210)],_0x5ba5b2),_0xd22c68[_0x2aba4b(0x2c7)](_0x1ee8b2),_0x1ea866['trackPhaseEnd'](_0x379a1d[_0x2aba4b(0x2a1)],{'filename':_0x1ee8b2,'bytes':_0x5ba5b2[_0x2aba4b(0x252)]});}catch(_0x5b8cf0){await _0x1ea866['trackPhaseError'](_0x379a1d['hHohq'],_0x5b8cf0,{'filename':_0x1ee8b2});}return{};};return{'hook':_0x88a21,'uploadedFiles':_0xd22c68};}async function runVerification(_0xee074d,_0x3ff43d){const _0x21f54a=_0x50bbd8,_0x57c7fc={'iPqOy':function(_0x3a483b,_0x3ade4f){return _0x3a483b!==_0x3ade4f;},'Mlcwd':_0x21f54a(0x1ba),'WRlYg':function(_0x1813c8,_0x31d86d){return _0x1813c8===_0x31d86d;},'lOSKr':'verified','Pwpmn':_0x21f54a(0x21d),'qscXK':function(_0x5b18bf,_0x2a12b9){return _0x5b18bf===_0x2a12b9;},'Mjkfa':function(_0x44c5e7,_0x38db86){return _0x44c5e7+_0x38db86;},'CAFkO':_0x21f54a(0x241),'HSHGT':'short','KsbFB':'numeric','PHBmL':'Agent\x20execution\x20timed\x20out\x20after\x2059\x20minutes','oJOOC':function(_0x205885,_0x25f037){return _0x205885+_0x25f037;},'deJcI':function(_0x194c01,_0x1e4fa6){return _0x194c01===_0x1e4fa6;},'asBjw':'tool_use','AuNkG':function(_0x14b601,_0x345ce7){return _0x14b601===_0x345ce7;},'FhBZZ':'StructuredOutput','zzyCW':function(_0x5144e6,_0x5b00ad){return _0x5144e6===_0x5b00ad;},'jFEdA':_0x21f54a(0x1e8),'ouQHv':'success','KmZiF':_0x21f54a(0x1bc),'kNZQm':function(_0x462a61){return _0x462a61();},'qnDEL':_0x21f54a(0x1f2),'TRzTc':function(_0x59a577){return _0x59a577();},'MdhXG':function(_0x4a8d35,_0x2f364f){return _0x4a8d35-_0x2f364f;},'lZAJB':function(_0x4cede3,_0x3c270b){return _0x4cede3<_0x3c270b;},'GvpyZ':function(_0x3f6d13,_0x5c2862){return _0x3f6d13>=_0x5c2862;},'sgVwT':function(_0x29b6ec,_0x4a8bbc){return _0x29b6ec===_0x4a8bbc;},'PwVlf':'not\x20actionable','rIrFR':'\x0aAvailable\x20scenarios\x20to\x20verify:','YPUMA':'\x0aUsage:\x20ranger\x20go\x20--scenario\x20<number>','CChVQ':_0x21f54a(0x1d6),'nfEcQ':_0x21f54a(0x198),'ResXE':'Selected\x20scenario\x20is\x20not\x20currently\x20actionable.\x20Choose\x20another\x20scenario.','jmFhU':'No\x20scenario\x20selected.\x20Create\x20scenarios\x20when\x20creating\x20the\x20feature\x20review\x20with\x20-c\x20or\x20--scenario\x20flags.','AByGy':'unknown\x20reason','kKOzv':_0x21f54a(0x1fc),'FVbuk':function(_0xb761ec,_0x25ab94){return _0xb761ec!==_0x25ab94;},'pVKjr':_0x21f54a(0x1fd),'mkIUz':function(_0x43f566,_0xa4e14f,_0x4bed91){return _0x43f566(_0xa4e14f,_0x4bed91);},'auFrF':function(_0x55991f,_0x418282){return _0x55991f instanceof _0x418282;},'BGXST':_0x21f54a(0x1a4),'YtIxs':'No\x20active\x20profile.\x20Run:\x20ranger\x20profile\x20use\x20<profile-name>','aOiMK':function(_0x524677,_0x466cbd){return _0x524677(_0x466cbd);},'RgMML':function(_0x3e791d,_0x3786a9){return _0x3e791d+_0x3786a9;},'SiREH':_0x21f54a(0x218),'qvlKF':function(_0x4ae0b6){return _0x4ae0b6();},'KiJKr':'failed','YtvDd':'session_error_update','DrQrN':'npx','fjlZp':_0x21f54a(0x211),'FrPTm':_0x21f54a(0x263),'IvbIi':'For\x20debug\x20purposes,\x20do\x20NOT\x20mark\x20any\x20comments\x20as\x20addressed.\x20Return\x20an\x20empty\x20addressedCommentIds\x20array.','ERxUi':function(_0x1cc5d7,_0x453cc4){return _0x1cc5d7+_0x453cc4;},'UaVJE':_0x21f54a(0x2b3),'fmWcW':_0x21f54a(0x1f8),'NaLPu':'object','vGYXi':_0x21f54a(0x1bd),'kiDlI':'HTTP_500','afjGZ':_0x21f54a(0x229),'DTCML':'severity','cHftm':_0x21f54a(0x1ee),'OSppU':function(_0x492dd1,_0x8478d5){return _0x492dd1(_0x8478d5);},'SiiOj':function(_0x247e91,_0x180a9f){return _0x247e91(_0x180a9f);},'rXXMI':_0x21f54a(0x1c5),'gsYue':'mcp__ranger-browser__*','mUEpr':'Glob','DDwmO':_0x21f54a(0x27c),'ZvSTI':function(_0x11016e,_0x4b34d6){return _0x11016e*_0x4b34d6;},'uoDty':function(_0x86a870,_0x14dd92){return _0x86a870(_0x14dd92);},'FuEUI':_0x21f54a(0x2b4)},_0x55fe97=!!_0xee074d[_0x21f54a(0x2ae)];_0x55fe97&&console['log']('\x0a[DEBUG\x20MODE]\x20Running\x20minimal\x20browser\x20test\x20with\x20outcome:\x20'+_0xee074d[_0x21f54a(0x2ae)]);_0x3ff43d[_0x21f54a(0x29d)](_0x21f54a(0x18d));const _0x59117d=await _0x57c7fc[_0x21f54a(0x1f9)](getActiveFeatureId);if(!_0x59117d)throw new Error(_0x57c7fc[_0x21f54a(0x246)]);const _0x5ab270=await getFeature(_0x59117d);_0x3ff43d[_0x21f54a(0x1c9)]({'featureId':_0x59117d});const _0x582851=_0x57c7fc['TRzTc'](getGitBranch);_0x582851&&_0x582851!==_0x5ab270['gitBranch']&&(await updateFeature(_0x59117d,{'gitBranch':_0x582851}),console['log']('\x20\x20\x20Updated\x20branch\x20to:\x20'+_0x582851));console['log'](_0x21f54a(0x272)+_0x5ab270['name']+'\x20('+_0x5ab270['dashboardUrl']+')');const {items:_0x158cdd}=await getActionItems(_0x59117d),_0x344fa7=new Map(_0x158cdd[_0x21f54a(0x209)](_0x50a235=>[_0x50a235['id'],_0x50a235])),_0x40719f=_0x5ab270['checklistItems'][_0x21f54a(0x209)]((_0xd40cc7,_0x72d9f0)=>{const _0x5391a7=_0x21f54a,_0x2ae33f=_0x344fa7[_0x5391a7(0x2bd)](_0xd40cc7['id']);return{..._0xd40cc7,'unaddressedCommentCount':_0x2ae33f?.[_0x5391a7(0x1e3)]??0x0,'displayIndex':_0x72d9f0,'actionable':!!_0x2ae33f&&_0x57c7fc[_0x5391a7(0x2cb)](_0xd40cc7['status'],_0x57c7fc['Mlcwd'])};});_0x3ff43d['trackPhaseEnd']('feature_load',{'itemCount':_0x158cdd['length']}),_0x3ff43d[_0x21f54a(0x29d)](_0x21f54a(0x26f));let _0x3d25b7=null,_0x16f55b=_0xee074d[_0x21f54a(0x261)];if(_0xee074d['scenario']!==undefined){const _0x3ed9d7=_0x57c7fc[_0x21f54a(0x208)](_0xee074d['scenario'],0x1);if(_0x57c7fc[_0x21f54a(0x1d5)](_0x3ed9d7,0x0)||_0x57c7fc['GvpyZ'](_0x3ed9d7,_0x40719f[_0x21f54a(0x252)]))throw new Error('Invalid\x20scenario\x20index:\x20'+_0xee074d[_0x21f54a(0x295)]+_0x21f54a(0x274)+_0x40719f[_0x21f54a(0x252)]+_0x21f54a(0x28a));const _0x441001=_0x40719f[_0x3ed9d7];if(!_0x441001['actionable']){const _0x75ce9f=_0x57c7fc['sgVwT'](_0x441001[_0x21f54a(0x290)],_0x57c7fc[_0x21f54a(0x243)])&&_0x441001[_0x21f54a(0x23c)]?_0x441001['terminalReason']:_0x57c7fc[_0x21f54a(0x231)];throw new Error(_0x21f54a(0x2cf)+_0xee074d['scenario']+_0x21f54a(0x1ae)+_0x75ce9f+_0x21f54a(0x1e7));}const _0x3eab9f=_0x344fa7[_0x21f54a(0x2bd)](_0x441001['id']);if(!_0x3eab9f)throw new Error('Scenario\x20'+_0xee074d[_0x21f54a(0x295)]+_0x21f54a(0x1fa));_0x3d25b7=_0x3eab9f,!_0x16f55b&&(_0x16f55b=_0x3d25b7[_0x21f54a(0x214)]);}else{const _0x5f39e0=process[_0x21f54a(0x285)][_0x21f54a(0x2ac)]&&process[_0x21f54a(0x2c6)][_0x21f54a(0x2ac)];if(!_0x5f39e0){console[_0x21f54a(0x2a0)]('\x0aNon-interactive\x20mode\x20detected.\x20The\x20--scenario\x20flag\x20is\x20required.'),console[_0x21f54a(0x2a0)](_0x57c7fc[_0x21f54a(0x26c)]),_0x40719f['forEach'](_0x30f899=>{const _0x4a2f21=_0x21f54a,_0x2c1710=_0x30f899['status']===_0x57c7fc['Mlcwd']&&_0x30f899[_0x4a2f21(0x23c)]==='approved'?'✅':_0x57c7fc[_0x4a2f21(0x29f)](_0x30f899['status'],_0x57c7fc[_0x4a2f21(0x2c5)])?'🟢':_0x57c7fc['WRlYg'](_0x30f899['status'],_0x4a2f21(0x2aa))?'🟠':_0x57c7fc[_0x4a2f21(0x29f)](_0x30f899[_0x4a2f21(0x290)],_0x57c7fc[_0x4a2f21(0x1b9)])?'🛑':_0x57c7fc['WRlYg'](_0x30f899[_0x4a2f21(0x290)],_0x4a2f21(0x1ba))?'⛔':_0x57c7fc[_0x4a2f21(0x2c9)](_0x30f899[_0x4a2f21(0x290)],_0x4a2f21(0x1a4))?'⏳':'⬜',_0x2d6b4b=_0x30f899['unaddressedCommentCount']>0x0?'\x20['+_0x30f899[_0x4a2f21(0x1e3)]+_0x4a2f21(0x1a7):'',_0x112a04=_0x30f899['actionable']?'':_0x4a2f21(0x1cd);console[_0x4a2f21(0x2a0)]('\x20\x20'+_0x57c7fc[_0x4a2f21(0x188)](_0x30f899['displayIndex'],0x1)+'.\x20'+_0x2c1710+'\x20'+_0x30f899['description']+_0x2d6b4b+_0x112a04);}),console[_0x21f54a(0x2a0)](_0x57c7fc[_0x21f54a(0x212)]),console[_0x21f54a(0x2a0)](_0x57c7fc['CChVQ']);throw new Error(_0x57c7fc['nfEcQ']);}const _0xe2a281=await selectChecklistItem(_0x40719f);if(_0xe2a281){const _0x173951=_0x344fa7[_0x21f54a(0x2bd)](_0xe2a281['id']);if(!_0x173951)throw new Error(_0x57c7fc[_0x21f54a(0x194)]);_0x3d25b7=_0x173951,!_0x16f55b&&(_0x16f55b=_0x3d25b7['description']);}}if(!_0x3d25b7)throw new Error(_0x57c7fc[_0x21f54a(0x25d)]);if(_0x3d25b7[_0x21f54a(0x290)]===_0x57c7fc[_0x21f54a(0x243)])throw new Error('Cannot\x20verify\x20scenario\x20\x22'+_0x3d25b7['description']+_0x21f54a(0x237)+(_0x3d25b7['terminalReason']||_0x57c7fc['AByGy'])+').');if(!_0x16f55b)throw new Error(_0x57c7fc[_0x21f54a(0x225)]);_0x3ff43d['setContext']({'checklistItemId':_0x3d25b7['id']}),_0x3ff43d[_0x21f54a(0x220)]('scenario_select',{'selectionMethod':_0x57c7fc[_0x21f54a(0x27a)](_0xee074d[_0x21f54a(0x295)],undefined)?_0x57c7fc[_0x21f54a(0x276)]:'interactive','itemStatus':_0x3d25b7[_0x21f54a(0x290)]}),console['log'](_0x21f54a(0x1ef)+_0x3d25b7['description']),console['log']('Notes:\x20'+_0x16f55b);let _0x46a055=null;if(_0x3d25b7['parentItemId']||_0x3d25b7[_0x21f54a(0x1e3)]>0x0)try{_0x46a055=await getItemFeedback(_0x59117d,_0x3d25b7['id']),_0x46a055[_0x21f54a(0x1c7)][_0x21f54a(0x252)]>0x0&&console['log']('Reviewer\x20feedback:\x20'+_0x46a055['unaddressedComments']['length']+_0x21f54a(0x250));}catch(_0xfecbf){await _0x3ff43d['trackPhaseError']('feedback_fetch',_0xfecbf);}if(_0x5ab270['currentSession']&&_0x57c7fc[_0x21f54a(0x18b)](_0x5ab270['currentSession']['status'],_0x21f54a(0x20b))&&_0x5ab270[_0x21f54a(0x2ce)])try{await _0x57c7fc[_0x21f54a(0x26d)](startSession,_0x59117d,_0x5ab270['currentSessionId']);}catch(_0x3f99a2){const _0x2670e9=_0x57c7fc['auFrF'](_0x3f99a2,Error)?_0x3f99a2[_0x21f54a(0x267)]:String(_0x3f99a2);if(!_0x2670e9[_0x21f54a(0x264)](_0x21f54a(0x221)))throw _0x3f99a2;}await updateChecklistItem(_0x59117d,_0x3d25b7['id'],{'status':_0x57c7fc['BGXST']}),_0x3ff43d[_0x21f54a(0x29d)](_0x21f54a(0x1e9));let _0x6b56da=null;_0xee074d['profile']?_0x6b56da=_0xee074d[_0x21f54a(0x22f)]:_0x6b56da=await readActiveProfileName();if(!_0x6b56da)throw new Error(_0x57c7fc['YtIxs']);const _0x2e03d4=_0x57c7fc[_0x21f54a(0x1b0)](getEnvDir,_0x6b56da);if(!_0x57c7fc['aOiMK'](existsSync,_0x2e03d4))throw new Error('Profile\x20\x22'+_0x6b56da+'\x22\x20not\x20found.\x20Run:\x20ranger\x20profile\x20add\x20'+_0x6b56da);const _0x5e3a43=await _0x57c7fc['aOiMK'](loadSettings,_0x6b56da),_0x4d463d=_0x57c7fc['aOiMK'](resolveEnvVars,_0x5e3a43);let _0x269be1=_0x4d463d['baseUrl'];if(!_0x269be1)throw new Error(_0x21f54a(0x266)+_0x6b56da+_0x21f54a(0x1fb)+_0x6b56da+'\x20baseUrl\x20<url>');if(_0xee074d[_0x21f54a(0x1b4)]){const _0x5dc8d7=_0x269be1[_0x21f54a(0x1f7)]('/')?_0x269be1['slice'](0x0,-0x1):_0x269be1,_0x44bd24=_0xee074d['startPath']['startsWith']('/')?_0xee074d['startPath']:_0x57c7fc['Mjkfa']('/',_0xee074d['startPath']);_0x269be1=_0x57c7fc[_0x21f54a(0x1c1)](_0x5dc8d7,_0x44bd24);}_0x3ff43d['trackPhaseEnd']('profile_resolution',{'profileName':_0x6b56da}),_0x3ff43d[_0x21f54a(0x29d)](_0x57c7fc['SiREH']);const _0x4b65fb=await _0x57c7fc['qvlKF'](getToken);if(!_0x4b65fb)throw new Error(_0x21f54a(0x23f));const _0x51b499=await createBrowserSession({'environmentName':_0x6b56da,'settings':_0x4d463d,'task':_0x16f55b,'url':_0x269be1,'featureId':_0x59117d,'checklistItemId':_0x3d25b7['id']});console['log'](_0x21f54a(0x1b2)+_0x51b499['id']),_0x3ff43d['setContext']({'browserSessionId':_0x51b499['id']}),await updateChecklistItem(_0x59117d,_0x3d25b7['id'],{'browserSessionId':_0x51b499['id']}),_0x3ff43d[_0x21f54a(0x220)](_0x21f54a(0x218)),_0x3ff43d['trackPhaseStart'](_0x21f54a(0x1a0));let _0x3506a9,_0x4ce518;try{_0x4ce518=await _0x57c7fc[_0x21f54a(0x240)](getProxySessionToken);}catch(_0x38bfac){const _0xae5884=_0x38bfac instanceof Error?_0x38bfac[_0x21f54a(0x267)]:_0x57c7fc[_0x21f54a(0x1b0)](String,_0x38bfac),_0x343a30='Failed\x20to\x20fetch\x20proxy\x20session\x20token:\x20'+_0xae5884;try{await updateBrowserSession(_0x51b499['id'],{'status':_0x57c7fc[_0x21f54a(0x219)],'durationMs':0x0,'errorMessage':_0x343a30});}catch(_0x490556){await _0x3ff43d[_0x21f54a(0x260)](_0x57c7fc['YtvDd'],_0x490556);}throw new Error(_0x343a30);}_0x3506a9=await buildPlaywrightConfig(_0x4d463d,_0x6b56da,_0x51b499?.['id']),_0x3ff43d['trackPhaseEnd']('playwright_config');const _0x32edc1=Date[_0x21f54a(0x20d)](),_0x115a1f={'command':_0x57c7fc[_0x21f54a(0x226)],'args':[_0x57c7fc[_0x21f54a(0x1ab)],_0x57c7fc[_0x21f54a(0x1f5)],'--config',_0x3506a9['configPath']]};let _0x5ce387;if(_0x55fe97){let _0x553d7c='';if(_0x46a055&&_0x46a055[_0x21f54a(0x1c7)][_0x21f54a(0x252)]>0x0){const _0xbe71ef=_0x46a055[_0x21f54a(0x1c7)][_0x21f54a(0x209)](_0x48ee88=>'-\x20['+_0x48ee88['id']+_0x21f54a(0x21c)+_0x48ee88[_0x21f54a(0x1f3)]+'\x22')[_0x21f54a(0x1d0)]('\x0a'),_0x5ce3ed=_0xee074d['debugAddressComments']?_0x21f54a(0x200):_0x57c7fc[_0x21f54a(0x236)];_0x553d7c='\x0a\x0a##\x20Debug:\x20Reviewer\x20Comments\x0a'+_0x5ce3ed+'\x0a\x0a'+_0xbe71ef;}_0x5ce387=_0x57c7fc['ERxUi'](getDebugPrompt(),_0x553d7c);}else{const _0x61ff52=_0x3d25b7['notes']?'\x0a\x0a##\x20Additional\x20Notes\x0a'+_0x3d25b7['notes']:'';let _0x1f0ea4='';if(_0x46a055&&_0x46a055['unaddressedComments']['length']>0x0){const _0x4f2fe2=_0x46a055['unaddressedComments']['map'](_0x240cbd=>{const _0x16d059=_0x21f54a,_0x2094ce=new Date(_0x240cbd[_0x16d059(0x1d2)])['toLocaleDateString'](_0x57c7fc[_0x16d059(0x19b)],{'month':_0x57c7fc[_0x16d059(0x228)],'day':_0x57c7fc[_0x16d059(0x1be)]}),_0x2a4327=_0x240cbd[_0x16d059(0x289)]||_0x240cbd['authorEmail']||'Reviewer';return'-\x20['+_0x240cbd['id']+_0x16d059(0x1e1)+_0x2a4327+'**\x20('+_0x2094ce+'):\x20\x22'+_0x240cbd[_0x16d059(0x1f3)]+'\x22';})['join']('\x0a');_0x1f0ea4=_0x21f54a(0x1a3)+_0x4f2fe2;}let _0x20135b='';_0x46a055?.['canonicalFlow']&&(_0x20135b='\x0a\x0a##\x20Expected\x20Flow\x20(from\x20previous\x20verification)\x0a'+_0x46a055['canonicalFlow']),_0x5ce387='You\x20are\x20a\x20Feature\x20Review\x20Verifier.\x20Your\x20job\x20is\x20to\x20verify\x20a\x20scenario\x20by\x20executing\x20a\x20UI\x20flow\x20and\x20evaluating\x20whether\x20it\x20adequately\x20completes\x20the\x20scenario.\x0a\x0a##\x20Scenario\x20to\x20Verify\x0a'+_0x3d25b7['description']+_0x61ff52+_0x1f0ea4+_0x20135b+'\x0a\x0a##\x20Task\x20to\x20Execute\x0a'+_0x16f55b+_0x21f54a(0x1d3)+_0x269be1+_0x21f54a(0x21b);}const _0x550be1={'type':_0x21f54a(0x190),'properties':{'success':{'type':_0x57c7fc[_0x21f54a(0x1c4)]},'summary':{'type':_0x57c7fc[_0x21f54a(0x213)]},'evaluation':{'type':_0x21f54a(0x1f8),'enum':[_0x57c7fc['lOSKr'],_0x21f54a(0x29a),_0x57c7fc[_0x21f54a(0x1b9)],_0x57c7fc[_0x21f54a(0x219)]]},'evaluationReason':{'type':_0x57c7fc['fmWcW']},'issues':{'type':_0x21f54a(0x202),'items':{'type':_0x57c7fc['NaLPu'],'properties':{'severity':{'type':_0x57c7fc[_0x21f54a(0x213)],'enum':[_0x57c7fc['vGYXi'],'MAJOR','MINOR']},'type':{'type':_0x57c7fc['fmWcW'],'enum':[_0x21f54a(0x19a),_0x57c7fc[_0x21f54a(0x195)],_0x57c7fc[_0x21f54a(0x19f)],'NAVIGATION_ERROR','OTHER']},'description':{'type':_0x57c7fc[_0x21f54a(0x213)]},'screenshot':{'type':_0x21f54a(0x1f8)}},'required':[_0x57c7fc['DTCML'],_0x21f54a(0x214)]}},'addressedCommentIds':{'type':_0x21f54a(0x202),'description':'IDs\x20of\x20reviewer\x20comments\x20that\x20have\x20been\x20addressed\x20in\x20the\x20current\x20implementation','items':{'type':_0x21f54a(0x1f8)}}},'required':['success','summary',_0x21f54a(0x2cd),_0x57c7fc[_0x21f54a(0x1ce)]]};_0x3ff43d[_0x21f54a(0x29d)](_0x21f54a(0x2b4));const _0x2bb163=getTraceDirectory(_0x51b499['id']),_0x10dc87=createScreenshotUploadHook(_0x51b499['id'],_0x3d25b7['id'],_0x2bb163,_0x3ff43d),_0x58b2a6=_0x57c7fc[_0x21f54a(0x227)](createToolCallTrackingHook,_0x3ff43d),_0x38a403=_0x57c7fc['OSppU'](createToolFailureHook,_0x3ff43d),_0x2aa47d=_0x57c7fc['SiiOj'](query,{'prompt':_0x5ce387,'options':{'cwd':process['cwd'](),'model':_0x57c7fc['rXXMI'],'mcpServers':{'ranger-browser':_0x115a1f},'tools':[_0x21f54a(0x1f4)],'permissionMode':'acceptEdits','allowedTools':[_0x57c7fc['gsYue'],'Read',_0x57c7fc[_0x21f54a(0x217)],_0x57c7fc[_0x21f54a(0x244)]],'outputFormat':{'type':'json_schema','schema':_0x550be1},'hooks':{'PostToolUse':[{'hooks':[_0x58b2a6[_0x21f54a(0x1a8)],_0x10dc87['hook']]}],'PostToolUseFailure':[{'hooks':[_0x38a403]}]},'env':{...process[_0x21f54a(0x1df)],'ANTHROPIC_API_KEY':_0x4ce518,'ANTHROPIC_BASE_URL':_0x57c7fc[_0x21f54a(0x240)](getAiProxyUrl)},'persistSession':![]}});let _0x5ecb26=null,_0x1a19ac=null,_0x3b94c7=null,_0x3fd20e={};const _0x576ab9=getConversationFilePath(_0x51b499['id']),_0x18902d=_0x57c7fc[_0x21f54a(0x227)](dirname,_0x576ab9);await mkdir(_0x18902d,{'recursive':!![]});const _0x5ce5fa=_0x57c7fc['ZvSTI'](0x3b*0x3c,0x3e8),_0x509af7=new Promise((_0x35778a,_0x2834e2)=>{const _0x42ed36={'xKpMy':function(_0x11cdbf,_0x3e5a37){return _0x11cdbf(_0x3e5a37);},'ByLhf':_0x57c7fc['PHBmL']};setTimeout(()=>{const _0x3bbcbc=_0x2c84;_0x42ed36['xKpMy'](_0x2834e2,new Error(_0x42ed36[_0x3bbcbc(0x292)]));},_0x5ce5fa);});try{await Promise[_0x21f54a(0x2b5)]([((async()=>{const _0x51a562=_0x21f54a;for await(const _0x2069a9 of _0x2aa47d){try{const _0x333096=_0x57c7fc[_0x51a562(0x23e)](JSON[_0x51a562(0x2a7)](_0x2069a9),'\x0a');await appendFile(_0x576ab9,_0x333096,_0x51a562(0x262));}catch{}const _0x3b8626=_0x2069a9;if(_0x57c7fc[_0x51a562(0x1ec)](_0x3b8626['type'],'assistant')&&_0x3b8626[_0x51a562(0x267)]?.[_0x51a562(0x1f3)])for(const _0x57a080 of _0x3b8626[_0x51a562(0x267)]['content']){_0x57a080[_0x51a562(0x25f)]===_0x57c7fc[_0x51a562(0x277)]&&_0x57c7fc[_0x51a562(0x18b)](_0x57a080['name'],_0x57c7fc['FhBZZ'])&&_0x57a080['input']&&(_0x3b94c7=_0x57a080[_0x51a562(0x280)]);}if(_0x3b8626['error']){let _0x56853c=_0x3b8626[_0x51a562(0x1b6)];if(_0x3b8626[_0x51a562(0x267)]?.[_0x51a562(0x1f3)]&&Array[_0x51a562(0x23b)](_0x3b8626[_0x51a562(0x267)]['content'])){const _0x4f9d42=_0x3b8626[_0x51a562(0x267)][_0x51a562(0x1f3)][_0x51a562(0x192)](_0x54270c=>_0x54270c['type']===_0x51a562(0x298))[_0x51a562(0x209)](_0x2e203c=>_0x2e203c['text']||'')['filter'](Boolean);_0x4f9d42[_0x51a562(0x252)]>0x0&&(_0x56853c=_0x4f9d42['join']('\x20'));}_0x1a19ac=_0x56853c;}if(_0x57c7fc[_0x51a562(0x20c)](_0x3b8626['type'],_0x57c7fc['jFEdA'])){_0x3fd20e={'numTurns':_0x3b8626[_0x51a562(0x1d9)],'totalCostUsd':_0x3b8626['total_cost_usd'],'durationApiMs':_0x3b8626['duration_api_ms'],'sdkDurationMs':_0x3b8626[_0x51a562(0x1cc)],'inputTokens':_0x3b8626['usage']?.[_0x51a562(0x232)],'outputTokens':_0x3b8626['usage']?.['output_tokens'],'cacheReadTokens':_0x3b8626['usage']?.[_0x51a562(0x2bf)],'cacheCreationTokens':_0x3b8626['usage']?.['cache_creation_input_tokens']};if(_0x3b8626[_0x51a562(0x251)]===_0x57c7fc[_0x51a562(0x28b)]&&_0x2069a9[_0x51a562(0x203)])_0x5ecb26=_0x2069a9['structured_output'];else{if(_0x3b8626['subtype']!=='success'){if(_0x3b94c7&&_0x57c7fc['WRlYg'](_0x3b8626[_0x51a562(0x239)]?.[_0x51a562(0x252)],0x0))_0x5ecb26=_0x3b94c7,_0x1a19ac=null;else!_0x1a19ac&&(_0x1a19ac=_0x3b8626[_0x51a562(0x239)]?.[_0x51a562(0x1d0)](',\x20')||_0x57c7fc[_0x51a562(0x24e)]);}}}}})()),_0x509af7]);}catch(_0x53e0a0){_0x1a19ac=_0x53e0a0 instanceof Error?_0x53e0a0[_0x21f54a(0x267)]:_0x57c7fc[_0x21f54a(0x1a6)](String,_0x53e0a0);}const _0x547fa5=_0x57c7fc[_0x21f54a(0x208)](Date[_0x21f54a(0x20d)](),_0x32edc1);return _0x3ff43d[_0x21f54a(0x220)](_0x57c7fc['FuEUI'],{..._0x3fd20e,'toolCallCounts':Object[_0x21f54a(0x28d)](_0x58b2a6[_0x21f54a(0x1b1)]),'hasResult':!!_0x5ecb26,'hasError':!!_0x1a19ac}),{'featureId':_0x59117d,'checklistItem':_0x3d25b7,'browserSession':_0x51b499,'finalResult':_0x5ecb26,'agentError':_0x1a19ac,'lastStructuredOutputInput':_0x3b94c7,'screenshotHook':_0x10dc87,'toolCallCounts':_0x58b2a6['toolCallCounts'],'configResult':_0x3506a9,'startTime':_0x32edc1,'durationMs':_0x547fa5,'conversationFilePath':_0x576ab9,'conversationDir':_0x18902d,'isDebugMode':_0x55fe97,'debugOutcome':_0xee074d['debugOutcome'],'debugAddressComments':_0xee074d[_0x21f54a(0x255)],'resultMeta':_0x3fd20e,'telemetry':_0x3ff43d,'feedbackCommentIds':_0x46a055?_0x46a055[_0x21f54a(0x1c7)][_0x21f54a(0x209)](_0x1f898a=>_0x1f898a['id']):[]};}async function processVerificationResult(_0x1bdd76){const _0x5e947e=_0x50bbd8,_0x2c8f28={'BGbiZ':function(_0x493c28,_0xe49393){return _0x493c28(_0xe49393);},'YYAvU':_0x5e947e(0x20f),'Jceoo':_0x5e947e(0x2c4),'LLCaF':function(_0x3075ee,_0x5d90b5){return _0x3075ee(_0x5d90b5);},'yMWFl':'upload_trace','rEaCU':function(_0x50c491,_0x2ee8e2){return _0x50c491(_0x2ee8e2);},'DYLDv':function(_0x1e696a,_0x19c222){return _0x1e696a+_0x19c222;},'dJcxm':function(_0x14e8e8,_0x5c3aa8){return _0x14e8e8<_0x5c3aa8;},'rYbFZ':'screenshot','fKoCv':'Key\x20moment\x20captured\x20during\x20verification','OqkEW':'Screenshot\x20captured\x20during\x20verification','ocYYa':function(_0x429ec5,_0x2fbda3,_0x5a26a7,_0x509d69){return _0x429ec5(_0x2fbda3,_0x5a26a7,_0x509d69);},'sLVxc':'after','ljDII':function(_0x1a45c1,_0x542127,_0x9ab4b7){return _0x1a45c1(_0x542127,_0x9ab4b7);},'zFSsF':_0x5e947e(0x2ab),'FShuq':_0x5e947e(0x26a),'xsRFT':function(_0x3b5642,_0x4eeb4b,_0x38b2a4,_0x46ad64){return _0x3b5642(_0x4eeb4b,_0x38b2a4,_0x46ad64);},'GSnwP':'conversation.jsonl','eahQX':'jsonl','twoUm':function(_0x5b2a83,_0x165a19){return _0x5b2a83(_0x165a19);},'NvHKc':_0x5e947e(0x1d8),'ncRzO':_0x5e947e(0x206),'SQLKD':_0x5e947e(0x2bb),'seBqN':function(_0x4aca38,_0x27fb3b){return _0x4aca38||_0x27fb3b;},'RaAck':_0x5e947e(0x1f0),'GiDzG':function(_0x1ac567,_0x202bd5){return _0x1ac567(_0x202bd5);},'Nspba':function(_0x40f229,_0x3ab597){return _0x40f229&&_0x3ab597;},'NmJKX':function(_0x34f3d0,_0x5572e8){return _0x34f3d0(_0x5572e8);},'APxkD':'evaluation','UkdBx':_0x5e947e(0x1e0),'lyiRZ':'blocked','jqmPF':function(_0xceddf7,_0x2f5256){return _0xceddf7===_0x2f5256;},'CaqAD':_0x5e947e(0x29a),'vneVB':_0x5e947e(0x2aa),'NkYsJ':function(_0x109b57,_0x4ad3c2){return _0x109b57>_0x4ad3c2;},'CteRB':'comment_addressing'},{featureId:_0x7fee02,checklistItem:_0x539a71,browserSession:_0x5097b8,screenshotHook:_0x8da2d3,durationMs:_0x35c54a,conversationFilePath:_0x345d12,isDebugMode:_0x21f2e3,debugOutcome:_0x81ec8e,debugAddressComments:_0x17da66,telemetry:_0x2c226b,feedbackCommentIds:_0x45b812}=_0x1bdd76,{finalResult:_0x281583,agentError:_0x28e70d}=_0x1bdd76;let _0x4ed322;try{const _0x29b04a=_0x2c8f28[_0x5e947e(0x21a)](getTraceDirectory,_0x5097b8['id']);if(existsSync(_0x29b04a)){const _0x485c24=await readdir(_0x29b04a);if(_0x485c24['length']>0x0){_0x2c226b['trackPhaseStart']('upload_trace');try{const _0x308cc2=await getUploadUrls(_0x5097b8['id'],_0x2c8f28[_0x5e947e(0x205)],_0x2c8f28['Jceoo']),_0x4d7543=await _0x2c8f28[_0x5e947e(0x1ed)](zipDirectory,_0x29b04a);await uploadTrace(_0x308cc2[_0x5e947e(0x210)],_0x4d7543),_0x4ed322=_0x308cc2['downloadUrl'],_0x2c226b[_0x5e947e(0x220)](_0x2c8f28[_0x5e947e(0x2c2)],{'bytes':_0x4d7543[_0x5e947e(0x252)]});}catch(_0x9f4725){await _0x2c226b['trackPhaseError'](_0x2c8f28[_0x5e947e(0x2c2)],_0x9f4725);}const _0xef9313=await _0x2c8f28['rEaCU'](loadSessionVideos,_0x29b04a);for(const _0x448e4b of _0xef9313){_0x2c226b['trackPhaseStart']('upload_video',{'filename':_0x448e4b['filename']});try{const _0x22370e=await _0x2c8f28['BGbiZ'](readFile,_0x448e4b['path']),_0x3025dd=await getUploadUrls(_0x5097b8['id'],_0x448e4b[_0x5e947e(0x1b7)],_0x5e947e(0x24b));await uploadVideo(_0x3025dd[_0x5e947e(0x210)],_0x22370e),_0x2c226b[_0x5e947e(0x220)]('upload_video',{'filename':_0x448e4b['filename'],'bytes':_0x22370e['length']});}catch(_0x3b2cb9){await _0x2c226b[_0x5e947e(0x260)](_0x5e947e(0x18a),_0x3b2cb9,{'filename':_0x448e4b[_0x5e947e(0x1b7)]});}}const _0xc2638f=_0x485c24[_0x5e947e(0x192)](_0x11fb38=>_0x11fb38[_0x5e947e(0x27b)]()['endsWith']('.png'))['filter'](_0xa8b65b=>!_0x8da2d3['uploadedFiles']['has'](_0xa8b65b))[_0x5e947e(0x1ca)](),_0x4482aa=_0x2c8f28[_0x5e947e(0x2a6)](_0x8da2d3[_0x5e947e(0x197)]['size'],0x1);for(let _0x5032f0=0x0;_0x2c8f28['dJcxm'](_0x5032f0,_0xc2638f['length']);_0x5032f0++){const _0x535db1=_0xc2638f[_0x5032f0],_0x378872=_0x535db1['toLowerCase']()['startsWith']('key_');_0x2c226b['trackPhaseStart']('upload_screenshot',{'filename':_0x535db1,'isKeyFrame':_0x378872});try{const _0x2dbd75=join(_0x29b04a,_0x535db1),_0x15e4e6=await readFile(_0x2dbd75),_0x45546e=await stat(_0x2dbd75),_0x5c543a=_0x535db1[_0x5e947e(0x2a3)](/\.png$/i,'')[_0x5e947e(0x2a3)](/^key_/i,'')[_0x5e947e(0x2a3)](/^\d+_/,'')['replace'](/-/g,'\x20'),{step:_0x4c9dbe}=await createVerificationStep(_0x5097b8['id'],{'checklistItemId':_0x539a71['id'],'position':_0x4482aa+_0x5032f0,'stepType':_0x2c8f28['rYbFZ'],'stepName':_0x5c543a,'description':_0x378872?_0x2c8f28[_0x5e947e(0x2c1)]:_0x2c8f28[_0x5e947e(0x24d)],'isKeyStep':_0x378872,'status':'success','metadata':{'filename':_0x535db1,'timestamp':_0x45546e['mtime'][_0x5e947e(0x25b)]()}}),_0xcd4039=await _0x2c8f28['ocYYa'](createStepAsset,_0x5097b8['id'],_0x4c9dbe['id'],{'filename':_0x535db1,'assetType':_0x5e947e(0x296),'timing':_0x2c8f28[_0x5e947e(0x2bc)],'position':0x0,'capturedAt':_0x45546e['mtime']['toISOString'](),'metadata':{'name':_0x5c543a,'highPriority':_0x378872}});await _0x2c8f28[_0x5e947e(0x233)](uploadScreenshot,_0xcd4039['uploadUrl'],_0x15e4e6),_0x2c226b[_0x5e947e(0x220)](_0x2c8f28[_0x5e947e(0x259)],{'filename':_0x535db1,'bytes':_0x15e4e6[_0x5e947e(0x252)]});}catch(_0x5422dd){await _0x2c226b[_0x5e947e(0x260)](_0x5e947e(0x2ab),_0x5422dd,{'filename':_0x535db1});}}}}if(existsSync(_0x345d12)){_0x2c226b['trackPhaseStart'](_0x2c8f28['FShuq']);try{const _0x63d549=await _0x2c8f28[_0x5e947e(0x26e)](getUploadUrls,_0x5097b8['id'],_0x2c8f28[_0x5e947e(0x273)],_0x2c8f28['eahQX']),_0x38bd0e=await _0x2c8f28[_0x5e947e(0x253)](readFile,_0x345d12);await uploadConversation(_0x63d549['uploadUrl'],_0x38bd0e),_0x2c226b[_0x5e947e(0x220)](_0x2c8f28['FShuq'],{'bytes':_0x38bd0e[_0x5e947e(0x252)]});}catch(_0x2ea142){await _0x2c226b['trackPhaseError'](_0x2c8f28[_0x5e947e(0x19d)],_0x2ea142);}}_0x2c226b[_0x5e947e(0x29d)](_0x2c8f28['NvHKc']);try{const _0x2ed544=_0x281583,_0x5e40a5={'status':_0x28e70d?_0x2c8f28['ncRzO']:_0x2c8f28[_0x5e947e(0x224)],'durationMs':_0x35c54a,'agentResponse':_0x2ed544?.[_0x5e947e(0x23a)]||_0x28e70d||undefined,'errorMessage':_0x2c8f28[_0x5e947e(0x28f)](_0x28e70d,undefined)};await updateBrowserSession(_0x5097b8['id'],_0x5e40a5);if(_0x2ed544&&_0x4ed322){const _0x22a3e7=_0x2c8f28['RaAck'][_0x5e947e(0x2b8)]('|');let _0x1a8128=0x0;while(!![]){switch(_0x22a3e7[_0x1a8128++]){case'0':_0x2ed544['sessionDir']=_0x2c8f28['GiDzG'](getTraceDirectory,_0x5097b8['id']);continue;case'1':_0x2ed544[_0x5e947e(0x2cc)]=_0x539a71['id'];continue;case'2':_0x2ed544['traceViewerUrl']=buildTraceViewerUrl(_0x4ed322);continue;case'3':_0x2ed544[_0x5e947e(0x293)]=_0x35c54a;continue;case'4':_0x2ed544[_0x5e947e(0x28e)]=_0x5097b8['id'];continue;}break;}}_0x2c226b['trackPhaseEnd']('update_session');}catch(_0x3f712a){await _0x2c226b['trackPhaseError'](_0x2c8f28[_0x5e947e(0x279)],_0x3f712a);}}catch{}_0x2c226b[_0x5e947e(0x29d)](_0x5e947e(0x2cd));let _0x25f893;if(_0x2c8f28['Nspba'](_0x21f2e3,_0x81ec8e)){const _0xbf40d1=_0x2c8f28['rEaCU'](getMockEvaluation,_0x81ec8e),_0x292aaf=_0x281583;_0x25f893={..._0xbf40d1,'sessionId':_0x5097b8['id'],'sessionDir':_0x2c8f28[_0x5e947e(0x29e)](getTraceDirectory,_0x5097b8['id']),'durationMs':_0x35c54a,'traceViewerUrl':_0x4ed322?buildTraceViewerUrl(_0x4ed322):undefined,'checklistItemId':_0x539a71['id'],'addressedCommentIds':_0x292aaf?.[_0x5e947e(0x1f6)]??[]},console[_0x5e947e(0x2a0)](_0x5e947e(0x1ff)+_0x81ec8e);}else{const _0x4cbbde=_0x281583;if(_0x28e70d&&!_0x4cbbde)throw new Error(_0x5e947e(0x223)+_0x28e70d);if(!_0x4cbbde)throw new Error(_0x5e947e(0x278));_0x25f893=_0x4cbbde;}_0x2c226b[_0x5e947e(0x220)](_0x2c8f28['APxkD'],{'evaluation':_0x25f893[_0x5e947e(0x2cd)],'issueCount':_0x25f893[_0x5e947e(0x1b5)]?.[_0x5e947e(0x252)]??0x0}),_0x2c226b['trackPhaseStart'](_0x2c8f28[_0x5e947e(0x189)]);const _0x4c5390=_0x25f893[_0x5e947e(0x2cd)];if(_0x4c5390===_0x5e947e(0x286))await updateChecklistItem(_0x7fee02,_0x539a71['id'],{'status':_0x5e947e(0x286),'browserSessionId':_0x5097b8['id']}),console[_0x5e947e(0x2a0)]('\x0a✅\x20Scenario\x20verified!');else{if(_0x4c5390===_0x2c8f28[_0x5e947e(0x19c)]){await updateChecklistItem(_0x7fee02,_0x539a71['id'],{'status':_0x2c8f28[_0x5e947e(0x19c)],'browserSessionId':_0x5097b8['id'],'blockedReason':_0x25f893[_0x5e947e(0x1ee)]}),console[_0x5e947e(0x2a0)]('\x0a'+'='['repeat'](0x3c)),console[_0x5e947e(0x2a0)](_0x5e947e(0x26b)),console[_0x5e947e(0x2a0)](''+'='['repeat'](0x3c)),console[_0x5e947e(0x2a0)]('\x0aIssue:\x20'+_0x25f893[_0x5e947e(0x1ee)]);if(_0x25f893['issues']?.[_0x5e947e(0x252)]){console['log']('\x0aDetails:');for(const _0x57f423 of _0x25f893[_0x5e947e(0x1b5)]){const _0x32f525=_0x57f423['type']?'\x20('+_0x57f423[_0x5e947e(0x25f)]+')':'';console[_0x5e947e(0x2a0)]('\x20\x20-\x20['+_0x57f423[_0x5e947e(0x1a1)]+']'+_0x32f525+'\x20'+_0x57f423['description']);}}_0x25f893['traceViewerUrl']&&console[_0x5e947e(0x2a0)](_0x5e947e(0x282)+_0x25f893[_0x5e947e(0x24f)]),console['log']('\x0aSuggested\x20action:\x20Debug\x20this\x20issue\x20in\x20your\x20code,\x20then\x20run\x20go\x20again.'),console[_0x5e947e(0x2a0)]('='['repeat'](0x3c)+'\x0a');}else(_0x2c8f28['jqmPF'](_0x4c5390,_0x2c8f28[_0x5e947e(0x1ac)])||_0x2c8f28['jqmPF'](_0x4c5390,'failed')||_0x4c5390===_0x2c8f28[_0x5e947e(0x2b1)])&&(await updateChecklistItem(_0x7fee02,_0x539a71['id'],{'status':_0x2c8f28['vneVB'],'browserSessionId':_0x5097b8['id'],'incompleteReason':_0x25f893[_0x5e947e(0x1ee)]}),await handleIncompleteItem(_0x7fee02,_0x539a71,_0x25f893));}_0x2c226b[_0x5e947e(0x220)](_0x2c8f28[_0x5e947e(0x189)],{'newStatus':_0x4c5390}),_0x2c226b['trackPhaseStart']('comment_addressing',{'totalFeedbackComments':_0x45b812['length'],'agentAddressedCount':_0x25f893[_0x5e947e(0x1f6)]?.[_0x5e947e(0x252)]??0x0});const _0x4ac995=_0x25f893['addressedCommentIds']?.[_0x5e947e(0x192)](_0x19a680=>_0x45b812['includes'](_0x19a680));if(_0x4ac995&&_0x2c8f28['NkYsJ'](_0x4ac995[_0x5e947e(0x252)],0x0))try{await _0x2c8f28[_0x5e947e(0x26e)](markCommentsAddressed,_0x7fee02,_0x539a71['id'],_0x4ac995),console['log']('Marked\x20'+_0x4ac995['length']+_0x5e947e(0x1b3)),_0x2c226b[_0x5e947e(0x220)](_0x5e947e(0x18c),{'addressedCount':_0x4ac995['length']});}catch(_0x33fb26){await _0x2c226b['trackPhaseError'](_0x5e947e(0x18c),_0x33fb26);}else _0x2c226b['trackPhaseEnd'](_0x2c8f28['CteRB'],{'addressedCount':0x0});return _0x25f893;}export async function verifyFeature(_0x554711){const _0x315c6a=_0x50bbd8,_0x2a5f49={'fkFTL':_0x315c6a(0x187),'eiiSP':function(_0x310a3d,_0x21c215,_0x579b37){return _0x310a3d(_0x21c215,_0x579b37);},'iypHE':function(_0x3e9ddf,_0x411cf1){return _0x3e9ddf-_0x411cf1;},'kmbLt':function(_0x2d62c8,_0x1cd1db,_0x305ba8,_0x1ff8eb){return _0x2d62c8(_0x1cd1db,_0x305ba8,_0x1ff8eb);},'BtGHY':function(_0x1d690a){return _0x1d690a();},'agnKT':function(_0xcc1e88,_0x510c9d){return _0xcc1e88(_0x510c9d);},'wRvBl':'SIGINT','JmIOl':_0x315c6a(0x1dd),'tTCAz':_0x315c6a(0x2be),'uavQt':function(_0x272234,_0x3b04df){return _0x272234&&_0x3b04df;},'PWTsf':function(_0xffcffa,_0x5b3efc,_0x1d0e95,_0x2cb6b5){return _0xffcffa(_0x5b3efc,_0x1d0e95,_0x2cb6b5);},'oSuWt':'pending','iVLjT':'Scenario\x20reset\x20to\x20pending\x20after\x20unexpected\x20error.','UadWm':_0x315c6a(0x284),'gHDVV':'cleanup'},_0x495419=createTelemetryCollector('go');await _0x495419[_0x315c6a(0x235)]({'hasProfile':!!_0x554711[_0x315c6a(0x22f)],'hasScenario':_0x554711['scenario']!==undefined,'hasNotes':!!_0x554711['notes'],'isDebugMode':!!_0x554711[_0x315c6a(0x2ae)]});const {envNames:_0x549eae}=await _0x2a5f49['BtGHY'](getEnvNames);if(_0x549eae['length']===0x0)throw new Error(_0x2a5f49[_0x315c6a(0x1fe)](formatProfileRequiredMessage,_0x23d6fe=>bold(_0x23d6fe)));let _0x28689e,_0x1334b4=![],_0x5ed48d=![];const _0x1cb602=async()=>{const _0x173406=_0x315c6a;if(_0x1334b4)return;_0x1334b4=!![],console[_0x173406(0x2a0)](_0x2a5f49['fkFTL']),await _0x495419['trackCommandEnd'](_0x173406(0x2c8),{'durationMs':_0x28689e?Date['now']()-_0x28689e[_0x173406(0x238)]:0x0});if(_0x28689e){try{await _0x2a5f49['eiiSP'](updateBrowserSession,_0x28689e['browserSession']['id'],{'status':'interrupted','durationMs':_0x2a5f49[_0x173406(0x222)](Date[_0x173406(0x20d)](),_0x28689e['startTime'])});}catch{}try{await _0x2a5f49['kmbLt'](updateChecklistItem,_0x28689e[_0x173406(0x2a8)],_0x28689e[_0x173406(0x29b)]['id'],{'status':_0x173406(0x1dc)});}catch{}if(_0x28689e['configResult'])try{await cleanupTempFiles(_0x28689e[_0x173406(0x275)]);}catch{}}console[_0x173406(0x2a0)]('Scenario\x20reset\x20to\x20pending.\x20Partial\x20steps\x20are\x20preserved.'),process['exit'](0x0);};process['on'](_0x2a5f49[_0x315c6a(0x1e5)],_0x1cb602),process['on'](_0x2a5f49[_0x315c6a(0x1ea)],_0x1cb602);try{_0x28689e=await runVerification(_0x554711,_0x495419),await _0x495419[_0x315c6a(0x288)]();const _0x14d821=await processVerificationResult(_0x28689e);return _0x5ed48d=!![],await _0x495419[_0x315c6a(0x1e4)](_0x2a5f49['tTCAz'],{'evaluation':_0x14d821[_0x315c6a(0x2cd)],'durationMs':_0x28689e[_0x315c6a(0x293)],..._0x28689e[_0x315c6a(0x199)]}),_0x14d821;}catch(_0x4ad904){await _0x495419[_0x315c6a(0x25e)](_0x4ad904);throw _0x4ad904;}finally{process['removeListener']('SIGINT',_0x1cb602),process[_0x315c6a(0x234)](_0x2a5f49['JmIOl'],_0x1cb602);if(_0x2a5f49['uavQt'](_0x28689e,!_0x1334b4)&&!_0x5ed48d)try{await _0x2a5f49[_0x315c6a(0x20e)](updateChecklistItem,_0x28689e['featureId'],_0x28689e['checklistItem']['id'],{'status':_0x2a5f49[_0x315c6a(0x291)]}),console[_0x315c6a(0x2a0)](_0x2a5f49['iVLjT']);}catch(_0x4584ca){await _0x495419[_0x315c6a(0x260)](_0x2a5f49[_0x315c6a(0x2b0)],_0x4584ca);}if(_0x28689e){_0x495419[_0x315c6a(0x29d)](_0x2a5f49[_0x315c6a(0x230)]);_0x28689e[_0x315c6a(0x275)]&&await cleanupTempFiles(_0x28689e[_0x315c6a(0x275)]);try{const _0x3d8b79=_0x2a5f49[_0x315c6a(0x1fe)](getTraceDirectory,_0x28689e['browserSession']['id']);_0x2a5f49['agnKT'](existsSync,_0x3d8b79)&&await _0x2a5f49['eiiSP'](rm,_0x3d8b79,{'recursive':!![],'force':!![]});}catch{}try{_0x28689e['conversationDir']&&existsSync(_0x28689e['conversationDir'])&&await rm(_0x28689e[_0x315c6a(0x18e)],{'recursive':!![],'force':!![]});}catch{}_0x495419[_0x315c6a(0x220)]('cleanup');}await _0x495419['flush']();}}