@ranger-testing/ranger-cli 2.0.5 → 2.0.7
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.
- package/build/cli.js +1 -914
- package/build/commands/addEnv.js +1 -1
- package/build/commands/authEncrypt.js +1 -36
- package/build/commands/clean.js +1 -1
- package/build/commands/config.js +1 -93
- package/build/commands/env.js +1 -98
- package/build/commands/feature.js +1 -653
- package/build/commands/hook.js +1 -33
- package/build/commands/hooks/autoPrompt.js +1 -32
- package/build/commands/hooks/disable.js +1 -33
- package/build/commands/hooks/enable.js +1 -58
- package/build/commands/hooks/exitPlanMode.js +1 -35
- package/build/commands/hooks/index.js +1 -12
- package/build/commands/hooks/output.js +1 -71
- package/build/commands/hooks/planReminder.js +1 -46
- package/build/commands/hooks/planStart.js +1 -30
- package/build/commands/hooks/postEdit.js +1 -43
- package/build/commands/hooks/preCompact.js +1 -30
- package/build/commands/hooks/sessionEnd.js +1 -25
- package/build/commands/hooks/sessionStart.js +1 -93
- package/build/commands/hooks/stopHook.js +1 -155
- package/build/commands/index.js +1 -12
- package/build/commands/login.js +1 -26
- package/build/commands/setupCi.js +1 -189
- package/build/commands/skillup.js +1 -80
- package/build/commands/start.js +1 -1
- package/build/commands/status.js +1 -198
- package/build/commands/update.js +1 -182
- package/build/commands/updateEnv.js +1 -1
- package/build/commands/useEnv.js +1 -1
- package/build/commands/utils/activeProfile.js +1 -76
- package/build/commands/utils/browserSessionsApi.js +1 -1
- package/build/commands/utils/claudeConfig.js +1 -73
- package/build/commands/utils/claudePlugin.js +1 -85
- package/build/commands/utils/crypto.js +1 -42
- package/build/commands/utils/desirePathLog.js +1 -139
- package/build/commands/utils/deviceAuth.js +1 -232
- package/build/commands/utils/environment.js +1 -65
- package/build/commands/utils/featureApi.js +1 -371
- package/build/commands/utils/featureReportGenerator.js +1 -204
- package/build/commands/utils/fixWebmDuration.js +1 -0
- package/build/commands/utils/git.js +1 -44
- package/build/commands/utils/keychain.js +1 -1
- package/build/commands/utils/localAgentInstallationsApi.js +1 -1
- package/build/commands/utils/profileMessages.js +1 -8
- package/build/commands/utils/profileSetupBanner.js +1 -167
- package/build/commands/utils/rangerRoot.js +1 -60
- package/build/commands/utils/reportGenerator.js +1 -130
- package/build/commands/utils/retry.js +1 -25
- package/build/commands/utils/sessionCache.js +1 -299
- package/build/commands/utils/settings.js +1 -313
- package/build/commands/utils/skillContent.js +1 -28
- package/build/commands/utils/skills.js +1 -1
- package/build/commands/utils/telemetry.js +1 -254
- package/build/commands/utils/userApi.js +1 -32
- package/build/commands/utils/version.js +1 -62
- package/build/commands/verifyFeature.js +1 -1343
- package/build/commands/verifyInBrowser.js +1 -1
- package/package.json +4 -1
- package/build/cli.js.map +0 -1
- package/build/commands/addEnv.js.map +0 -1
- package/build/commands/authEncrypt.js.map +0 -1
- package/build/commands/clean.js.map +0 -1
- package/build/commands/config.js.map +0 -1
- package/build/commands/env.js.map +0 -1
- package/build/commands/feature.js.map +0 -1
- package/build/commands/hook.js.map +0 -1
- package/build/commands/hooks/autoPrompt.js.map +0 -1
- package/build/commands/hooks/disable.js.map +0 -1
- package/build/commands/hooks/enable.js.map +0 -1
- package/build/commands/hooks/exitPlanMode.js.map +0 -1
- package/build/commands/hooks/index.js.map +0 -1
- package/build/commands/hooks/output.js.map +0 -1
- package/build/commands/hooks/planReminder.js.map +0 -1
- package/build/commands/hooks/planStart.js.map +0 -1
- package/build/commands/hooks/postEdit.js.map +0 -1
- package/build/commands/hooks/preCompact.js.map +0 -1
- package/build/commands/hooks/sessionEnd.js.map +0 -1
- package/build/commands/hooks/sessionStart.js.map +0 -1
- package/build/commands/hooks/stopHook.js.map +0 -1
- package/build/commands/index.js.map +0 -1
- package/build/commands/login.js.map +0 -1
- package/build/commands/setupCi.js.map +0 -1
- package/build/commands/skillup.js.map +0 -1
- package/build/commands/start.js.map +0 -1
- package/build/commands/status.js.map +0 -1
- package/build/commands/update.js.map +0 -1
- package/build/commands/updateEnv.js.map +0 -1
- package/build/commands/useEnv.js.map +0 -1
- package/build/commands/utils/activeProfile.js.map +0 -1
- package/build/commands/utils/browserSessionsApi.js.map +0 -1
- package/build/commands/utils/claudeConfig.js.map +0 -1
- package/build/commands/utils/claudePlugin.js.map +0 -1
- package/build/commands/utils/crypto.js.map +0 -1
- package/build/commands/utils/desirePathLog.js.map +0 -1
- package/build/commands/utils/deviceAuth.js.map +0 -1
- package/build/commands/utils/environment.js.map +0 -1
- package/build/commands/utils/featureApi.js.map +0 -1
- package/build/commands/utils/featureReportGenerator.js.map +0 -1
- package/build/commands/utils/git.js.map +0 -1
- package/build/commands/utils/keychain.js.map +0 -1
- package/build/commands/utils/localAgentInstallationsApi.js.map +0 -1
- package/build/commands/utils/profileMessages.js.map +0 -1
- package/build/commands/utils/profileSetupBanner.js.map +0 -1
- package/build/commands/utils/rangerRoot.js.map +0 -1
- package/build/commands/utils/reportGenerator.js.map +0 -1
- package/build/commands/utils/retry.js.map +0 -1
- package/build/commands/utils/sessionCache.js.map +0 -1
- package/build/commands/utils/settings.js.map +0 -1
- package/build/commands/utils/skillContent.js.map +0 -1
- package/build/commands/utils/skills.js.map +0 -1
- package/build/commands/utils/telemetry.js.map +0 -1
- package/build/commands/utils/userApi.js.map +0 -1
- package/build/commands/utils/version.js.map +0 -1
- package/build/commands/verifyFeature.js.map +0 -1
- 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
|
+
function _0x38a4(_0x1ca7a0,_0x456d47){_0x1ca7a0=_0x1ca7a0-0xd3;const _0x2265a4=_0x2265();let _0x38a4ae=_0x2265a4[_0x1ca7a0];return _0x38a4ae;}const _0x2f4b72=_0x38a4;(function(_0x1390c9,_0xc96a43){const _0x508a6a=_0x38a4,_0x23f57b=_0x1390c9();while(!![]){try{const _0x1b66ba=-parseInt(_0x508a6a(0x209))/0x1+parseInt(_0x508a6a(0x19f))/0x2*(parseInt(_0x508a6a(0x21c))/0x3)+parseInt(_0x508a6a(0xdc))/0x4+parseInt(_0x508a6a(0x10c))/0x5+parseInt(_0x508a6a(0x1e9))/0x6+-parseInt(_0x508a6a(0x215))/0x7+-parseInt(_0x508a6a(0x143))/0x8;if(_0x1b66ba===_0xc96a43)break;else _0x23f57b['push'](_0x23f57b['shift']());}catch(_0x2c661f){_0x23f57b['push'](_0x23f57b['shift']());}}}(_0x2265,0x33a0c));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 _0x5a38ae 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';function _0x2265(){const _0x37ea0f=['OFONY','toLowerCase','Feature\x20is\x20partially\x20implemented\x20but\x20missing\x20key\x20functionality','replace','mtime','boolean','videos','Some\x20requirements\x20were\x20not\x20fully\x20verified.','Reviewer\x20feedback:\x20','configResult','1038884AizsEU','xvPDH','\x0aSuggested\x20action:\x20Debug\x20this\x20issue\x20in\x20your\x20code,\x20then\x20run\x20go\x20again.','success','log','qNbMa','PmQsJ','run-mcp-server','LBSiC','BLOCKING\x20ISSUE\x20DETECTED\x20-\x20Debug\x20Required','evaluation','setContext','currentSessionId','IrlhS','ludDo','hook_screenshot_upload','xusMX','IjaEy','interactive','Otebu','\x0aAll\x20other\x20scenarios\x20are\x20complete.','\x0a\x0a##\x20Debug:\x20Reviewer\x20Comments\x0a','PiGjG','text','BnJxb','Notes:\x20','cijAF','trackPhaseEnd','exit','currentSession','\x20comment(s)\x20as\x20addressed','after','lKAhp','hlBqE','description','closed','verification_in_progress','browser_click','conversation.jsonl','debugOutcome','SItFd','EdeDs','trackCommandEnd','zoyCg','jBaQF','XvoSQ','browser_press_key','key_','1757145McGaYG','error','GZngn','tDCJi','checklistItem','fbmqe','stringify','object','playwright_config','short','KzRmQ','[browser]\x20Press\x20key\x20→\x20','pnSgd','includes','scenario','UI\x20elements\x20present\x20but\x20not\x20fully\x20functional','MINOR','NAVIGATION_ERROR','checklistItemId','All\x20scenario\x20requirements\x20were\x20met.','nAKOm','tool_call','YsnVV','\x20\x201.\x20Fix\x20the\x20issues\x20above\x20in\x20your\x20code','MWTux','now','cXwYy','PmEGy','bmarC','filter','uploadUrl','The\x20--scenario\x20flag\x20is\x20required\x20in\x20non-interactive\x20mode.\x20See\x20available\x20scenarios\x20above.','repeat','toISOString','canonicalFlow','tsMDV','\x0a\x0a##\x20Expected\x20Flow\x20(from\x20previous\x20verification)\x0a','tool_name','unaddressedCommentCount','\x22\x20into\x20\x22','trace-upload.zip','\x1b[0m','checklistItems','time','array','status','OTHER','BaByC','MAIiD','verified','pending','QAvhV','mcp__ranger-browser__','fkTlm','MAJOR','893328MPibxt','unaddressedComments','HTTP\x20404\x20-\x20Page\x20not\x20found.','uploadedFiles','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','[hook]\x20Creating\x20step\x20#','cuxUj','fSblB','join','tool_use','VoQKt','toLocaleDateString','zEZIt','split','MSbjL','ZKOck','length','session_error_update','content','utf-8','fLsae','No\x20active\x20profile.\x20Run:\x20ranger\x20profile\x20use\x20<profile-name>','approved','trackPhaseStart','\x22.\x20Run:\x20ranger\x20profile\x20config\x20set\x20','\x20and\x20cannot\x20be\x20verified.\x20Choose\x20a\x20different\x20scenario.','No\x20result\x20received\x20from\x20agent','cSEGx','--config','\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','pipe','filename','uYAZU','YPOkz','conversationDir','Cannot\x20verify\x20scenario\x20\x22','rtEqX','agaUf','vVJrZ','traceViewerUrl','severity','HzrFu','feature_load','SRzSs','JYbuS','tool_failure','zgWOy','Scenario\x20reset\x20to\x20pending.\x20Partial\x20steps\x20are\x20preserved.','BLOCKER','IpvwC','TVrHF','\x20\x20•\x20','PtaPz','If\x20you\x27re\x20done\x20for\x20now,\x20you\x20can\x20stop\x20and\x20resume\x20later\x20with\x20\x27ranger\x20resume\x27.','string','aBsAN','QGZGU','comment_addressing','is_interrupt','eQSuh','mcp__ranger-browser__browser_navigate','LuLvF','Wpcik','isArray','assistant','Target\x20page\x20returns\x20404\x20Not\x20Found','profile_resolution','Invalid\x20scenario\x20index:\x20','agent_execution','input','\x20[not\x20actionable]','structured_output','baseUrl','ecHeb','YieCG','env','addressedCommentIds','PostToolUseFailure','Unable\x20to\x20proceed\x20due\x20to\x20missing\x20page','NYoOD','Unknown\x20error','sessionId','not\x20actionable','num_turns','dashboardUrl','\x0aVerification\x20interrupted.\x20Cleaning\x20up...','cache_creation_input_tokens','notes','.png','xiGrS','ElzNG','size','9838fPXyyp','feedback_fetch','update_session','OHXhW','toolCallCounts','witHZ','browserSession','fnLQy','duration_ms','Timeout\x20waiting\x20for\x20element','YLRne','xEQTg','IDs\x20of\x20reviewer\x20comments\x20that\x20have\x20been\x20addressed\x20in\x20the\x20current\x20implementation','[browser]\x20Navigate\x20→\x20','Key\x20moment\x20captured\x20during\x20verification','yAhuD','HpZdj','\x22\x20not\x20found.\x20Run:\x20ranger\x20profile\x20add\x20','duration_api_ms','endsWith','mzkEf','BVcZh','HTTP_500','element','##\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','aJYDw','startTime','scenario_update','GSqAk','PuZrK','SIGTERM','aUoKE','failed','mGuJx','OTzJR','qNOKq','\x20comments]','HTTP_404','issues','actionable','map','No\x20baseUrl\x20configured\x20for\x20profile\x20\x22','UJexk','url','mcp__ranger-browser__browser_type','[DEBUG]\x20Mock\x20failed\x20verification.','oKEez','slice','scenario_select','KfZhF','ukpUI','browser_type','hook','usage','sALko','Screenshot\x20captured\x20during\x20verification','hook_event_name','upload_conversation','get','terminalReason','upload_video','trackPhaseError','ZrzsB','incomplete','\x0aNon-interactive\x20mode\x20detected.\x20The\x20--scenario\x20flag\x20is\x20required.','RoJSw','\x22\x20.',']\x20\x22','forEach','[DEBUG]\x20Mock\x20incomplete\x20verification.','FfPBA','**\x20(','eurLk','ranger-browser-sessions','1344198OouHzL','TXOMr','EwDDe','-\x20[','Selected\x20scenario\x20is\x20not\x20currently\x20actionable.\x20Choose\x20another\x20scenario.','message','SIGINT','yMXVY','oxncg','condition','YkVoD','YzRYn','[DEBUG]\x20Mock\x20partial\x20verification.','blocked','eikYn','summary','startsWith','Scenario\x20','iEdzN','BZVmV','type','flush','\x0a[DEBUG\x20MODE]\x20Running\x20minimal\x20browser\x20test\x20with\x20outcome:\x20','JSwQU','upload_screenshot','git\x20rev-parse\x20--abbrev-ref\x20HEAD','evaluationReason','sIhvN','zLeXy','path','cdpEndpoint','key','419096sxOrYg','\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\x20the\x20initial\x20snapshot\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\x20navigating\x20to\x20different\x20views\x20or\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_initial-state.png\x22,\x20\x2202_button-clicked.png\x22,\x20\x2203_result-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-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','agoHn','nXHCJ','.webm','ThiSD','YVgWI','hHjab','resultMeta','XOyow','\x0aUsage:\x20ranger\x20go\x20--scenario\x20<number>','screenshot','1545929ljCEGv','trim','\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','startPath','.\x0aThis\x20is\x20NOT\x20a\x20web\x20app\x20—\x20there\x20is\x20no\x20URL\x20to\x20navigate\x20to.\x20The\x20app\x20is\x20already\x20running\x20and\x20connected.','parentItemId','DdtbW','78XqhvCX','jFwzf'];_0x2265=function(){return _0x37ea0f;};return _0x2265();}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';import{fixWebmDuration}from'./utils/fixWebmDuration.js';const bold=_0x56716d=>'\x1b[1m'+_0x56716d+_0x2f4b72(0x135);function getGitBranch(){const _0x1401cd=_0x2f4b72,_0x55540d={'Ramgc':function(_0x5417f6,_0x2b2bef,_0x470d2d){return _0x5417f6(_0x2b2bef,_0x470d2d);},'IgEpS':_0x1401cd(0x202),'YsnVV':_0x1401cd(0x156),'LuLvF':'pipe'};try{return _0x55540d['Ramgc'](execSync,_0x55540d['IgEpS'],{'encoding':_0x55540d[_0x1401cd(0x122)],'stdio':[_0x1401cd(0x161),_0x55540d[_0x1401cd(0x180)],_0x1401cd(0x161)]})[_0x1401cd(0x216)]();}catch{return undefined;}}async function zipDirectory(_0x2fd771){const _0x8b01db=_0x2f4b72,_0x44392e={'STrCd':function(_0x4691ad,_0x44b69a,_0x1c36e9,_0x4d09e5){return _0x4691ad(_0x44b69a,_0x1c36e9,_0x4d09e5);},'iEdzN':'pipe','yAhuD':function(_0x59b08d,_0x204a36){return _0x59b08d(_0x204a36);}},_0x413d2c=_0x44392e['STrCd'](join,_0x2fd771,'..',_0x8b01db(0x134));execSync('cd\x20\x22'+_0x2fd771+'\x22\x20&&\x20zip\x20-r\x20\x22'+_0x413d2c+_0x8b01db(0x1e1),{'stdio':_0x44392e['iEdzN']});const _0x27d155=await _0x44392e[_0x8b01db(0x1ae)](readFile,_0x413d2c);return execSync('rm\x20\x22'+_0x413d2c+'\x22',{'stdio':_0x44392e[_0x8b01db(0x1fb)]}),_0x27d155;}function getTraceDirectory(_0x163625){const _0x388c1e=_0x2f4b72,_0x30ab82={'MAIiD':function(_0x38bb17,_0x43271c,_0xb0e3bb,_0x43e52c){return _0x38bb17(_0x43271c,_0xb0e3bb,_0x43e52c);},'tXrFS':function(_0x484f09){return _0x484f09();},'bfmlY':'sessions'};return _0x30ab82[_0x388c1e(0x13c)](join,_0x30ab82['tXrFS'](getRangerDir),_0x30ab82['bfmlY'],_0x163625);}function getConversationFilePath(_0x5d93e3){const _0x121d24=_0x2f4b72,_0xe6bd6f={'EfvCj':function(_0x77fd4b){return _0x77fd4b();}};return join(_0xe6bd6f['EfvCj'](tmpdir),_0x121d24(0x1e8),_0x5d93e3,_0x121d24(0x102));}async function loadSessionVideos(_0x4a04bc){const _0x412289=_0x2f4b72,_0xc964a5={'oKEez':_0x412289(0xd8),'xEQTg':function(_0x265dce,_0x972f79){return _0x265dce(_0x972f79);}},_0x1bd5b7=join(_0x4a04bc,_0xc964a5[_0x412289(0x1cd)]);if(!_0xc964a5[_0x412289(0x1aa)](existsSync,_0x1bd5b7))return[];const _0x4757cf=await readdir(_0x1bd5b7),_0x40cf25=_0x4757cf[_0x412289(0x129)](_0x5966cc=>_0x5966cc['toLowerCase']()['endsWith'](_0x412289(0x20d)));return _0x40cf25['map'](_0x2b8292=>({'filename':_0x2b8292,'path':join(_0x1bd5b7,_0x2b8292)}));}function getMockEvaluation(_0x6cbfd){const _0x2062b5=_0x2f4b72,_0x4f98ec={'witHZ':'verified','fCQnc':_0x2062b5(0x11f),'HpZdj':_0x2062b5(0x13a),'lmcuU':'Secondary\x20feature\x20not\x20fully\x20implemented','NIWfI':'Implementation\x20is\x20incomplete\x20and\x20needs\x20additional\x20work.','nzUtY':_0x2062b5(0x142),'YPOkz':_0x2062b5(0xd4),'KfZhF':_0x2062b5(0x173),'VrBgS':'HTTP_404','ecHeb':_0x2062b5(0x11d),'BVQeE':_0x2062b5(0x1bf),'NOZxa':_0x2062b5(0x1a8)},_0x1f73fb={'verified':{'success':!![],'summary':'[DEBUG]\x20Mock\x20verification\x20completed\x20successfully.','evaluation':_0x4f98ec[_0x2062b5(0x1a4)],'evaluationReason':_0x4f98ec['fCQnc']},'partial':{'success':![],'summary':_0x2062b5(0x1f5),'evaluation':'partial','evaluationReason':_0x2062b5(0xd9),'issues':[{'severity':'MINOR','type':_0x4f98ec[_0x2062b5(0x1af)],'description':_0x4f98ec['lmcuU']}]},'incomplete':{'success':![],'summary':_0x2062b5(0x1e4),'evaluation':_0x2062b5(0x1de),'evaluationReason':_0x4f98ec['NIWfI'],'issues':[{'severity':_0x4f98ec['nzUtY'],'type':_0x2062b5(0x13a),'description':_0x4f98ec[_0x2062b5(0x164)]},{'severity':_0x2062b5(0x11c),'type':'OTHER','description':_0x2062b5(0x11b)}]},'blocked':{'success':![],'summary':'[DEBUG]\x20Mock\x20blocked\x20verification.','evaluation':_0x2062b5(0x1f6),'evaluationReason':_0x2062b5(0x145),'issues':[{'severity':_0x4f98ec[_0x2062b5(0x1d0)],'type':_0x4f98ec['VrBgS'],'description':_0x2062b5(0x184)},{'severity':_0x2062b5(0x142),'type':_0x4f98ec[_0x2062b5(0x18c)],'description':_0x2062b5(0x191)}]},'failed':{'success':![],'summary':_0x2062b5(0x1cc),'evaluation':_0x4f98ec['BVQeE'],'evaluationReason':'Browser\x20automation\x20failed\x20with\x20timeout\x20error.','issues':[{'severity':_0x2062b5(0x173),'type':'OTHER','description':_0x4f98ec['NOZxa']}]}};return _0x1f73fb[_0x6cbfd];}function getDebugPrompt(){return'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.';}async function selectChecklistItem(_0x289edb){const _0x3017cf=_0x2f4b72,_0x3c25db={'YzRYn':function(_0x28eacd,_0x34621e){return _0x28eacd===_0x34621e;},'QGZGU':'closed','FaCfI':function(_0x33952c,_0x2e31d8){return _0x33952c===_0x2e31d8;},'DQhFH':_0x3017cf(0x1de),'ThiSD':function(_0x41109e,_0x2d80ad){return _0x41109e===_0x2d80ad;},'qNbMa':function(_0x2a536a,_0x49293a){return _0x2a536a===_0x49293a;},'tDCJi':_0x3017cf(0x100),'YVgWI':function(_0x1f16c4,_0x1f6299){return _0x1f16c4>_0x1f6299;},'LBCdx':_0x3017cf(0x195),'nAKOm':function(_0x3197aa,_0x6fd01e){return _0x3197aa+_0x6fd01e;},'eikYn':'list','beepa':'selected','MSbjL':'Which\x20scenario\x20does\x20this\x20verify?'};if(_0x289edb[_0x3017cf(0x153)]===0x0)return null;const _0x55ab11=_0x289edb['map'](_0x11cb0c=>{const _0x76f927=_0x3017cf,_0x31bb58=_0x3c25db[_0x76f927(0x1f4)](_0x11cb0c[_0x76f927(0x139)],_0x3c25db[_0x76f927(0x17b)])&&_0x3c25db['FaCfI'](_0x11cb0c['terminalReason'],_0x76f927(0x159))?'✅':_0x11cb0c[_0x76f927(0x139)]===_0x76f927(0x13d)?'🟢':_0x11cb0c['status']===_0x3c25db['DQhFH']?'🟠':_0x3c25db[_0x76f927(0x20e)](_0x11cb0c['status'],_0x76f927(0x1f6))?'🛑':_0x11cb0c[_0x76f927(0x139)]===_0x3c25db['QGZGU']?'⛔':_0x3c25db[_0x76f927(0xe1)](_0x11cb0c['status'],_0x3c25db[_0x76f927(0x10f)])?'⏳':'⬜',_0x2ad413=_0x3c25db[_0x76f927(0x20f)](_0x11cb0c['unaddressedCommentCount'],0x0)?'\x20['+_0x11cb0c['unaddressedCommentCount']+_0x76f927(0x1c3):'',_0x51dee5=_0x11cb0c[_0x76f927(0x1c6)]?![]:_0x11cb0c[_0x76f927(0x139)]===_0x3c25db[_0x76f927(0x17b)]&&_0x11cb0c[_0x76f927(0x1da)]?_0x11cb0c['terminalReason']:_0x3c25db['LBCdx'];return{'name':_0x3c25db[_0x76f927(0x120)](_0x11cb0c['displayIndex'],0x1)+'.\x20'+_0x31bb58+'\x20'+_0x11cb0c[_0x76f927(0xfe)]+_0x2ad413,'value':_0x11cb0c['id'],'disabled':_0x51dee5};}),{selected:_0x2a7bc3}=await _0x5a38ae['prompt']([{'type':_0x3c25db[_0x3017cf(0x1f7)],'name':_0x3c25db['beepa'],'message':_0x3c25db[_0x3017cf(0x151)],'choices':_0x55ab11}]);return _0x289edb['find'](_0x506cf7=>_0x506cf7['id']===_0x2a7bc3)||null;}async function handleIncompleteItem(_0x47cf96,_0x5b69e3,_0x1ac61e){const _0x2fb0a1=_0x2f4b72,_0x285a90={'fkTlm':function(_0x2ed941,_0xe16858){return _0x2ed941(_0xe16858);},'xusMX':function(_0x878e00,_0x559aff){return _0x878e00>_0x559aff;}},{items:_0x5a9d78}=await _0x285a90[_0x2fb0a1(0x141)](getActionItems,_0x47cf96),_0x2ae276=_0x5a9d78['filter'](_0x247406=>_0x247406['id']!==_0x5b69e3['id']),_0x465353=_0x2ae276['every'](_0x411c82=>_0x411c82['status']==='verified'||_0x411c82['status']===_0x2fb0a1(0x1f6)||_0x411c82[_0x2fb0a1(0x139)]==='closed'||_0x411c82[_0x2fb0a1(0x139)]===_0x2fb0a1(0x1de));console[_0x2fb0a1(0xe0)]('\x0a'+'='[_0x2fb0a1(0x12c)](0x3c)),console[_0x2fb0a1(0xe0)]('INCOMPLETE\x20-\x20Verification\x20found\x20issues'),console['log'](''+'='['repeat'](0x3c));if(_0x1ac61e['issues']&&_0x1ac61e[_0x2fb0a1(0x1c5)][_0x2fb0a1(0x153)]>0x0){console[_0x2fb0a1(0xe0)]('\x0aIssues\x20found:');for(const _0x4ed7b6 of _0x1ac61e[_0x2fb0a1(0x1c5)]){console['log'](_0x2fb0a1(0x176)+_0x4ed7b6['description']);}}else _0x1ac61e[_0x2fb0a1(0x203)]&&console['log']('\x0aReason:\x20'+_0x1ac61e['evaluationReason']);console['log']('\x0aNext\x20steps:'),console['log'](_0x2fb0a1(0x123)),console['log']('\x20\x202.\x20Run\x20\x27ranger\x20go\x27\x20again\x20to\x20re-verify'),_0x465353&&_0x285a90[_0x2fb0a1(0xec)](_0x2ae276['length'],0x0)&&(console['log'](_0x2fb0a1(0xf0)),console[_0x2fb0a1(0xe0)](_0x2fb0a1(0x178))),console['log']('='[_0x2fb0a1(0x12c)](0x3c)+'\x0a');}function createToolCallTrackingHook(_0x30bb54){const _0x43b987=_0x2f4b72,_0x355f52={'OFONY':function(_0x4a1cb8,_0x59b283){return _0x4a1cb8!==_0x59b283;},'QSHYR':'PostToolUse','xiGrS':'mcp__ranger-browser__','ukpUI':function(_0x187427,_0x38205b){return _0x187427+_0x38205b;},'RoJSw':_0x43b987(0x121),'OHXhW':'mcp__ranger-browser__browser_click','cpCYQ':'mcp__ranger-browser__browser_wait_for'},_0x30ca9f=new Map(),_0x228907=async _0xbcc5c8=>{const _0x1a3c9a=_0x43b987;if(_0x355f52[_0x1a3c9a(0x21e)](_0xbcc5c8['hook_event_name'],_0x355f52['QSHYR']))return{};const _0x2fd878=_0xbcc5c8,_0x35bffa=_0x2fd878['tool_input'],_0x143b17=_0x2fd878[_0x1a3c9a(0x131)][_0x1a3c9a(0xd5)](_0x355f52[_0x1a3c9a(0x19c)],'');_0x30ca9f['set'](_0x143b17,_0x355f52[_0x1a3c9a(0x1d1)](_0x30ca9f[_0x1a3c9a(0x1d9)](_0x143b17)||0x0,0x1)),_0x30bb54['trackPhaseStart'](_0x355f52[_0x1a3c9a(0x1e0)],{'toolName':_0x143b17}),_0x30bb54['trackPhaseEnd'](_0x355f52['RoJSw'],{'toolName':_0x143b17,'toolInput':summarizeToolInput(_0x143b17,_0x35bffa)});switch(_0x2fd878['tool_name']){case _0x1a3c9a(0x17f):console[_0x1a3c9a(0xe0)](_0x1a3c9a(0x1ac)+_0x35bffa[_0x1a3c9a(0x1ca)]);break;case _0x355f52[_0x1a3c9a(0x1a2)]:console['log']('[browser]\x20Click\x20→\x20\x22'+_0x35bffa[_0x1a3c9a(0x1b6)]+'\x22');break;case _0x1a3c9a(0x1cb):console[_0x1a3c9a(0xe0)]('[browser]\x20Type\x20→\x20\x22'+_0x35bffa[_0x1a3c9a(0xf3)]+_0x1a3c9a(0x133)+_0x35bffa[_0x1a3c9a(0x1b6)]+'\x22');break;case'mcp__ranger-browser__browser_press_key':console['log'](_0x1a3c9a(0x117)+_0x35bffa[_0x1a3c9a(0x208)]);break;case _0x355f52['cpCYQ']:console['log']('[browser]\x20Wait\x20→\x20'+(_0x35bffa[_0x1a3c9a(0x137)]?_0x35bffa[_0x1a3c9a(0x137)]+'ms':_0x35bffa['text']||_0x1a3c9a(0x1f2)));break;}return{};};return{'hook':_0x228907,'toolCallCounts':_0x30ca9f};}function createToolFailureHook(_0x22bc4c){const _0x1a8780=_0x2f4b72,_0x38e0a4={'kltPX':_0x1a8780(0x190),'YgWpY':_0x1a8780(0x170)};return async _0x4c09cb=>{const _0x2e7263=_0x1a8780;if(_0x4c09cb[_0x2e7263(0x1d7)]!==_0x38e0a4['kltPX'])return{};const _0x46993c=_0x4c09cb,_0xf4a496=_0x46993c[_0x2e7263(0x131)]['replace'](_0x2e7263(0x140),'');return await _0x22bc4c['trackPhaseError'](_0x38e0a4['YgWpY'],_0x46993c['error'],{'toolName':_0xf4a496,'isInterrupt':_0x46993c[_0x2e7263(0x17d)]}),{};};}function summarizeToolInput(_0xf444cf,_0x14178f){const _0x2e809a=_0x2f4b72,_0x39308f={'xhEpQ':'browser_navigate','BFtWu':_0x2e809a(0x1d2),'Wpcik':'browser_take_screenshot','czIPk':_0x2e809a(0x10a),'fPlnK':'browser_wait_for'};switch(_0xf444cf){case _0x39308f['xhEpQ']:return{'url':_0x14178f[_0x2e809a(0x1ca)]};case _0x2e809a(0x101):return{'element':_0x14178f[_0x2e809a(0x1b6)]};case _0x39308f['BFtWu']:return{'element':_0x14178f['element']};case _0x39308f[_0x2e809a(0x181)]:return{'filename':_0x14178f[_0x2e809a(0x162)]};case _0x39308f['czIPk']:return{'key':_0x14178f[_0x2e809a(0x208)]};case _0x39308f['fPlnK']:return{'time':_0x14178f['time'],'text':_0x14178f['text']};default:return{};}}function createScreenshotUploadHook(_0x55c202,_0x186c91,_0x5f01b4,_0x49fb02){const _0x432f76=_0x2f4b72,_0x582230={'LYZCu':function(_0x29b12e,_0xa4a446){return _0x29b12e!==_0xa4a446;},'ZKOck':_0x432f76(0xeb),'JgeXZ':function(_0x38f088,_0x223ae3){return _0x38f088(_0x223ae3);},'vVJrZ':function(_0x1ff462,_0x36db15,_0x538223){return _0x1ff462(_0x36db15,_0x538223);},'HzrFu':_0x432f76(0x214),'ElzNG':_0x432f76(0x1ad),'NYoOD':'success','xgUaf':_0x432f76(0xfb)},_0x45feb1=new Set();let _0x36cb07=0x1;const _0x13bca8=async _0x182db4=>{const _0x3250cc=_0x432f76;if(_0x582230['LYZCu'](_0x182db4[_0x3250cc(0x1d7)],'PostToolUse'))return{};const _0x2cb0d6=_0x182db4;if(_0x582230['LYZCu'](_0x2cb0d6[_0x3250cc(0x131)],'mcp__ranger-browser__browser_take_screenshot'))return{};const _0x4443ed=_0x2cb0d6['tool_input'],_0x12ec02=_0x4443ed?.['filename'];if(!_0x12ec02)return{};const _0x3ec6aa=_0x12ec02['toLowerCase']()[_0x3250cc(0x1f9)](_0x3250cc(0x10b));_0x49fb02['trackPhaseStart'](_0x582230['ZKOck'],{'filename':_0x12ec02,'isKeyFrame':_0x3ec6aa});try{const _0x343fb1=join(_0x5f01b4,_0x12ec02),_0x472e57=await _0x582230['JgeXZ'](readFile,_0x343fb1),_0x32605f=await stat(_0x343fb1),_0x5ee341=_0x12ec02[_0x3250cc(0xd5)](/\.png$/i,'')[_0x3250cc(0xd5)](/^key_/i,'')['replace'](/^\d+_/,'')['replace'](/-/g,'\x20'),_0x32d0ff=_0x36cb07++;console[_0x3250cc(0xe0)](_0x3250cc(0x148)+_0x32d0ff+':\x20\x22'+_0x5ee341+'\x22'+(_0x3ec6aa?'\x20(key\x20frame)':''));const {step:_0x3ecab8}=await _0x582230[_0x3250cc(0x169)](createVerificationStep,_0x55c202,{'checklistItemId':_0x186c91,'position':_0x32d0ff,'stepType':_0x582230[_0x3250cc(0x16c)],'stepName':_0x5ee341,'description':_0x3ec6aa?_0x582230[_0x3250cc(0x19d)]:'Screenshot\x20captured\x20during\x20verification','isKeyStep':_0x3ec6aa,'status':_0x582230[_0x3250cc(0x192)],'metadata':{'filename':_0x12ec02,'timestamp':_0x32605f['mtime']['toISOString']()}}),_0x4119cc=await createStepAsset(_0x55c202,_0x3ecab8['id'],{'filename':_0x12ec02,'assetType':_0x582230[_0x3250cc(0x16c)],'timing':_0x582230['xgUaf'],'position':0x0,'capturedAt':_0x32605f[_0x3250cc(0xd6)]['toISOString'](),'metadata':{'name':_0x5ee341,'highPriority':_0x3ec6aa}});await _0x582230[_0x3250cc(0x169)](uploadScreenshot,_0x4119cc[_0x3250cc(0x12a)],_0x472e57),_0x45feb1['add'](_0x12ec02),_0x49fb02['trackPhaseEnd']('hook_screenshot_upload',{'filename':_0x12ec02,'bytes':_0x472e57[_0x3250cc(0x153)]});}catch(_0x51f743){await _0x49fb02[_0x3250cc(0x1dc)](_0x582230[_0x3250cc(0x152)],_0x51f743,{'filename':_0x12ec02});}return{};};return{'hook':_0x13bca8,'uploadedFiles':_0x45feb1};}async function runVerification(_0xbcc106,_0x3090f2){const _0x275026=_0x2f4b72,_0x47751d={'uYAZU':'closed','XOyow':function(_0x183f01,_0x2bf22c){return _0x183f01===_0x2bf22c;},'Otebu':_0x275026(0x13d),'UXlVk':_0x275026(0x189),'zRzFe':function(_0x953744,_0x2898e8){return _0x953744+_0x2898e8;},'nuuJQ':'en-US','zmpKH':'numeric','BZVmV':'Reviewer','visVX':function(_0x5f3056,_0x415a5e,_0x1391cd){return _0x5f3056(_0x415a5e,_0x1391cd);},'ZrzsB':'utf-8','CNVjS':'StructuredOutput','UJexk':'result','FfPBA':_0x275026(0xdf),'rtEqX':function(_0x3b7005,_0x2c71d8){return _0x3b7005!==_0x2c71d8;},'hHjab':_0x275026(0x193),'zLeXy':_0x275026(0x16d),'fLsae':'No\x20active\x20feature\x20review.\x20Run:\x20ranger\x20resume\x20<id>\x20or\x20ranger\x20create','eurLk':function(_0x2ba8fe){return _0x2ba8fe();},'YkVoD':function(_0x134f77,_0x149ab1){return _0x134f77(_0x149ab1);},'agaUf':_0x275026(0x1cf),'GZngn':function(_0x1a9bce,_0x4505e4){return _0x1a9bce-_0x4505e4;},'xnXvV':function(_0x4c6d8f,_0x49b6cf){return _0x4c6d8f<_0x49b6cf;},'agoHn':function(_0x5c53e6,_0x598c84){return _0x5c53e6>=_0x598c84;},'EwDDe':'not\x20actionable','oGHMH':_0x275026(0x1df),'shRnw':'\x0aAvailable\x20scenarios\x20to\x20verify:','QAvhV':function(_0x477472,_0x603f42){return _0x477472(_0x603f42);},'aJYDw':_0x275026(0x1ed),'cjvAP':function(_0x2da316,_0x1a1863){return _0x2da316===_0x1a1863;},'RyrAQ':'unknown\x20reason','caOyn':'No\x20notes\x20provided','nMySK':function(_0x31dbdd,_0x81f40a){return _0x31dbdd!==_0x81f40a;},'KbRPg':_0x275026(0xee),'zEZIt':function(_0x2d8fcf,_0x212f1b,_0x444e7d){return _0x2d8fcf(_0x212f1b,_0x444e7d);},'jpNZS':function(_0x5264a3,_0x4aa20e){return _0x5264a3>_0x4aa20e;},'EdeDs':function(_0x6e9a48,_0xdef74c){return _0x6e9a48===_0xdef74c;},'OVAIS':'already','aUoKE':_0x275026(0x185),'AOXgh':function(_0x1853a9,_0x1e913c){return _0x1853a9(_0x1e913c);},'UMGsY':function(_0x5c122b,_0x552539){return _0x5c122b(_0x552539);},'VhMUA':function(_0x5f4db3,_0x3c01dd){return _0x5f4db3(_0x3c01dd);},'bGJXJ':function(_0x37e374,_0x6e29f7){return _0x37e374&&_0x6e29f7;},'jFwzf':function(_0x5055fb,_0x35a6c7){return _0x5055fb+_0x35a6c7;},'TXOMr':function(_0x311b87){return _0x311b87();},'tsMDV':'No\x20API\x20token\x20configured.\x20Run:\x20ranger\x20setup\x20[token]','cuxUj':function(_0x14f263){return _0x14f263();},'AqIFH':function(_0x47e152,_0x3f1e24){return _0x47e152(_0x3f1e24);},'PmEGy':_0x275026(0x1bf),'SPNkA':_0x275026(0x154),'BVcZh':function(_0x5bd365,_0x2ebbe8,_0x457e06,_0x2a64bc){return _0x5bd365(_0x2ebbe8,_0x457e06,_0x2a64bc);},'cXwYy':_0x275026(0x114),'lKAhp':'@ranger-testing/playwright','oNTbz':'For\x20debug\x20purposes,\x20mark\x20ALL\x20of\x20the\x20following\x20comments\x20as\x20addressed\x20by\x20including\x20every\x20ID\x20in\x20addressedCommentIds.','RcXSu':function(_0x2b51a5){return _0x2b51a5();},'sALko':_0x275026(0x179),'fnLQy':_0x275026(0x1f6),'Bbida':'BLOCKER','YLRne':'MAJOR','eDBIY':'MINOR','BnJxb':_0x275026(0x1b5),'fbmqe':'HTTP_400','UecJK':'OTHER','Jhlun':_0x275026(0x16b),'XvoSQ':_0x275026(0xfe),'PUpgB':_0x275026(0x138),'LBSiC':_0x275026(0x1f8),'IrlhS':'evaluation','fSblB':function(_0x208bdb,_0x515b12){return _0x208bdb(_0x515b12);},'mGuJx':function(_0x4b1f72,_0x1708e4){return _0x4b1f72(_0x1708e4);},'CjePt':'acceptEdits','vvLLM':'mcp__ranger-browser__*','oJbbu':'Grep','xvPDH':function(_0x13dd84,_0x5a6276){return _0x13dd84*_0x5a6276;},'cSEGx':function(_0x3c957c,_0x2f4a99){return _0x3c957c instanceof _0x2f4a99;},'BaByC':function(_0x270b20,_0x32959f){return _0x270b20(_0x32959f);}},_0x576675=!!_0xbcc106['debugOutcome'];_0x576675&&console[_0x275026(0xe0)](_0x275026(0x1ff)+_0xbcc106[_0x275026(0x103)]);_0x3090f2['trackPhaseStart'](_0x47751d['zLeXy']);const _0x344e06=await getActiveFeatureId();if(!_0x344e06)throw new Error(_0x47751d[_0x275026(0x157)]);const _0x3773b5=await getFeature(_0x344e06);_0x3090f2['setContext']({'featureId':_0x344e06});const _0x3239bb=_0x47751d[_0x275026(0x1e7)](getGitBranch);_0x3239bb&&_0x47751d['rtEqX'](_0x3239bb,_0x3773b5['gitBranch'])&&(await updateFeature(_0x344e06,{'gitBranch':_0x3239bb}),console[_0x275026(0xe0)]('\x20\x20\x20Updated\x20branch\x20to:\x20'+_0x3239bb));console['log']('\x0aActive\x20feature\x20review:\x20'+_0x3773b5['name']+'\x20('+_0x3773b5[_0x275026(0x197)]+')');const {items:_0x1b11af}=await _0x47751d[_0x275026(0x1f3)](getActionItems,_0x344e06),_0x1e2457=new Map(_0x1b11af['map'](_0x4c902d=>[_0x4c902d['id'],_0x4c902d])),_0x5d36c6=_0x3773b5[_0x275026(0x136)]['map']((_0x28111d,_0x44fe75)=>{const _0x4a9a39=_0x275026,_0x35572d=_0x1e2457[_0x4a9a39(0x1d9)](_0x28111d['id']);return{..._0x28111d,'unaddressedCommentCount':_0x35572d?.['unaddressedCommentCount']??0x0,'displayIndex':_0x44fe75,'actionable':!!_0x35572d&&_0x28111d[_0x4a9a39(0x139)]!==_0x47751d['uYAZU']};});_0x3090f2['trackPhaseEnd'](_0x47751d[_0x275026(0x205)],{'itemCount':_0x1b11af[_0x275026(0x153)]}),_0x3090f2[_0x275026(0x15a)](_0x47751d[_0x275026(0x168)]);let _0x55d8d0=null,_0x4be31f=_0xbcc106[_0x275026(0x19a)];if(_0x47751d[_0x275026(0x167)](_0xbcc106['scenario'],undefined)){const _0x3ce690=_0x47751d['GZngn'](_0xbcc106['scenario'],0x1);if(_0x47751d['xnXvV'](_0x3ce690,0x0)||_0x47751d[_0x275026(0x20b)](_0x3ce690,_0x5d36c6[_0x275026(0x153)]))throw new Error(_0x275026(0x186)+_0xbcc106[_0x275026(0x11a)]+'.\x20Feature\x20review\x20has\x20'+_0x5d36c6[_0x275026(0x153)]+'\x20scenarios.');const _0x198da7=_0x5d36c6[_0x3ce690];if(!_0x198da7[_0x275026(0x1c6)]){const _0x106959=_0x198da7['status']===_0x47751d[_0x275026(0x163)]&&_0x198da7['terminalReason']?_0x198da7['terminalReason']:_0x47751d[_0x275026(0x1eb)];throw new Error(_0x275026(0x1fa)+_0xbcc106['scenario']+'\x20is\x20'+_0x106959+_0x275026(0x15c));}const _0x5f1e5a=_0x1e2457['get'](_0x198da7['id']);if(!_0x5f1e5a)throw new Error(_0x275026(0x1fa)+_0xbcc106['scenario']+'\x20is\x20not\x20currently\x20actionable.\x20Try\x20another\x20scenario.');_0x55d8d0=_0x5f1e5a,!_0x4be31f&&(_0x4be31f=_0x55d8d0['description']);}else{const _0x538bff=process['stdin']['isTTY']&&process['stdout']['isTTY'];if(!_0x538bff){console['log'](_0x47751d['oGHMH']),console['log'](_0x47751d['shRnw']),_0x5d36c6[_0x275026(0x1e3)](_0x140c3a=>{const _0x3bd4e1=_0x275026,_0x5b53bb=_0x140c3a[_0x3bd4e1(0x139)]===_0x47751d[_0x3bd4e1(0x163)]&&_0x140c3a[_0x3bd4e1(0x1da)]===_0x3bd4e1(0x159)?'✅':_0x47751d[_0x3bd4e1(0x212)](_0x140c3a[_0x3bd4e1(0x139)],_0x47751d[_0x3bd4e1(0xef)])?'🟢':_0x47751d['XOyow'](_0x140c3a[_0x3bd4e1(0x139)],_0x3bd4e1(0x1de))?'🟠':_0x47751d[_0x3bd4e1(0x212)](_0x140c3a[_0x3bd4e1(0x139)],'blocked')?'🛑':_0x140c3a[_0x3bd4e1(0x139)]===_0x3bd4e1(0xff)?'⛔':_0x47751d['XOyow'](_0x140c3a['status'],'verification_in_progress')?'⏳':'⬜',_0x4a2e77=_0x140c3a['unaddressedCommentCount']>0x0?'\x20['+_0x140c3a[_0x3bd4e1(0x132)]+'\x20comments]':'',_0x52f226=_0x140c3a[_0x3bd4e1(0x1c6)]?'':_0x47751d['UXlVk'];console['log']('\x20\x20'+_0x47751d['zRzFe'](_0x140c3a['displayIndex'],0x1)+'.\x20'+_0x5b53bb+'\x20'+_0x140c3a['description']+_0x4a2e77+_0x52f226);}),console[_0x275026(0xe0)](_0x275026(0x213)),console['log']('Example:\x20ranger\x20go\x20--scenario\x201');throw new Error(_0x275026(0x12b));}const _0x2832bd=await _0x47751d[_0x275026(0x13f)](selectChecklistItem,_0x5d36c6);if(_0x2832bd){const _0x3575f9=_0x1e2457['get'](_0x2832bd['id']);if(!_0x3575f9)throw new Error(_0x47751d[_0x275026(0x1b8)]);_0x55d8d0=_0x3575f9,!_0x4be31f&&(_0x4be31f=_0x55d8d0['description']);}}if(!_0x55d8d0)throw new Error('No\x20scenario\x20selected.\x20Create\x20scenarios\x20when\x20creating\x20the\x20feature\x20review\x20with\x20-c\x20or\x20--scenario\x20flags.');if(_0x47751d['cjvAP'](_0x55d8d0['status'],_0x47751d['uYAZU']))throw new Error(_0x275026(0x166)+_0x55d8d0['description']+'\x22\x20—\x20it\x20is\x20concluded\x20('+(_0x55d8d0[_0x275026(0x1da)]||_0x47751d['RyrAQ'])+').');if(!_0x4be31f)throw new Error(_0x47751d['caOyn']);_0x3090f2[_0x275026(0xe7)]({'checklistItemId':_0x55d8d0['id']}),_0x3090f2[_0x275026(0xf7)](_0x275026(0x1cf),{'selectionMethod':_0x47751d['nMySK'](_0xbcc106['scenario'],undefined)?'flag':_0x47751d['KbRPg'],'itemStatus':_0x55d8d0[_0x275026(0x139)]}),console['log']('\x0aVerifying\x20scenario:\x20'+_0x55d8d0[_0x275026(0xfe)]),console['log'](_0x275026(0xf5)+_0x4be31f);let _0x533ffd=null;if(_0x55d8d0[_0x275026(0x21a)]||_0x55d8d0['unaddressedCommentCount']>0x0)try{_0x533ffd=await _0x47751d['zEZIt'](getItemFeedback,_0x344e06,_0x55d8d0['id']),_0x47751d['jpNZS'](_0x533ffd['unaddressedComments'][_0x275026(0x153)],0x0)&&console['log'](_0x275026(0xda)+_0x533ffd[_0x275026(0x144)][_0x275026(0x153)]+'\x20comment(s)\x20to\x20verify');}catch(_0x247e58){await _0x3090f2[_0x275026(0x1dc)](_0x275026(0x1a0),_0x247e58);}if(_0x3773b5[_0x275026(0xf9)]&&_0x47751d[_0x275026(0x105)](_0x3773b5[_0x275026(0xf9)]['status'],'ready')&&_0x3773b5[_0x275026(0xe8)])try{await _0x47751d[_0x275026(0x14f)](startSession,_0x344e06,_0x3773b5[_0x275026(0xe8)]);}catch(_0x40f1c8){const _0x4cabba=_0x40f1c8 instanceof Error?_0x40f1c8[_0x275026(0x1ee)]:String(_0x40f1c8);if(!_0x4cabba[_0x275026(0x119)](_0x47751d['OVAIS']))throw _0x40f1c8;}await updateChecklistItem(_0x344e06,_0x55d8d0['id'],{'status':'verification_in_progress'}),_0x3090f2[_0x275026(0x15a)](_0x47751d[_0x275026(0x1be)]);let _0x2c2c0a=null;_0xbcc106['profile']?_0x2c2c0a=_0xbcc106['profile']:_0x2c2c0a=await readActiveProfileName();if(!_0x2c2c0a)throw new Error(_0x275026(0x158));const _0x998c91=_0x47751d['AOXgh'](getEnvDir,_0x2c2c0a);if(!existsSync(_0x998c91))throw new Error('Profile\x20\x22'+_0x2c2c0a+_0x275026(0x1b0)+_0x2c2c0a);const _0x1f97d5=await loadSettings(_0x2c2c0a),_0x51b8bb=_0x47751d['UMGsY'](resolveEnvVars,_0x1f97d5),_0x58ba36=_0x47751d['VhMUA'](Boolean,_0x51b8bb['cdpEndpoint']);let _0x528006=_0x51b8bb[_0x275026(0x18b)];if(_0x47751d['bGJXJ'](!_0x528006,!_0x58ba36))throw new Error(_0x275026(0x1c8)+_0x2c2c0a+_0x275026(0x15b)+_0x2c2c0a+'\x20baseUrl\x20<url>');if(!_0x58ba36&&_0xbcc106[_0x275026(0x218)]&&_0x528006){const _0x18955f=_0x528006[_0x275026(0x1b2)]('/')?_0x528006[_0x275026(0x1ce)](0x0,-0x1):_0x528006,_0x4f314a=_0xbcc106['startPath']['startsWith']('/')?_0xbcc106[_0x275026(0x218)]:'/'+_0xbcc106['startPath'];_0x528006=_0x47751d[_0x275026(0x21d)](_0x18955f,_0x4f314a);}_0x3090f2[_0x275026(0xf7)](_0x47751d['aUoKE'],{'profileName':_0x2c2c0a}),_0x3090f2[_0x275026(0x15a)]('browser_session_create');const _0x554dd0=await _0x47751d[_0x275026(0x1ea)](getToken);if(!_0x554dd0)throw new Error(_0x47751d[_0x275026(0x12f)]);const _0x4e6efa=await createBrowserSession({'environmentName':_0x2c2c0a,'settings':_0x51b8bb,'task':_0x4be31f,'url':_0x528006,'featureId':_0x344e06,'checklistItemId':_0x55d8d0['id']});console[_0x275026(0xe0)]('Browser\x20session\x20created:\x20'+_0x4e6efa['id']),_0x3090f2['setContext']({'browserSessionId':_0x4e6efa['id']}),await updateChecklistItem(_0x344e06,_0x55d8d0['id'],{'browserSessionId':_0x4e6efa['id']}),_0x3090f2['trackPhaseEnd']('browser_session_create'),_0x3090f2[_0x275026(0x15a)]('playwright_config');let _0x5325e4,_0x215c45;try{_0x215c45=await _0x47751d[_0x275026(0x149)](getProxySessionToken);}catch(_0x542e86){const _0x162ca9=_0x542e86 instanceof Error?_0x542e86['message']:_0x47751d['AqIFH'](String,_0x542e86),_0x14979f='Failed\x20to\x20fetch\x20proxy\x20session\x20token:\x20'+_0x162ca9;try{await updateBrowserSession(_0x4e6efa['id'],{'status':_0x47751d[_0x275026(0x127)],'durationMs':0x0,'errorMessage':_0x14979f});}catch(_0x5dd925){await _0x3090f2[_0x275026(0x1dc)](_0x47751d['SPNkA'],_0x5dd925);}throw new Error(_0x14979f);}_0x5325e4=await _0x47751d[_0x275026(0x1b4)](buildPlaywrightConfig,_0x51b8bb,_0x2c2c0a,_0x4e6efa?.['id']),_0x3090f2[_0x275026(0xf7)](_0x47751d[_0x275026(0x126)]);const _0x362334=Date['now'](),_0x2aeaea={'command':'npx','args':[_0x47751d[_0x275026(0xfc)],_0x275026(0xe3),_0x275026(0x15f),_0x5325e4['configPath']]};let _0x465faf;if(_0x576675){let _0x341ef4='';if(_0x533ffd&&_0x533ffd['unaddressedComments'][_0x275026(0x153)]>0x0){const _0x304db4=_0x533ffd['unaddressedComments']['map'](_0x2952a3=>_0x275026(0x1ec)+_0x2952a3['id']+_0x275026(0x1e2)+_0x2952a3['content']+'\x22')['join']('\x0a'),_0x339575=_0xbcc106['debugAddressComments']?_0x47751d['oNTbz']:'For\x20debug\x20purposes,\x20do\x20NOT\x20mark\x20any\x20comments\x20as\x20addressed.\x20Return\x20an\x20empty\x20addressedCommentIds\x20array.';_0x341ef4=_0x275026(0xf1)+_0x339575+'\x0a\x0a'+_0x304db4;}_0x465faf=_0x47751d['RcXSu'](getDebugPrompt)+_0x341ef4;}else{const _0x3e0309=_0x55d8d0[_0x275026(0x19a)]?'\x0a\x0a##\x20Additional\x20Notes\x0a'+_0x55d8d0[_0x275026(0x19a)]:'';let _0x581e76='';if(_0x533ffd&&_0x533ffd[_0x275026(0x144)][_0x275026(0x153)]>0x0){const _0x3932f4=_0x533ffd['unaddressedComments'][_0x275026(0x1c7)](_0x3e0831=>{const _0x97ffec=_0x275026,_0x28c613=new Date(_0x3e0831['createdAt'])[_0x97ffec(0x14e)](_0x47751d['nuuJQ'],{'month':_0x97ffec(0x115),'day':_0x47751d['zmpKH']}),_0x1706b0=_0x3e0831['authorName']||_0x3e0831['authorEmail']||_0x47751d[_0x97ffec(0x1fc)];return _0x97ffec(0x1ec)+_0x3e0831['id']+']\x20**'+_0x1706b0+_0x97ffec(0x1e6)+_0x28c613+'):\x20\x22'+_0x3e0831[_0x97ffec(0x155)]+'\x22';})[_0x275026(0x14b)]('\x0a');_0x581e76=_0x275026(0x160)+_0x3932f4;}let _0x10b075='';_0x533ffd?.[_0x275026(0x12e)]&&(_0x10b075=_0x275026(0x130)+_0x533ffd['canonicalFlow']);const _0xc60db2=_0x58ba36?'##\x20App\x20Context\x0aYou\x20are\x20connected\x20to\x20a\x20desktop\x20application\x20via\x20CDP\x20(Chrome\x20DevTools\x20Protocol)\x20at\x20'+_0x51b8bb[_0x275026(0x207)]+_0x275026(0x219):'CRITICAL\x20URL\x20REQUIREMENT:\x0aYour\x20base\x20URL\x20is:\x20'+_0x528006+_0x275026(0x217),_0x4ac052=_0x58ba36?'##\x20Instructions\x0a1.\x20Take\x20a\x20snapshot\x20with\x20browser_snapshot\x20to\x20see\x20the\x20current\x20state\x20of\x20the\x20app\x0a2.\x20Execute\x20the\x20task\x20step-by-step\x20using\x20browser\x20tools\x20(click,\x20type,\x20etc.)\x0a3.\x20**Take\x20screenshots\x20at\x20key\x20moments**\x20(see\x20Screenshot\x20Guidelines\x20below)\x0a4.\x20Document\x20any\x20issues\x20found\x20(bugs,\x20errors,\x20unexpected\x20behavior)\x0a5.\x20After\x20completing\x20the\x20verification,\x20evaluate\x20whether\x20the\x20result\x20adequately\x20verifies\x20the\x20scenario':_0x275026(0x1b7),_0x1824b1=_0x58ba36?'\x0a\x0a##\x20Critical:\x20Early\x20Error\x20Detection\x0aAfter\x20taking\x20the\x20initial\x20snapshot,\x20IMMEDIATELY\x20check\x20for\x20blocking\x20errors:\x0a\x0a**Blocking\x20errors\x20to\x20detect:**\x0a-\x20Crash\x20or\x20error\x20dialogs\x20(\x22An\x20error\x20occurred\x22,\x20\x22Something\x20went\x20wrong\x22,\x20unhandled\x20exception\x20messages)\x0a-\x20Blank\x20or\x20empty\x20window\x20with\x20no\x20rendered\x20content\x0a-\x20Framework\x20error\x20boundaries\x20(React\x20error\x20page,\x20Electron\x20crash\x20reporter)\x0a-\x20Unresponsive\x20or\x20frozen\x20UI\x20(no\x20interactive\x20elements\x20in\x20snapshot)\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\x22App\x20crashed\x20—\x20error\x20dialog\x20visible\x20on\x20launch\x22)\x0a4.\x20Include\x20the\x20error\x20in\x20issues\x20array\x20with\x20severity:\x20\x22BLOCKER\x22\x20and\x20type:\x20\x22APP_ERROR\x22\x0a\x0aThis\x20early\x20exit\x20prevents\x20wasting\x20time\x20on\x20tasks\x20that\x20cannot\x20succeed\x20due\x20to\x20fundamental\x20errors.':'\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.';_0x465faf=_0x275026(0x147)+_0x55d8d0['description']+_0x3e0309+_0x581e76+_0x10b075+'\x0a\x0a##\x20Task\x20to\x20Execute\x0a'+_0x4be31f+'\x0a\x0a'+_0xc60db2+'\x0a\x0a'+_0x4ac052+_0x275026(0x20a)+_0x1824b1+'\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.';}const _0x39d7ef={'type':_0x275026(0x113),'properties':{'success':{'type':_0x275026(0xd7)},'summary':{'type':'string'},'evaluation':{'type':_0x47751d[_0x275026(0x1d5)],'enum':['verified','partial',_0x47751d[_0x275026(0x1a6)],_0x275026(0x1bf)]},'evaluationReason':{'type':_0x275026(0x179)},'issues':{'type':'array','items':{'type':_0x275026(0x113),'properties':{'severity':{'type':_0x47751d[_0x275026(0x1d5)],'enum':[_0x47751d['Bbida'],_0x47751d[_0x275026(0x1a9)],_0x47751d['eDBIY']]},'type':{'type':_0x47751d['sALko'],'enum':[_0x275026(0x1c4),_0x47751d[_0x275026(0xf4)],_0x47751d[_0x275026(0x111)],'NAVIGATION_ERROR',_0x47751d['UecJK']]},'description':{'type':_0x47751d[_0x275026(0x1d5)]},'screenshot':{'type':'string'}},'required':[_0x47751d['Jhlun'],_0x47751d[_0x275026(0x109)]]}},'addressedCommentIds':{'type':_0x47751d['PUpgB'],'description':_0x275026(0x1ab),'items':{'type':_0x47751d['sALko']}}},'required':['success',_0x47751d[_0x275026(0xe4)],_0x47751d[_0x275026(0xe9)],_0x275026(0x203)]};_0x3090f2['trackPhaseStart'](_0x275026(0x187));const _0x256f50=_0x47751d[_0x275026(0x14a)](getTraceDirectory,_0x4e6efa['id']),_0x55f76d=createScreenshotUploadHook(_0x4e6efa['id'],_0x55d8d0['id'],_0x256f50,_0x3090f2),_0x152d75=_0x47751d[_0x275026(0x14a)](createToolCallTrackingHook,_0x3090f2),_0x673639=_0x47751d['AOXgh'](createToolFailureHook,_0x3090f2),_0x1372dc=_0x47751d[_0x275026(0x1c0)](query,{'prompt':_0x465faf,'options':{'cwd':process['cwd'](),'model':'claude-opus-4-6','mcpServers':{'ranger-browser':_0x2aeaea},'tools':['mcp__ranger-browser__*'],'permissionMode':_0x47751d['CjePt'],'allowedTools':[_0x47751d['vvLLM'],'Read','Glob',_0x47751d['oJbbu']],'outputFormat':{'type':'json_schema','schema':_0x39d7ef},'hooks':{'PostToolUse':[{'hooks':[_0x152d75[_0x275026(0x1d3)],_0x55f76d[_0x275026(0x1d3)]]}],'PostToolUseFailure':[{'hooks':[_0x673639]}]},'env':{...process[_0x275026(0x18e)],'ANTHROPIC_API_KEY':_0x215c45,'ANTHROPIC_BASE_URL':getAiProxyUrl()},'persistSession':![]}});let _0x772f42=null,_0x52a704=null,_0x4549af=null,_0x1ca3d1={};const _0x43d0e2=_0x47751d['mGuJx'](getConversationFilePath,_0x4e6efa['id']),_0x2e3871=dirname(_0x43d0e2);await _0x47751d['visVX'](mkdir,_0x2e3871,{'recursive':!![]});const _0x118e6a=_0x47751d[_0x275026(0xdd)](0x3b*0x3c,0x3e8),_0x539a53=new Promise((_0x31e28f,_0xbb1d80)=>{const _0x4b1ea3={'YieCG':'Agent\x20execution\x20timed\x20out\x20after\x2059\x20minutes'};_0x47751d['visVX'](setTimeout,()=>{const _0x253cfb=_0x38a4;_0xbb1d80(new Error(_0x4b1ea3[_0x253cfb(0x18d)]));},_0x118e6a);});try{await Promise['race']([((async()=>{const _0x4cdd40=_0x275026;for await(const _0x2731d0 of _0x1372dc){try{const _0x1fa792=JSON[_0x4cdd40(0x112)](_0x2731d0)+'\x0a';await appendFile(_0x43d0e2,_0x1fa792,_0x47751d[_0x4cdd40(0x1dd)]);}catch{}const _0x49672d=_0x2731d0;if(_0x49672d['type']===_0x4cdd40(0x183)&&_0x49672d['message']?.['content'])for(const _0x334a44 of _0x49672d['message'][_0x4cdd40(0x155)]){_0x334a44['type']===_0x4cdd40(0x14c)&&_0x47751d['XOyow'](_0x334a44['name'],_0x47751d['CNVjS'])&&_0x334a44['input']&&(_0x4549af=_0x334a44[_0x4cdd40(0x188)]);}if(_0x49672d['error']){let _0x1c343e=_0x49672d[_0x4cdd40(0x10d)];if(_0x49672d[_0x4cdd40(0x1ee)]?.['content']&&Array[_0x4cdd40(0x182)](_0x49672d[_0x4cdd40(0x1ee)]['content'])){const _0x5cfef8=_0x49672d['message']['content']['filter'](_0x4a91d9=>_0x4a91d9[_0x4cdd40(0x1fd)]==='text')[_0x4cdd40(0x1c7)](_0x3f35e1=>_0x3f35e1['text']||'')['filter'](Boolean);_0x5cfef8[_0x4cdd40(0x153)]>0x0&&(_0x1c343e=_0x5cfef8['join']('\x20'));}_0x52a704=_0x1c343e;}if(_0x47751d['XOyow'](_0x49672d['type'],_0x47751d[_0x4cdd40(0x1c9)])){_0x1ca3d1={'numTurns':_0x49672d[_0x4cdd40(0x196)],'totalCostUsd':_0x49672d['total_cost_usd'],'durationApiMs':_0x49672d[_0x4cdd40(0x1b1)],'sdkDurationMs':_0x49672d[_0x4cdd40(0x1a7)],'inputTokens':_0x49672d[_0x4cdd40(0x1d4)]?.['input_tokens'],'outputTokens':_0x49672d[_0x4cdd40(0x1d4)]?.['output_tokens'],'cacheReadTokens':_0x49672d[_0x4cdd40(0x1d4)]?.['cache_read_input_tokens'],'cacheCreationTokens':_0x49672d['usage']?.[_0x4cdd40(0x199)]};if(_0x49672d['subtype']===_0x47751d[_0x4cdd40(0x1e5)]&&_0x2731d0[_0x4cdd40(0x18a)])_0x772f42=_0x2731d0['structured_output'];else{if(_0x47751d[_0x4cdd40(0x167)](_0x49672d['subtype'],_0x47751d[_0x4cdd40(0x1e5)])){if(_0x4549af&&_0x47751d['XOyow'](_0x49672d['errors']?.[_0x4cdd40(0x153)],0x0))_0x772f42=_0x4549af,_0x52a704=null;else!_0x52a704&&(_0x52a704=_0x49672d['errors']?.['join'](',\x20')||_0x47751d[_0x4cdd40(0x210)]);}}}}})()),_0x539a53]);}catch(_0xb9e413){_0x52a704=_0x47751d[_0x275026(0x15e)](_0xb9e413,Error)?_0xb9e413['message']:_0x47751d[_0x275026(0x13b)](String,_0xb9e413);}const _0x64a7f2=_0x47751d[_0x275026(0x10e)](Date[_0x275026(0x125)](),_0x362334);return _0x3090f2['trackPhaseEnd'](_0x275026(0x187),{..._0x1ca3d1,'toolCallCounts':Object['fromEntries'](_0x152d75['toolCallCounts']),'hasResult':!!_0x772f42,'hasError':!!_0x52a704}),{'featureId':_0x344e06,'checklistItem':_0x55d8d0,'browserSession':_0x4e6efa,'finalResult':_0x772f42,'agentError':_0x52a704,'lastStructuredOutputInput':_0x4549af,'screenshotHook':_0x55f76d,'toolCallCounts':_0x152d75[_0x275026(0x1a3)],'configResult':_0x5325e4,'startTime':_0x362334,'durationMs':_0x64a7f2,'conversationFilePath':_0x43d0e2,'conversationDir':_0x2e3871,'isDebugMode':_0x576675,'debugOutcome':_0xbcc106[_0x275026(0x103)],'debugAddressComments':_0xbcc106['debugAddressComments'],'resultMeta':_0x1ca3d1,'telemetry':_0x3090f2,'feedbackCommentIds':_0x533ffd?_0x533ffd['unaddressedComments']['map'](_0x365858=>_0x365858['id']):[]};}async function processVerificationResult(_0x8c9c15){const _0x1be50b=_0x2f4b72,_0x2882c5={'yMXVY':function(_0x8b65b3,_0x102e64){return _0x8b65b3(_0x102e64);},'zoyCg':'upload_trace','ROTlK':'trace.zip','NmXSx':function(_0x442cf9,_0x46c7ed,_0x47813d){return _0x442cf9(_0x46c7ed,_0x47813d);},'oxncg':function(_0x35f87d,_0x3d7a9e,_0x862994,_0x292240){return _0x35f87d(_0x3d7a9e,_0x862994,_0x292240);},'qNOKq':'webm','JSwQU':'upload_video','Tuwlo':function(_0x5c4ae9,_0x1d1bb2){return _0x5c4ae9+_0x1d1bb2;},'sIhvN':function(_0x3452d4,_0x496eee){return _0x3452d4<_0x496eee;},'DUZzI':_0x1be50b(0x201),'TVrHF':function(_0x329277,_0x145d90,_0x261ce2){return _0x329277(_0x145d90,_0x261ce2);},'nXHCJ':'screenshot','qCFCj':_0x1be50b(0x1ad),'eQSuh':_0x1be50b(0x1d6),'YlFjE':_0x1be50b(0xdf),'VoQKt':_0x1be50b(0xfb),'zgWOy':function(_0x17263f,_0xccf5f4){return _0x17263f(_0xccf5f4);},'cijAF':'jsonl','PiGjG':_0x1be50b(0x1d8),'SRzSs':'update_session','jBaQF':'completed','PmQsJ':function(_0x46e8ee,_0x21f0b8){return _0x46e8ee||_0x21f0b8;},'DdtbW':function(_0x30a4ae,_0x263b95){return _0x30a4ae&&_0x263b95;},'IpvwC':function(_0x5c743b,_0x34590b){return _0x5c743b(_0x34590b);},'sQPsN':function(_0x4eb882,_0x1ff946){return _0x4eb882&&_0x1ff946;},'dPjjF':function(_0x548507,_0x4004e5){return _0x548507(_0x4004e5);},'GNfTU':function(_0x581b7e,_0x4c4570){return _0x581b7e&&_0x4c4570;},'bmSke':'evaluation','anSfo':'verified','GSqAk':function(_0xdf062d,_0x1382b0){return _0xdf062d===_0x1382b0;},'loRWf':function(_0x190e22,_0x1199c2,_0x2f3935,_0x41bd38){return _0x190e22(_0x1199c2,_0x2f3935,_0x41bd38);},'BxeMH':_0x1be50b(0x1f6),'Euiwl':function(_0x9fd0ad,_0x4d7f2c){return _0x9fd0ad===_0x4d7f2c;},'KzRmQ':_0x1be50b(0x1bf),'mzkEf':function(_0x33626b,_0x261eeb){return _0x33626b===_0x261eeb;},'MWTux':_0x1be50b(0x1de),'XjcyB':function(_0x8996e7,_0x2a10aa,_0x320238,_0x5d98f7){return _0x8996e7(_0x2a10aa,_0x320238,_0x5d98f7);},'hlBqE':'scenario_update'},{featureId:_0x5c1857,checklistItem:_0x331108,browserSession:_0x56ba3e,screenshotHook:_0x1766f6,durationMs:_0x94ecdc,conversationFilePath:_0x2134dc,isDebugMode:_0x3e9698,debugOutcome:_0x4eeed7,debugAddressComments:_0x262665,telemetry:_0x1c9e74,feedbackCommentIds:_0x5d0dd6}=_0x8c9c15,{finalResult:_0xd67d80,agentError:_0x607cd7}=_0x8c9c15;let _0x34b8e1;try{const _0x1daba4=_0x2882c5[_0x1be50b(0x1f0)](getTraceDirectory,_0x56ba3e['id']);if(existsSync(_0x1daba4)){const _0x199e3a=await _0x2882c5[_0x1be50b(0x1f0)](readdir,_0x1daba4);if(_0x199e3a['length']>0x0){_0x1c9e74['trackPhaseStart'](_0x2882c5[_0x1be50b(0x107)]);try{const _0x55fb41=await getUploadUrls(_0x56ba3e['id'],_0x2882c5['ROTlK'],'zip'),_0x5cd0fb=await _0x2882c5[_0x1be50b(0x1f0)](zipDirectory,_0x1daba4);await _0x2882c5['NmXSx'](uploadTrace,_0x55fb41[_0x1be50b(0x12a)],_0x5cd0fb),_0x34b8e1=_0x55fb41['downloadUrl'],_0x1c9e74['trackPhaseEnd']('upload_trace',{'bytes':_0x5cd0fb[_0x1be50b(0x153)]});}catch(_0x48563a){await _0x1c9e74['trackPhaseError'](_0x2882c5['zoyCg'],_0x48563a);}const _0x4b3009=await loadSessionVideos(_0x1daba4);for(const _0x16efdf of _0x4b3009){_0x1c9e74[_0x1be50b(0x15a)]('upload_video',{'filename':_0x16efdf['filename']});try{try{fixWebmDuration(_0x16efdf[_0x1be50b(0x206)],_0x16efdf[_0x1be50b(0x206)]);}catch{}const _0x248489=await readFile(_0x16efdf['path']),_0x1f4ce2=await _0x2882c5[_0x1be50b(0x1f1)](getUploadUrls,_0x56ba3e['id'],_0x16efdf['filename'],_0x2882c5[_0x1be50b(0x1c2)]);await uploadVideo(_0x1f4ce2['uploadUrl'],_0x248489),_0x1c9e74['trackPhaseEnd'](_0x2882c5[_0x1be50b(0x200)],{'filename':_0x16efdf['filename'],'bytes':_0x248489['length']});}catch(_0x156398){await _0x1c9e74['trackPhaseError'](_0x1be50b(0x1db),_0x156398,{'filename':_0x16efdf[_0x1be50b(0x162)]});}}const _0x228b0a=_0x199e3a[_0x1be50b(0x129)](_0x446324=>_0x446324[_0x1be50b(0xd3)]()['endsWith'](_0x1be50b(0x19b)))[_0x1be50b(0x129)](_0x1bc4eb=>!_0x1766f6['uploadedFiles']['has'](_0x1bc4eb))['sort'](),_0x42a6e8=_0x2882c5['Tuwlo'](_0x1766f6[_0x1be50b(0x146)][_0x1be50b(0x19e)],0x1);for(let _0xbddaa6=0x0;_0x2882c5[_0x1be50b(0x204)](_0xbddaa6,_0x228b0a['length']);_0xbddaa6++){const _0x68a9ff=_0x228b0a[_0xbddaa6],_0x2c1689=_0x68a9ff['toLowerCase']()['startsWith'](_0x1be50b(0x10b));_0x1c9e74[_0x1be50b(0x15a)](_0x2882c5['DUZzI'],{'filename':_0x68a9ff,'isKeyFrame':_0x2c1689});try{const _0x2b9677=join(_0x1daba4,_0x68a9ff),_0x11dc22=await _0x2882c5['yMXVY'](readFile,_0x2b9677),_0xcd0bf4=await _0x2882c5[_0x1be50b(0x1f0)](stat,_0x2b9677),_0xb8fa94=_0x68a9ff['replace'](/\.png$/i,'')[_0x1be50b(0xd5)](/^key_/i,'')[_0x1be50b(0xd5)](/^\d+_/,'')['replace'](/-/g,'\x20'),{step:_0x559478}=await _0x2882c5[_0x1be50b(0x175)](createVerificationStep,_0x56ba3e['id'],{'checklistItemId':_0x331108['id'],'position':_0x42a6e8+_0xbddaa6,'stepType':_0x2882c5[_0x1be50b(0x20c)],'stepName':_0xb8fa94,'description':_0x2c1689?_0x2882c5['qCFCj']:_0x2882c5[_0x1be50b(0x17e)],'isKeyStep':_0x2c1689,'status':_0x2882c5['YlFjE'],'metadata':{'filename':_0x68a9ff,'timestamp':_0xcd0bf4[_0x1be50b(0xd6)]['toISOString']()}}),_0x169fc5=await createStepAsset(_0x56ba3e['id'],_0x559478['id'],{'filename':_0x68a9ff,'assetType':'screenshot','timing':_0x2882c5[_0x1be50b(0x14d)],'position':0x0,'capturedAt':_0xcd0bf4[_0x1be50b(0xd6)][_0x1be50b(0x12d)](),'metadata':{'name':_0xb8fa94,'highPriority':_0x2c1689}});await uploadScreenshot(_0x169fc5['uploadUrl'],_0x11dc22),_0x1c9e74[_0x1be50b(0xf7)](_0x2882c5['DUZzI'],{'filename':_0x68a9ff,'bytes':_0x11dc22[_0x1be50b(0x153)]});}catch(_0x2dbc33){await _0x1c9e74[_0x1be50b(0x1dc)]('upload_screenshot',_0x2dbc33,{'filename':_0x68a9ff});}}}}if(_0x2882c5[_0x1be50b(0x171)](existsSync,_0x2134dc)){_0x1c9e74[_0x1be50b(0x15a)]('upload_conversation');try{const _0x1e3978=await getUploadUrls(_0x56ba3e['id'],'conversation.jsonl',_0x2882c5[_0x1be50b(0xf6)]),_0x355466=await readFile(_0x2134dc);await uploadConversation(_0x1e3978[_0x1be50b(0x12a)],_0x355466),_0x1c9e74['trackPhaseEnd']('upload_conversation',{'bytes':_0x355466['length']});}catch(_0x596a66){await _0x1c9e74[_0x1be50b(0x1dc)](_0x2882c5[_0x1be50b(0xf2)],_0x596a66);}}_0x1c9e74[_0x1be50b(0x15a)](_0x2882c5[_0x1be50b(0x16e)]);try{const _0x39a324=_0xd67d80,_0x432c83={'status':_0x607cd7?_0x1be50b(0x1bf):_0x2882c5[_0x1be50b(0x108)],'durationMs':_0x94ecdc,'agentResponse':_0x39a324?.['summary']||_0x607cd7||undefined,'errorMessage':_0x2882c5[_0x1be50b(0xe2)](_0x607cd7,undefined)};await updateBrowserSession(_0x56ba3e['id'],_0x432c83),_0x2882c5[_0x1be50b(0x21b)](_0x39a324,_0x34b8e1)&&(_0x39a324['traceViewerUrl']=_0x2882c5[_0x1be50b(0x174)](buildTraceViewerUrl,_0x34b8e1),_0x39a324[_0x1be50b(0x194)]=_0x56ba3e['id'],_0x39a324['sessionDir']=getTraceDirectory(_0x56ba3e['id']),_0x39a324['durationMs']=_0x94ecdc,_0x39a324[_0x1be50b(0x11e)]=_0x331108['id']),_0x1c9e74['trackPhaseEnd'](_0x2882c5[_0x1be50b(0x16e)]);}catch(_0x1b9f2e){await _0x1c9e74['trackPhaseError'](_0x1be50b(0x1a1),_0x1b9f2e);}}catch{}_0x1c9e74[_0x1be50b(0x15a)](_0x1be50b(0xe6));let _0x59744c;if(_0x2882c5['sQPsN'](_0x3e9698,_0x4eeed7)){const _0x11b55a=_0x2882c5['dPjjF'](getMockEvaluation,_0x4eeed7),_0x43f79a=_0xd67d80;_0x59744c={..._0x11b55a,'sessionId':_0x56ba3e['id'],'sessionDir':getTraceDirectory(_0x56ba3e['id']),'durationMs':_0x94ecdc,'traceViewerUrl':_0x34b8e1?buildTraceViewerUrl(_0x34b8e1):undefined,'checklistItemId':_0x331108['id'],'addressedCommentIds':_0x43f79a?.[_0x1be50b(0x18f)]??[]},console['log']('\x0a[DEBUG\x20MODE]\x20Using\x20mock\x20evaluation:\x20'+_0x4eeed7);}else{const _0x25f196=_0xd67d80;if(_0x2882c5['GNfTU'](_0x607cd7,!_0x25f196))throw new Error('Verification\x20failed:\x20'+_0x607cd7);if(!_0x25f196)throw new Error(_0x1be50b(0x15d));_0x59744c=_0x25f196;}_0x1c9e74[_0x1be50b(0xf7)](_0x2882c5['bmSke'],{'evaluation':_0x59744c[_0x1be50b(0xe6)],'issueCount':_0x59744c['issues']?.[_0x1be50b(0x153)]??0x0}),_0x1c9e74['trackPhaseStart'](_0x1be50b(0x1ba));const _0x180e33=_0x59744c['evaluation'];if(_0x180e33===_0x2882c5['anSfo'])await _0x2882c5['oxncg'](updateChecklistItem,_0x5c1857,_0x331108['id'],{'status':_0x1be50b(0x13d),'browserSessionId':_0x56ba3e['id']}),console['log']('\x0a✅\x20Scenario\x20verified!');else{if(_0x2882c5['GSqAk'](_0x180e33,_0x1be50b(0x1f6))){await _0x2882c5['loRWf'](updateChecklistItem,_0x5c1857,_0x331108['id'],{'status':_0x2882c5['BxeMH'],'browserSessionId':_0x56ba3e['id'],'blockedReason':_0x59744c['evaluationReason']}),console['log']('\x0a'+'='['repeat'](0x3c)),console['log'](_0x1be50b(0xe5)),console[_0x1be50b(0xe0)](''+'='[_0x1be50b(0x12c)](0x3c)),console['log']('\x0aIssue:\x20'+_0x59744c[_0x1be50b(0x203)]);if(_0x59744c[_0x1be50b(0x1c5)]?.[_0x1be50b(0x153)]){console[_0x1be50b(0xe0)]('\x0aDetails:');for(const _0x4e8ca1 of _0x59744c['issues']){const _0x3dfbae=_0x4e8ca1['type']?'\x20('+_0x4e8ca1[_0x1be50b(0x1fd)]+')':'';console[_0x1be50b(0xe0)]('\x20\x20-\x20['+_0x4e8ca1['severity']+']'+_0x3dfbae+'\x20'+_0x4e8ca1[_0x1be50b(0xfe)]);}}_0x59744c[_0x1be50b(0x16a)]&&console[_0x1be50b(0xe0)]('\x0aTrace:\x20'+_0x59744c['traceViewerUrl']),console['log'](_0x1be50b(0xde)),console[_0x1be50b(0xe0)]('='[_0x1be50b(0x12c)](0x3c)+'\x0a');}else(_0x2882c5[_0x1be50b(0x1bb)](_0x180e33,'partial')||_0x2882c5['Euiwl'](_0x180e33,_0x2882c5[_0x1be50b(0x116)])||_0x2882c5[_0x1be50b(0x1b3)](_0x180e33,_0x2882c5[_0x1be50b(0x124)]))&&(await updateChecklistItem(_0x5c1857,_0x331108['id'],{'status':_0x2882c5[_0x1be50b(0x124)],'browserSessionId':_0x56ba3e['id'],'incompleteReason':_0x59744c['evaluationReason']}),await _0x2882c5['XjcyB'](handleIncompleteItem,_0x5c1857,_0x331108,_0x59744c));}_0x1c9e74['trackPhaseEnd'](_0x2882c5[_0x1be50b(0xfd)],{'newStatus':_0x180e33}),_0x1c9e74['trackPhaseStart'](_0x1be50b(0x17c),{'totalFeedbackComments':_0x5d0dd6['length'],'agentAddressedCount':_0x59744c['addressedCommentIds']?.[_0x1be50b(0x153)]??0x0});const _0x5117f7=_0x59744c[_0x1be50b(0x18f)]?.['filter'](_0x570662=>_0x5d0dd6['includes'](_0x570662));if(_0x5117f7&&_0x5117f7[_0x1be50b(0x153)]>0x0)try{await _0x2882c5['oxncg'](markCommentsAddressed,_0x5c1857,_0x331108['id'],_0x5117f7),console['log']('Marked\x20'+_0x5117f7[_0x1be50b(0x153)]+_0x1be50b(0xfa)),_0x1c9e74['trackPhaseEnd']('comment_addressing',{'addressedCount':_0x5117f7[_0x1be50b(0x153)]});}catch(_0x15cbff){await _0x1c9e74[_0x1be50b(0x1dc)](_0x1be50b(0x17c),_0x15cbff);}else _0x1c9e74[_0x1be50b(0xf7)](_0x1be50b(0x17c),{'addressedCount':0x0});return _0x59744c;}export async function verifyFeature(_0x4d112d){const _0x18a546=_0x2f4b72,_0x2673cf={'SItFd':'3|0|5|4|6|2|1','ludDo':_0x18a546(0x172),'hTkCu':_0x18a546(0x198),'bmarC':function(_0x3b9731,_0x26f7a0,_0x59cdd6){return _0x3b9731(_0x26f7a0,_0x59cdd6);},'IjaEy':function(_0x4d0afd,_0x249b0c){return _0x4d0afd(_0x249b0c);},'YBYcg':function(_0xead9af,_0x549a1b){return _0xead9af(_0x549a1b);},'JYbuS':function(_0x2e21c9,_0x452b7f){return _0x2e21c9===_0x452b7f;},'aBsAN':function(_0x39f444,_0x28a26f){return _0x39f444(_0x28a26f);},'xRKLu':_0x18a546(0xdf),'OTzJR':_0x18a546(0x1ef),'pnSgd':_0x18a546(0x1bd),'JWNtq':function(_0x352519,_0x4b0a86,_0x4c595b,_0x59fdcd){return _0x352519(_0x4b0a86,_0x4c595b,_0x59fdcd);},'EjCMS':_0x18a546(0x13e),'JmEGA':'Scenario\x20reset\x20to\x20pending\x20after\x20unexpected\x20error.','PuZrK':'scenario_reset','PtaPz':function(_0x38f207,_0x4e2338,_0x45875e){return _0x38f207(_0x4e2338,_0x45875e);},'qVxdq':function(_0xc817cd,_0xeeb92e){return _0xc817cd(_0xeeb92e);},'JURVY':'cleanup'},_0x274e4f=_0x2673cf['YBYcg'](createTelemetryCollector,'go');await _0x274e4f['trackCommandStart']({'hasProfile':!!_0x4d112d['profile'],'hasScenario':_0x4d112d[_0x18a546(0x11a)]!==undefined,'hasNotes':!!_0x4d112d[_0x18a546(0x19a)],'isDebugMode':!!_0x4d112d['debugOutcome']});const {envNames:_0x3fdbab}=await getEnvNames();if(_0x2673cf[_0x18a546(0x16f)](_0x3fdbab[_0x18a546(0x153)],0x0))throw new Error(_0x2673cf[_0x18a546(0x17a)](formatProfileRequiredMessage,_0x599ce4=>bold(_0x599ce4)));let _0x2e39be,_0x2770f9=![],_0x4164b9=![];const _0x22f730=async()=>{const _0x57d25c=_0x18a546,_0x4539b7=_0x2673cf[_0x57d25c(0x104)][_0x57d25c(0x150)]('|');let _0x135537=0x0;while(!![]){switch(_0x4539b7[_0x135537++]){case'0':_0x2770f9=!![];continue;case'1':process[_0x57d25c(0xf8)](0x0);continue;case'2':console[_0x57d25c(0xe0)](_0x2673cf[_0x57d25c(0xea)]);continue;case'3':if(_0x2770f9)return;continue;case'4':await _0x274e4f[_0x57d25c(0x106)]('interrupted',{'durationMs':_0x2e39be?Date[_0x57d25c(0x125)]()-_0x2e39be[_0x57d25c(0x1b9)]:0x0});continue;case'5':console[_0x57d25c(0xe0)](_0x2673cf['hTkCu']);continue;case'6':if(_0x2e39be){try{await _0x2673cf[_0x57d25c(0x128)](updateBrowserSession,_0x2e39be[_0x57d25c(0x1a5)]['id'],{'status':'interrupted','durationMs':Date['now']()-_0x2e39be['startTime']});}catch{}try{await updateChecklistItem(_0x2e39be['featureId'],_0x2e39be['checklistItem']['id'],{'status':'pending'});}catch{}if(_0x2e39be[_0x57d25c(0xdb)])try{await _0x2673cf[_0x57d25c(0xed)](cleanupTempFiles,_0x2e39be['configResult']);}catch{}}continue;}break;}};process['on'](_0x18a546(0x1ef),_0x22f730),process['on'](_0x18a546(0x1bd),_0x22f730);try{_0x2e39be=await runVerification(_0x4d112d,_0x274e4f),await _0x274e4f[_0x18a546(0x1fe)]();const _0x1ce9e9=await processVerificationResult(_0x2e39be);return _0x4164b9=!![],await _0x274e4f[_0x18a546(0x106)](_0x2673cf['xRKLu'],{'evaluation':_0x1ce9e9[_0x18a546(0xe6)],'durationMs':_0x2e39be['durationMs'],..._0x2e39be[_0x18a546(0x211)]}),_0x1ce9e9;}catch(_0x3e810a){await _0x274e4f['trackCommandError'](_0x3e810a);throw _0x3e810a;}finally{process['removeListener'](_0x2673cf[_0x18a546(0x1c1)],_0x22f730),process['removeListener'](_0x2673cf[_0x18a546(0x118)],_0x22f730);if(_0x2e39be&&!_0x2770f9&&!_0x4164b9)try{await _0x2673cf['JWNtq'](updateChecklistItem,_0x2e39be['featureId'],_0x2e39be[_0x18a546(0x110)]['id'],{'status':_0x2673cf['EjCMS']}),console[_0x18a546(0xe0)](_0x2673cf['JmEGA']);}catch(_0x1ce97f){await _0x274e4f['trackPhaseError'](_0x2673cf[_0x18a546(0x1bc)],_0x1ce97f);}if(_0x2e39be){_0x274e4f[_0x18a546(0x15a)]('cleanup');_0x2e39be['configResult']&&await cleanupTempFiles(_0x2e39be[_0x18a546(0xdb)]);try{const _0x1e643d=getTraceDirectory(_0x2e39be['browserSession']['id']);_0x2673cf['YBYcg'](existsSync,_0x1e643d)&&await _0x2673cf[_0x18a546(0x177)](rm,_0x1e643d,{'recursive':!![],'force':!![]});}catch{}try{_0x2e39be[_0x18a546(0x165)]&&_0x2673cf['qVxdq'](existsSync,_0x2e39be['conversationDir'])&&await rm(_0x2e39be['conversationDir'],{'recursive':!![],'force':!![]});}catch{}_0x274e4f[_0x18a546(0xf7)](_0x2673cf['JURVY']);}await _0x274e4f[_0x18a546(0x1fe)]();}}
|