@validate.qa/runner 1.0.47 → 1.0.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +471 -41
  2. package/dist/cli.mjs +471 -41
  3. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -5046,6 +5046,191 @@ var init_auth_block_detector = __esm({
5046
5046
  }
5047
5047
  });
5048
5048
 
5049
+ // ../shared/dist/auth-state-freshness.js
5050
+ function isCsrfTokenName(name) {
5051
+ return /csrf|xsrf|forgery|authenticity|requestverification/.test(name.toLowerCase());
5052
+ }
5053
+ function isAuthTokenName(name) {
5054
+ const n = name.toLowerCase();
5055
+ if (isCsrfTokenName(n))
5056
+ return false;
5057
+ if (/session|auth|jwt|credential|identity|login|token/.test(n))
5058
+ return true;
5059
+ if (/(?:^|[^a-z0-9])(?:sid|sess|account|remember)(?:[^a-z0-9]|$)/.test(n))
5060
+ return true;
5061
+ return [
5062
+ "connect.sid",
5063
+ "jsessionid",
5064
+ "phpsessid",
5065
+ "aspnet",
5066
+ "asp.net",
5067
+ "aspxauth",
5068
+ "sb-",
5069
+ "supabase",
5070
+ "amplify",
5071
+ "cognito",
5072
+ "auth0",
5073
+ "okta",
5074
+ "kc-",
5075
+ "li_at",
5076
+ "ltpatoken",
5077
+ "wordpress_logged_in"
5078
+ ].some((frag) => n.includes(frag));
5079
+ }
5080
+ function isSessionClassName(name) {
5081
+ const n = name.toLowerCase();
5082
+ if (isCsrfTokenName(n))
5083
+ return false;
5084
+ if (/session|sess/.test(n))
5085
+ return true;
5086
+ if (/(?:^|[^a-z0-9])sid(?:[^a-z0-9]|$)/.test(n))
5087
+ return true;
5088
+ return [
5089
+ "connect.sid",
5090
+ "jsessionid",
5091
+ "phpsessid",
5092
+ "aspxauth",
5093
+ "li_at",
5094
+ "ltpatoken",
5095
+ "wordpress_logged_in"
5096
+ ].some((frag) => n.includes(frag));
5097
+ }
5098
+ function decodeBase64UrlToUtf8(segment) {
5099
+ if (typeof atob !== "function")
5100
+ return void 0;
5101
+ const base64 = segment.replace(/-/g, "+").replace(/_/g, "/");
5102
+ const padded = base64.length % 4 === 0 ? base64 : base64 + "=".repeat(4 - base64.length % 4);
5103
+ try {
5104
+ const binary = atob(padded);
5105
+ let percentEncoded = "";
5106
+ for (let i = 0; i < binary.length; i += 1) {
5107
+ percentEncoded += `%${binary.charCodeAt(i).toString(16).padStart(2, "0")}`;
5108
+ }
5109
+ return decodeURIComponent(percentEncoded);
5110
+ } catch {
5111
+ return void 0;
5112
+ }
5113
+ }
5114
+ function jwtExpiryMs(value) {
5115
+ const parts = value.split(".");
5116
+ if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2])
5117
+ return void 0;
5118
+ const json = decodeBase64UrlToUtf8(parts[1]);
5119
+ if (json === void 0)
5120
+ return void 0;
5121
+ try {
5122
+ const payload = JSON.parse(json);
5123
+ const exp = payload?.exp;
5124
+ return typeof exp === "number" && Number.isFinite(exp) && exp > 0 ? exp * 1e3 : void 0;
5125
+ } catch {
5126
+ return void 0;
5127
+ }
5128
+ }
5129
+ function localStorageAuthExpiryMs(value) {
5130
+ const direct = jwtExpiryMs(value);
5131
+ if (direct !== void 0)
5132
+ return direct;
5133
+ let obj;
5134
+ try {
5135
+ obj = JSON.parse(value);
5136
+ } catch {
5137
+ return void 0;
5138
+ }
5139
+ if (!obj || typeof obj !== "object")
5140
+ return void 0;
5141
+ const record = obj;
5142
+ const nested = record.currentSession ?? record.session;
5143
+ const jwtCandidates = [
5144
+ record.access_token,
5145
+ record.accessToken,
5146
+ record.token,
5147
+ record.id_token,
5148
+ record.idToken,
5149
+ nested?.access_token,
5150
+ nested?.accessToken
5151
+ ];
5152
+ for (const candidate of jwtCandidates) {
5153
+ if (typeof candidate === "string") {
5154
+ const exp = jwtExpiryMs(candidate);
5155
+ if (exp !== void 0)
5156
+ return exp;
5157
+ }
5158
+ }
5159
+ const expiresAt = record.expires_at ?? record.expiresAt ?? nested?.expires_at;
5160
+ if (typeof expiresAt === "number" && Number.isFinite(expiresAt) && expiresAt > 0) {
5161
+ return expiresAt < 1e12 ? expiresAt * 1e3 : expiresAt;
5162
+ }
5163
+ return void 0;
5164
+ }
5165
+ function collectAuthTokens(parsed) {
5166
+ const tokens = [];
5167
+ if (!parsed || typeof parsed !== "object")
5168
+ return tokens;
5169
+ const cookies = parsed.cookies;
5170
+ if (Array.isArray(cookies)) {
5171
+ for (const raw of cookies) {
5172
+ if (!raw || typeof raw !== "object")
5173
+ continue;
5174
+ const name = typeof raw.name === "string" ? raw.name : "";
5175
+ const value = typeof raw.value === "string" ? raw.value : "";
5176
+ if (isCsrfTokenName(name))
5177
+ continue;
5178
+ const jwtExp = value ? jwtExpiryMs(value) : void 0;
5179
+ if (jwtExp !== void 0) {
5180
+ tokens.push({ expiryMs: jwtExp, sessionClass: true });
5181
+ continue;
5182
+ }
5183
+ if (isAuthTokenName(name) && typeof raw.expires === "number" && Number.isFinite(raw.expires) && raw.expires > 0) {
5184
+ tokens.push({ expiryMs: raw.expires * 1e3, sessionClass: isSessionClassName(name) });
5185
+ }
5186
+ }
5187
+ }
5188
+ const origins = parsed.origins;
5189
+ if (Array.isArray(origins)) {
5190
+ for (const origin of origins) {
5191
+ if (!origin || typeof origin !== "object" || !Array.isArray(origin.localStorage))
5192
+ continue;
5193
+ for (const entry of origin.localStorage) {
5194
+ if (!entry || typeof entry !== "object")
5195
+ continue;
5196
+ const key = typeof entry.name === "string" ? entry.name : "";
5197
+ const value = typeof entry.value === "string" ? entry.value : "";
5198
+ if (!key || !value || isCsrfTokenName(key) || !isAuthTokenName(key))
5199
+ continue;
5200
+ const exp = localStorageAuthExpiryMs(value);
5201
+ if (exp !== void 0)
5202
+ tokens.push({ expiryMs: exp, sessionClass: true });
5203
+ }
5204
+ }
5205
+ }
5206
+ return tokens;
5207
+ }
5208
+ function storageStateExpiredBySkew(stateJson, nowMs, skewMs = AUTH_STATE_EXPIRY_SKEW_MS) {
5209
+ let parsed;
5210
+ try {
5211
+ parsed = JSON.parse(stateJson);
5212
+ } catch {
5213
+ return false;
5214
+ }
5215
+ const tokens = collectAuthTokens(parsed);
5216
+ if (tokens.length === 0)
5217
+ return false;
5218
+ const margin = Number.isFinite(skewMs) ? Math.max(0, skewMs) : AUTH_STATE_EXPIRY_SKEW_MS;
5219
+ const deadline = nowMs + margin;
5220
+ const anyExpired = tokens.some((t) => t.expiryMs <= deadline);
5221
+ if (!anyExpired)
5222
+ return false;
5223
+ const liveSession = tokens.some((t) => t.expiryMs > deadline && t.sessionClass);
5224
+ return !liveSession;
5225
+ }
5226
+ var AUTH_STATE_EXPIRY_SKEW_MS;
5227
+ var init_auth_state_freshness = __esm({
5228
+ "../shared/dist/auth-state-freshness.js"() {
5229
+ "use strict";
5230
+ AUTH_STATE_EXPIRY_SKEW_MS = 3e4;
5231
+ }
5232
+ });
5233
+
5049
5234
  // ../shared/dist/skip-coverage-detector.js
5050
5235
  function detectSkippedApiCoverage(steps) {
5051
5236
  const allSkipped = steps.length > 0 && steps.every((s) => s.status === "skipped");
@@ -5435,6 +5620,7 @@ __export(dist_exports, {
5435
5620
  API_AUTH_HEADERS_ENV_KEY: () => API_AUTH_HEADERS_ENV_KEY,
5436
5621
  API_PREFIX: () => API_PREFIX,
5437
5622
  AUDIO_CHUNK_DURATION_MS: () => AUDIO_CHUNK_DURATION_MS,
5623
+ AUTH_STATE_EXPIRY_SKEW_MS: () => AUTH_STATE_EXPIRY_SKEW_MS,
5438
5624
  AUTH_TAG_COOKIE_STATE: () => AUTH_TAG_COOKIE_STATE,
5439
5625
  AUTH_TAG_LOGIN_BEARER: () => AUTH_TAG_LOGIN_BEARER,
5440
5626
  AUTH_TAG_LOGIN_COOKIE: () => AUTH_TAG_LOGIN_COOKIE,
@@ -5552,6 +5738,7 @@ __export(dist_exports, {
5552
5738
  isApiTest: () => isApiTest,
5553
5739
  isAppOriginResolutionEnabled: () => isAppOriginResolutionEnabled,
5554
5740
  isAuthReadinessGuardEnabled: () => isAuthReadinessGuardEnabled,
5741
+ isAuthTokenName: () => isAuthTokenName,
5555
5742
  isBlockedEnvVar: () => isBlockedEnvVar,
5556
5743
  isCountedFailure: () => isCountedFailure,
5557
5744
  isCreditPackAmountCents: () => isCreditPackAmountCents,
@@ -5574,6 +5761,7 @@ __export(dist_exports, {
5574
5761
  isValidSttProvider: () => isValidSttProvider,
5575
5762
  isValidTtsModel: () => isValidTtsModel,
5576
5763
  isValidTtsVoiceId: () => isValidTtsVoiceId,
5764
+ jwtExpiryMs: () => jwtExpiryMs,
5577
5765
  mediumForMobilePlatform: () => mediumForMobilePlatform,
5578
5766
  mobileCredentialPlaceholder: () => mobileCredentialPlaceholder,
5579
5767
  mobilePlatformFromMedium: () => mobilePlatformFromMedium,
@@ -5605,6 +5793,7 @@ __export(dist_exports, {
5605
5793
  stepIsDivergentForPlatform: () => stepIsDivergentForPlatform,
5606
5794
  stepsMissingPlatformLocators: () => stepsMissingPlatformLocators,
5607
5795
  stepsNeedingReviewForPlatform: () => stepsNeedingReviewForPlatform,
5796
+ storageStateExpiredBySkew: () => storageStateExpiredBySkew,
5608
5797
  stripEnvironmentalPrefix: () => stripEnvironmentalPrefix,
5609
5798
  stripLoginScaffolding: () => stripLoginScaffolding,
5610
5799
  sttModelsForProvider: () => sttModelsForProvider,
@@ -5635,6 +5824,7 @@ var init_dist = __esm({
5635
5824
  init_origin_domain();
5636
5825
  init_auth_plan();
5637
5826
  init_auth_block_detector();
5827
+ init_auth_state_freshness();
5638
5828
  init_skip_coverage_detector();
5639
5829
  init_redact_needles();
5640
5830
  init_voice_config();
@@ -77103,7 +77293,7 @@ var require_discover_ai_planner = __commonJS({
77103
77293
  "../runner-core/dist/services/discover-ai-planner.js"(exports) {
77104
77294
  "use strict";
77105
77295
  Object.defineProperty(exports, "__esModule", { value: true });
77106
- exports.PLANNER_STRENGTHEN_GATE_PROMPT = exports.SCENARIO_SYSTEM_PROMPT = void 0;
77296
+ exports.PLANNER_STRENGTHEN_GATE_PROMPT = exports.SCENARIO_SYSTEM_PROMPT = exports.SURVEY_SYSTEM_PROMPT = void 0;
77107
77297
  exports.getAIPlannerMaxTokens = getAIPlannerMaxTokens;
77108
77298
  exports.getSnapshotConfidence = getSnapshotConfidence;
77109
77299
  exports.compressSiteMap = compressSiteMap;
@@ -77639,7 +77829,7 @@ Plan these LOGGED-OUT instead (keep their own establishment steps; do NOT assume
77639
77829
  }
77640
77830
  return sections.join("\n\n");
77641
77831
  }
77642
- var SURVEY_SYSTEM_PROMPT = `You are a feature analyst for a web application. You receive evidence from an AI explorer that mapped the entire site. Your job is to organize pages into meaningful feature groups.
77832
+ exports.SURVEY_SYSTEM_PROMPT = `You are a feature analyst for a web application. You receive evidence from an AI explorer that mapped the entire site. Your job is to organize pages into meaningful feature groups.
77643
77833
 
77644
77834
  ## Output Discipline
77645
77835
  Return ONLY one valid JSON object matching the schema below. Do NOT include <think> blocks, markdown fences, analysis, prose, or any text outside the JSON object. Put any brief explanation only in the JSON "reasoning" field.
@@ -77659,6 +77849,7 @@ Return ONLY one valid JSON object matching the schema below. Do NOT include <thi
77659
77849
  6. Do NOT merge pages into one feature just because they serve a similar broader goal. Treat areas as separate features when the evidence shows separate primary navigation destinations (e.g. separate sidebar links), separate route families below the app shell (e.g. /dashboard/documents/* vs /dashboard/reports/* are separate even though both are under /dashboard), or different CRUD entities/forms. Keep pages together when they share one navigational area and operate on the same entity (e.g. /applications, /applications/stats, /applications?filter= all belong together).
77660
77850
  7. Feature descriptions MUST reference actual element labels from the evidence
77661
77851
  8. Do NOT create standalone features for generic error pages (404, 500, access denied). If a business route is broken or crashes, keep it in its intended feature and note the failure in the description. Error pages do not need their own deep exploration run.
77852
+ 9. The Base URL's PATH is the survey's entry scope. When it is non-root (e.g. \`/shop/checkout\`), the operator is auditing that one flow in isolation: do NOT create featureGroups for parent paths, sibling paths, or guessed/nonexistent routes on the same host \u2014 even if the explorer's evidence wandered there. Auth pages the flow itself traverses (its login page, an \`/api/auth/*\` callback) belong INSIDE that flow's group, not in separate groups. When the Base URL path is \`/\`, group the whole app as usual.
77662
77853
 
77663
77854
  ## Output JSON Schema
77664
77855
  {
@@ -78411,7 +78602,7 @@ Keep the exact JSON schema { "featureGroups":[...], "scenarios":[...], "reasonin
78411
78602
  const agentModel = options.modelOverride ? (0, ai_provider_js_1.resolveModelForTask)(options.modelOverride, "agent") : (0, ai_provider_js_1.getModel)("agent");
78412
78603
  const plannerMaxTokens = getAIPlannerMaxTokens(agentModel);
78413
78604
  logs2.push(`AI planner [${context.mode}]: ${siteMap.pages.length} pages, ${evidence.recordedJourneys.length} journeys, budget ${context.testBudget}`);
78414
- const systemPrompt = options.systemPromptOverride ?? (context.mode === "survey" ? SURVEY_SYSTEM_PROMPT : exports.SCENARIO_SYSTEM_PROMPT);
78605
+ const systemPrompt = options.systemPromptOverride ?? (context.mode === "survey" ? exports.SURVEY_SYSTEM_PROMPT : exports.SCENARIO_SYSTEM_PROMPT);
78415
78606
  const userPrompt = options.userPromptOverride ?? formatEvidenceForPlanner(compressed, evidence, context);
78416
78607
  logs2.push(`AI planner: ~${Math.ceil(userPrompt.length / 3.5)} input tokens`);
78417
78608
  const aiClient = options.aiClient ?? (options.modelOverride ? (0, ai_provider_js_1.getRunnerClientForModel)(options.modelOverride) : (0, ai_provider_js_1.getRunnerAIClient)());
@@ -321382,6 +321573,168 @@ var require_evidence_harvester = __commonJS({
321382
321573
  }
321383
321574
  });
321384
321575
 
321576
+ // ../runner-core/dist/services/survey-scope.js
321577
+ var require_survey_scope = __commonJS({
321578
+ "../runner-core/dist/services/survey-scope.js"(exports) {
321579
+ "use strict";
321580
+ Object.defineProperty(exports, "__esModule", { value: true });
321581
+ exports.deriveSurveyPathScope = deriveSurveyPathScope;
321582
+ exports.isAuthFlowRoute = isAuthFlowRoute;
321583
+ exports.buildSurveyScopeBoundary = buildSurveyScopeBoundary;
321584
+ exports.filterFeatureGroupsToPathScope = filterFeatureGroupsToPathScope;
321585
+ var exploration_parser_js_1 = require_exploration_parser();
321586
+ function deriveSurveyPathScope(baseUrl) {
321587
+ const trimmed = (baseUrl ?? "").trim();
321588
+ if (!trimmed)
321589
+ return null;
321590
+ let parsed;
321591
+ try {
321592
+ parsed = new URL(trimmed);
321593
+ } catch {
321594
+ return null;
321595
+ }
321596
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
321597
+ return null;
321598
+ const hashRoute = parsed.hash.startsWith("#/") ? parsed.hash : "";
321599
+ const basePath = (0, exploration_parser_js_1.normalizeRouteForFocusScope)(`${parsed.pathname}${hashRoute}`);
321600
+ if (basePath === "/" || basePath === "/#")
321601
+ return null;
321602
+ return { origin: parsed.origin, basePath };
321603
+ }
321604
+ var AUTH_FLOW_SEGMENTS = /* @__PURE__ */ new Set([
321605
+ "auth",
321606
+ "oauth",
321607
+ "oauth2",
321608
+ "callback",
321609
+ "login",
321610
+ "log-in",
321611
+ "signin",
321612
+ "sign-in",
321613
+ "signup",
321614
+ "sign-up",
321615
+ "register",
321616
+ "logout",
321617
+ "log-out",
321618
+ "signout",
321619
+ "sign-out",
321620
+ "sso",
321621
+ "saml",
321622
+ "oidc"
321623
+ ]);
321624
+ function isAuthFlowRoute(route2) {
321625
+ const normalized = (0, exploration_parser_js_1.normalizeRouteForFocusScope)(route2).toLowerCase();
321626
+ return normalized.split(/[/#]/).some((segment) => AUTH_FLOW_SEGMENTS.has(segment));
321627
+ }
321628
+ function routePathForScope(route2, origin) {
321629
+ const trimmed = (route2 ?? "").trim();
321630
+ if (trimmed.startsWith("//")) {
321631
+ try {
321632
+ const parsed = new URL(`https:${trimmed}`);
321633
+ if (parsed.host !== new URL(origin).host)
321634
+ return null;
321635
+ return `${parsed.pathname}${parsed.search}${parsed.hash}`;
321636
+ } catch {
321637
+ return null;
321638
+ }
321639
+ }
321640
+ if (/^https?:\/\//i.test(trimmed)) {
321641
+ try {
321642
+ const parsed = new URL(trimmed);
321643
+ if (parsed.origin !== origin)
321644
+ return null;
321645
+ return `${parsed.pathname}${parsed.search}${parsed.hash}`;
321646
+ } catch {
321647
+ return trimmed;
321648
+ }
321649
+ }
321650
+ if (trimmed.startsWith("/") && trimmed.includes("..")) {
321651
+ try {
321652
+ const resolved = new URL(trimmed, "https://scope.invalid");
321653
+ return `${resolved.pathname}${resolved.search}${resolved.hash}`;
321654
+ } catch {
321655
+ return trimmed;
321656
+ }
321657
+ }
321658
+ return trimmed;
321659
+ }
321660
+ function buildSurveyScopeBoundary(baseUrl) {
321661
+ const scope = deriveSurveyPathScope(baseUrl);
321662
+ const lines = [
321663
+ '- **Never invent a URL.** Navigate only to pages the app presented (links/buttons you can click, per the rule above) or redirects it performs. Do not guess "common" routes and do not probe made-up routes to see what the error page does'
321664
+ ];
321665
+ if (scope) {
321666
+ lines.push(`- **The base URL PATH is the entry scope** (\`${scope.basePath}\`) \u2014 this survey audits that flow in ISOLATION. Do not truncate the path to visit a parent, and do not visit or catalog sibling paths on this host that in-scope pages never linked to. Pages the flow itself pushes you through (its login page, an auth/OAuth callback, a post-submit redirect) are in scope`);
321667
+ }
321668
+ return lines.join("\n");
321669
+ }
321670
+ function filterFeatureGroupsToPathScope(featureGroups, baseUrl) {
321671
+ const scope = deriveSurveyPathScope(baseUrl);
321672
+ if (!scope) {
321673
+ return { kept: featureGroups, droppedGroups: [], trimmedRoutes: [], trimmedPages: [], basePath: null };
321674
+ }
321675
+ const withinBase = (route2) => {
321676
+ const path = routePathForScope(route2, scope.origin);
321677
+ if (path === null)
321678
+ return false;
321679
+ if ((0, exploration_parser_js_1.routeIsWithinFocusAreas)(path, [scope.basePath]))
321680
+ return true;
321681
+ const norm = (0, exploration_parser_js_1.normalizeRouteForFocusScope)(path);
321682
+ return norm === `${scope.basePath}#` || norm.startsWith(`${scope.basePath}#/`);
321683
+ };
321684
+ const allowedAuthFlow = (route2) => {
321685
+ const path = routePathForScope(route2, scope.origin);
321686
+ return path !== null && isAuthFlowRoute(path);
321687
+ };
321688
+ const kept = [];
321689
+ const droppedGroups = [];
321690
+ const trimmedRoutes = [];
321691
+ const trimmedPages = [];
321692
+ for (const group of featureGroups) {
321693
+ const routes = Array.isArray(group.routes) ? group.routes : [];
321694
+ const keptRoutes = [];
321695
+ const removedRoutes = [];
321696
+ let anchored = false;
321697
+ for (const route2 of routes) {
321698
+ if (withinBase(route2)) {
321699
+ anchored = true;
321700
+ keptRoutes.push(route2);
321701
+ } else if (allowedAuthFlow(route2)) {
321702
+ keptRoutes.push(route2);
321703
+ } else {
321704
+ removedRoutes.push(route2);
321705
+ }
321706
+ }
321707
+ if (!anchored) {
321708
+ droppedGroups.push({ name: group.name, routes: [...routes] });
321709
+ continue;
321710
+ }
321711
+ let pages = group.pages;
321712
+ const removedPages = [];
321713
+ if (Array.isArray(group.pages)) {
321714
+ const keptPages = [];
321715
+ for (const page of group.pages) {
321716
+ if (withinBase(page.route) || allowedAuthFlow(page.route))
321717
+ keptPages.push(page);
321718
+ else
321719
+ removedPages.push(page.route);
321720
+ }
321721
+ pages = keptPages;
321722
+ }
321723
+ if (removedRoutes.length === 0 && removedPages.length === 0) {
321724
+ kept.push(group);
321725
+ } else {
321726
+ kept.push({ ...group, routes: keptRoutes, ...group.pages !== void 0 ? { pages } : {} });
321727
+ if (removedRoutes.length > 0)
321728
+ trimmedRoutes.push({ name: group.name, routes: removedRoutes });
321729
+ if (removedPages.length > 0)
321730
+ trimmedPages.push({ name: group.name, routes: removedPages });
321731
+ }
321732
+ }
321733
+ return { kept, droppedGroups, trimmedRoutes, trimmedPages, basePath: scope.basePath };
321734
+ }
321735
+ }
321736
+ });
321737
+
321385
321738
  // ../runner-core/dist/services/discover-explorer.js
321386
321739
  var require_discover_explorer = __commonJS({
321387
321740
  "../runner-core/dist/services/discover-explorer.js"(exports) {
@@ -321423,6 +321776,7 @@ var require_discover_explorer = __commonJS({
321423
321776
  var evidence_harvester_js_1 = require_evidence_harvester();
321424
321777
  var exploration_parser_js_1 = require_exploration_parser();
321425
321778
  var survey_link_frontier_js_1 = require_survey_link_frontier();
321779
+ var survey_scope_js_1 = require_survey_scope();
321426
321780
  var snapshot_context_compactor_js_2 = require_snapshot_context_compactor();
321427
321781
  Object.defineProperty(exports, "formatMCPToolResult", { enumerable: true, get: function() {
321428
321782
  return snapshot_context_compactor_js_2.formatMCPToolResult;
@@ -321850,6 +322204,7 @@ If a snapshot's readiness section exposes \`loading_indicators\`, \`skeleton_con
321850
322204
  - **Map, do not mutate.** You MAY click buttons that open modals, popups, or dropdowns for the purpose of CATALOGING their contents (capture_page). Do NOT fill forms, submit data, or click destructive buttons (save, delete, submit) - just catalog the elements and close the modal
321851
322205
  - **Modals/popups/banners**: If a cookie consent banner, onboarding tour, or overlay appears, dismiss it (click close/X/dismiss/accept) and continue. If it reappears on the next page, dismiss it again - some sites show consent banners on every navigation until accepted
321852
322206
  - **Stay on the same origin** as the base URL. Note external links in elements but do not follow them
322207
+ ${(0, survey_scope_js_1.buildSurveyScopeBoundary)(baseUrl)}
321853
322208
  - **OAuth/SSO**: If login redirects to an external provider (Google, GitHub), do not follow it. Note that the app uses OAuth and explore unauthenticated pages only
321854
322209
  - **Auth failure**: If login${isRegisterMode ? " or account registration" : ""} fails after 2 attempts, capture the auth page and explore unauthenticated pages. Note the failure in your \`finish_exploration\` summary
321855
322210
  - **Parameterized routes**: /users/1 and /users/2 are the same page template. Visit ONE instance, not all of them
@@ -325348,6 +325703,8 @@ var require_agentic_survey_prompts = __commonJS({
325348
325703
  exports.validateSurveyorOutput = validateSurveyorOutput;
325349
325704
  exports.buildSurveyAgentsMd = buildSurveyAgentsMd;
325350
325705
  exports.buildSurveyBriefMd = buildSurveyBriefMd;
325706
+ exports.buildClaudeSurveyKickoffPrompt = buildClaudeSurveyKickoffPrompt;
325707
+ var survey_scope_js_1 = require_survey_scope();
325351
325708
  var CRITICALITY_VALUES = ["critical", "high", "medium", "low"];
325352
325709
  function isString(v) {
325353
325710
  return typeof v === "string";
@@ -325527,9 +325884,40 @@ var require_agentic_survey_prompts = __commonJS({
325527
325884
  '- Click "delete" or destructive admin actions.',
325528
325885
  "- Leave the baseUrl domain except briefly for OAuth providers.",
325529
325886
  "",
325887
+ "## Navigation scope",
325888
+ "",
325889
+ "The Base URL in BRIEF.md is the ENTRY SCOPE \u2014 its full path, not just",
325890
+ "its domain. Open the exact Base URL first. From there, navigate ONLY to:",
325891
+ "",
325892
+ "- URLs the app actually presented to you: a link, button, or form",
325893
+ " visible in a snapshot of an in-scope page. (`browser_navigate` to a",
325894
+ " URL you saw as a link href is fine \u2014 composing one yourself is not.)",
325895
+ "- Redirects the app itself performs during those interactions,",
325896
+ " including login/OAuth/SSO callback routes needed to complete a flow.",
325897
+ "- The Base URL itself, to return to the start of the flow.",
325898
+ "",
325899
+ "NEVER invent a URL. Do NOT:",
325900
+ "- Parent-walk: truncating the Base URL path (base `/shop/checkout` \u2192",
325901
+ " visiting `/shop` or `/`) is out of scope even though it is same-origin.",
325902
+ "- Sibling-walk: do not visit or catalog neighbours of the Base URL path",
325903
+ " that in-scope pages never linked to.",
325904
+ '- Guess "common" routes (`/admin`, `/about`) or probe made-up routes',
325905
+ " (`/nonexistent`) to see what the error handling does. Do not create",
325906
+ " featureGroups for generic error/404 pages unless the app itself",
325907
+ " routes users there as part of a real flow.",
325908
+ "- Any other URL surgery: if the UI did not present it and the app did",
325909
+ " not redirect you to it, it does not exist for this survey.",
325910
+ "",
325911
+ "When the Base URL path is non-root (e.g. `/shop/checkout`, `/portal/billing`),",
325912
+ "the operator is auditing THAT flow in isolation: every featureGroup must",
325913
+ "describe that flow. Auth pages the flow pushes you through belong inside",
325914
+ "that flow's featureGroup \u2014 do not mint separate featureGroups for them.",
325915
+ "When the Base URL is the origin root, survey the whole app \u2014 the",
325916
+ "no-guessing rule still applies: reach pages through the UI.",
325917
+ "",
325530
325918
  "## Exploration philosophy",
325531
325919
  "",
325532
- "- Breadth before depth \u2014 map the whole surface before drilling in.",
325920
+ "- Breadth before depth \u2014 map the whole in-scope surface before drilling in.",
325533
325921
  "- Authenticated and unauthenticated surfaces are different products;",
325534
325922
  " explore both when credentials are available.",
325535
325923
  "- Prefer observing over interacting: open a page, snapshot, note the",
@@ -325624,6 +326012,17 @@ var require_agentic_survey_prompts = __commonJS({
325624
326012
  lines.push("");
325625
326013
  lines.push(`**Base URL:** ${input.baseUrl}`);
325626
326014
  lines.push("");
326015
+ const pathScope = (0, survey_scope_js_1.deriveSurveyPathScope)(input.baseUrl);
326016
+ if (pathScope) {
326017
+ lines.push(`**Entry scope:** \`${pathScope.basePath}\` \u2014 this is a SCOPED survey of one flow.`);
326018
+ lines.push("");
326019
+ lines.push(`Everything you catalog must live under \`${pathScope.basePath}\`, or be a page`);
326020
+ lines.push("this flow itself pushes you through (its own login page, an auth/OAuth");
326021
+ lines.push("callback, a post-submit redirect). Parent and sibling paths on this host");
326022
+ lines.push("are OTHER fixtures/apps and are out of scope \u2014 do not visit them, do not");
326023
+ lines.push("catalog them, do not mint featureGroups for them.");
326024
+ lines.push("");
326025
+ }
325627
326026
  if (input.appBrief && input.appBrief.trim().length > 0) {
325628
326027
  lines.push("## App brief from the operator");
325629
326028
  lines.push("");
@@ -325693,6 +326092,37 @@ var require_agentic_survey_prompts = __commonJS({
325693
326092
  lines.push("");
325694
326093
  return lines.join("\n");
325695
326094
  }
326095
+ function buildClaudeSurveyKickoffPrompt(resultFilePath) {
326096
+ return [
326097
+ "Read AGENTS.md, then read BRIEF.md.",
326098
+ "",
326099
+ `Your deliverable is a SurveyorOutput JSON file at ${resultFilePath}.`,
326100
+ "Follow this procedure exactly:",
326101
+ "",
326102
+ "STEP 1 \u2014 Orient: navigate to the EXACT Base URL from BRIEF.md and",
326103
+ " snapshot it, then open 2-3 pages its UI visibly links to (e.g. a login",
326104
+ " link it presents). Navigate only to URLs the page actually presented \u2014",
326105
+ ' never guessed parent, sibling, or "common" routes (see "Navigation',
326106
+ ' scope" in AGENTS.md).',
326107
+ `STEP 2 \u2014 IMMEDIATELY use the Write tool to save a first COMPLETE draft of`,
326108
+ ` the SurveyorOutput JSON to ${resultFilePath}, based on what you have seen`,
326109
+ " so far. featureGroups must be a non-empty array. Do NOT skip this step",
326110
+ " and do NOT explore further until this file exists.",
326111
+ "STEP 3 \u2014 Continue exploring breadth-first. Visit each remaining page AT",
326112
+ " MOST ONCE \u2014 never re-navigate to or re-snapshot a page already seen.",
326113
+ ` After every 3-4 new pages, OVERWRITE ${resultFilePath} with an updated,`,
326114
+ " complete JSON object.",
326115
+ "STEP 4 \u2014 Stop once you have covered the main in-scope pages (around 20",
326116
+ ` page visits is plenty) or stop finding new features. Do a final OVERWRITE`,
326117
+ ` of ${resultFilePath} with the complete SurveyorOutput, then exit.`,
326118
+ "",
326119
+ "Budget: about 40 tool calls total. If you ever sense you are running low,",
326120
+ `immediately overwrite ${resultFilePath} with your best current JSON and`,
326121
+ "exit \u2014 a complete-but-shallow file always beats no file. Never write an",
326122
+ "empty featureGroups array and never omit the key. No markdown fences, no",
326123
+ "prose in the file \u2014 a single JSON object only."
326124
+ ].join("\n");
326125
+ }
325696
326126
  }
325697
326127
  });
325698
326128
 
@@ -326243,6 +326673,9 @@ ${(0, inbox_tools_js_1.buildInboxAgentsGuidance)(inboxReg.email, inboxReg.passwo
326243
326673
  const kickoffPrompt = [
326244
326674
  "Read AGENTS.md, then read BRIEF.md.",
326245
326675
  "Use the playwright MCP tools to explore the site.",
326676
+ 'Follow the "Navigation scope" rules in AGENTS.md: start at the exact Base',
326677
+ 'URL and never navigate to guessed parent, sibling, or "common" routes \u2014',
326678
+ "only URLs the pages actually present (or redirects the app performs).",
326246
326679
  `When done, write your JSON output to ${resultFilePath} as a single JSON object matching the SurveyorOutput schema in AGENTS.md.`,
326247
326680
  "Then exit."
326248
326681
  ].join("\n");
@@ -329959,32 +330392,7 @@ ${(0, inbox_tools_js_1.buildInboxAgentsGuidance)(inboxReg.email, inboxReg.passwo
329959
330392
  await (0, promises_1.writeFile)((0, node_path_1.join)(runDir, "AGENTS.md"), surveyAgentsMd, "utf8");
329960
330393
  await (0, promises_1.writeFile)((0, node_path_1.join)(runDir, "BRIEF.md"), (0, agentic_survey_prompts_js_1.buildSurveyBriefMd)(briefInput), "utf8");
329961
330394
  log2("Wrote AGENTS.md + BRIEF.md");
329962
- const kickoffPrompt = [
329963
- "Read AGENTS.md, then read BRIEF.md.",
329964
- "",
329965
- `Your deliverable is a SurveyorOutput JSON file at ${resultFilePath}.`,
329966
- "Follow this procedure exactly:",
329967
- "",
329968
- "STEP 1 \u2014 Orient: navigate to the base URL and 2-3 obvious pages (e.g. the",
329969
- " login page and the main dashboard).",
329970
- `STEP 2 \u2014 IMMEDIATELY use the Write tool to save a first COMPLETE draft of`,
329971
- ` the SurveyorOutput JSON to ${resultFilePath}, based on what you have seen`,
329972
- " so far. featureGroups must be a non-empty array. Do NOT skip this step",
329973
- " and do NOT explore further until this file exists.",
329974
- "STEP 3 \u2014 Continue exploring breadth-first. Visit each remaining page AT",
329975
- " MOST ONCE \u2014 never re-navigate to or re-snapshot a page already seen.",
329976
- ` After every 3-4 new pages, OVERWRITE ${resultFilePath} with an updated,`,
329977
- " complete JSON object.",
329978
- "STEP 4 \u2014 Stop once you have covered the main pages (around 20 page visits",
329979
- ` is plenty) or stop finding new features. Do a final OVERWRITE of`,
329980
- ` ${resultFilePath} with the complete SurveyorOutput, then exit.`,
329981
- "",
329982
- "Budget: about 40 tool calls total. If you ever sense you are running low,",
329983
- `immediately overwrite ${resultFilePath} with your best current JSON and`,
329984
- "exit \u2014 a complete-but-shallow file always beats no file. Never write an",
329985
- "empty featureGroups array and never omit the key. No markdown fences, no",
329986
- "prose in the file \u2014 a single JSON object only."
329987
- ].join("\n");
330395
+ const kickoffPrompt = (0, agentic_survey_prompts_js_1.buildClaudeSurveyKickoffPrompt)(resultFilePath);
329988
330396
  const credentialScrubMap = {};
329989
330397
  for (const c of credentials) {
329990
330398
  credentialScrubMap[c.email] = "<email>";
@@ -333996,7 +334404,7 @@ var require_local_auth_state = __commonJS({
333996
334404
  exports.ensurePrivateDirectory = ensurePrivateDirectory2;
333997
334405
  exports.isValidStorageState = isValidStorageState;
333998
334406
  exports.isValidStorageStateJson = isValidStorageStateJson;
333999
- exports.isValidLocalAuthStateFile = isValidLocalAuthStateFile;
334407
+ exports.isValidLocalAuthStateFile = isValidLocalAuthStateFile2;
334000
334408
  exports.cleanupLocalAuthStateFiles = cleanupLocalAuthStateFiles;
334001
334409
  exports.cleanupExpiredLocalAuthStateFiles = cleanupExpiredLocalAuthStateFiles2;
334002
334410
  exports.localAuthStatePaths = localAuthStatePaths2;
@@ -334010,6 +334418,7 @@ var require_local_auth_state = __commonJS({
334010
334418
  var node_fs_1 = __require("fs");
334011
334419
  var node_crypto_1 = __require("crypto");
334012
334420
  var node_path_1 = __require("path");
334421
+ var shared_1 = (init_dist(), __toCommonJS(dist_exports));
334013
334422
  var auth_recipe_js_1 = require_auth_recipe();
334014
334423
  exports.LOCAL_AUTH_STATE_MAX_AGE_MS = 60 * 60 * 1e3;
334015
334424
  exports.LOCAL_AUTH_STATE_WAIT_MS = 3 * 60 * 1e3;
@@ -334139,7 +334548,7 @@ var require_local_auth_state = __commonJS({
334139
334548
  return false;
334140
334549
  return isValidStorageState(value);
334141
334550
  }
334142
- function isValidLocalAuthStateFile(path) {
334551
+ function isValidLocalAuthStateFile2(path) {
334143
334552
  try {
334144
334553
  return isStorageStateShape(JSON.parse((0, node_fs_1.readFileSync)(path, "utf8")));
334145
334554
  } catch {
@@ -334197,11 +334606,22 @@ var require_local_auth_state = __commonJS({
334197
334606
  lockPath: `${cachePath}.lock`
334198
334607
  };
334199
334608
  }
334609
+ function localAuthStateExpiredByAuthMaterial(path, now) {
334610
+ try {
334611
+ return (0, shared_1.storageStateExpiredBySkew)((0, node_fs_1.readFileSync)(path, "utf8"), now);
334612
+ } catch {
334613
+ return false;
334614
+ }
334615
+ }
334200
334616
  function getValidLocalAuthState2(path, now = Date.now(), maxAgeMs = exports.LOCAL_AUTH_STATE_MAX_AGE_MS) {
334201
334617
  if (!(0, node_fs_1.existsSync)(path))
334202
334618
  return void 0;
334203
334619
  try {
334204
- if (now - (0, node_fs_1.statSync)(path).mtimeMs >= maxAgeMs || !isValidLocalAuthStateFile(path)) {
334620
+ if (now - (0, node_fs_1.statSync)(path).mtimeMs >= maxAgeMs || !isValidLocalAuthStateFile2(path)) {
334621
+ removeFile(path);
334622
+ return void 0;
334623
+ }
334624
+ if (localAuthStateExpiredByAuthMaterial(path, now)) {
334205
334625
  removeFile(path);
334206
334626
  return void 0;
334207
334627
  }
@@ -334248,7 +334668,7 @@ var require_local_auth_state = __commonJS({
334248
334668
  removeFile(capturePath);
334249
334669
  return false;
334250
334670
  }
334251
- if (!(0, node_fs_1.existsSync)(capturePath) || !isValidLocalAuthStateFile(capturePath)) {
334671
+ if (!(0, node_fs_1.existsSync)(capturePath) || !isValidLocalAuthStateFile2(capturePath)) {
334252
334672
  removeFile(capturePath);
334253
334673
  return false;
334254
334674
  }
@@ -334262,7 +334682,7 @@ var require_local_auth_state = __commonJS({
334262
334682
  return false;
334263
334683
  }
334264
334684
  if ((0, node_fs_1.existsSync)(cachePath)) {
334265
- if (!replaceExisting && isValidLocalAuthStateFile(cachePath)) {
334685
+ if (!replaceExisting && isValidLocalAuthStateFile2(cachePath)) {
334266
334686
  removeFile(capturePath);
334267
334687
  return true;
334268
334688
  }
@@ -334279,7 +334699,7 @@ var require_local_auth_state = __commonJS({
334279
334699
  }
334280
334700
  return true;
334281
334701
  } catch {
334282
- const winnerIsValid = (0, node_fs_1.existsSync)(cachePath) && isValidLocalAuthStateFile(cachePath);
334702
+ const winnerIsValid = (0, node_fs_1.existsSync)(cachePath) && isValidLocalAuthStateFile2(cachePath);
334283
334703
  removeFile(capturePath);
334284
334704
  return winnerIsValid;
334285
334705
  }
@@ -346589,9 +347009,9 @@ var require_dist2 = __commonJS({
346589
347009
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p);
346590
347010
  };
346591
347011
  Object.defineProperty(exports, "__esModule", { value: true });
346592
- exports.extractZipSafely = exports.downloadArchiveCapped = exports.detectArchiveKind = exports.acquireRepoForImport = exports.runFrameworkImportOnRunner = exports.getAgenticEngine = exports.resolveRefFromSnapshot = exports.replayAuthRecipeIntoBrowser = exports.runRegisterCaptureRun = exports.runLoginCaptureRun = exports.filterLoginCaptureCredentials = exports.buildLoginCaptureCredentials = exports.isReservedRunnerEnvVar = exports.RESERVED_RUNNER_ENV_VARS = exports.AUTH_MARKER_VERSION = exports.buildHealAuthStamp = exports.extractAuthRegion = exports.upsertAuthBlock = exports.buildAuthRecipe = exports.commandToReplayStep = exports.buildReplayPlan = exports.absoluteRoute = exports.renderReplaySettleArrow = exports.renderReplayClickArrow = exports.renderAuthFn = exports.canonicalizeRoute = exports.executeGrokPerBatchHeal = exports.executeGrokPerTestHeal = exports.headerVal = exports.classifyAuthType = exports.isTextExcerptable = exports.isStaticAssetByExt = exports.isApiCall = exports.buildApiCallSummary = exports.computeMobileHealOutcome = exports.detectMobileStepSecretLeak = exports.redactMobileSecretsFromText = exports.MOBILE_LIVE_HEAL_SYSTEM_PROMPT = exports.MOBILE_SCRIPT_HEAL_SYSTEM_PROMPT = exports.executeMobileLiveHeal = exports.executeMobileTestWithHealing = exports.executeMobileScriptDrivenHeal = exports.prepareMobileScenarioAppState = exports.runMobileGeneratePhase = exports.runMobileAIPlanner = exports.buildMobileSiteMap = exports.formatMobileEvidenceForPlanner = exports.MOBILE_SCENARIO_SYSTEM_PROMPT = exports.MOBILE_SURVEY_SYSTEM_PROMPT = exports.runMobileExplorePhase = void 0;
346593
- exports.decideAppRenderType = exports.detectCanvasApp = exports.detectRateLimitBlock = exports.isSafeCanonicalRedirect = exports.resolveBaseUrlPreflight = exports.checkBaseUrlRedirect = exports.stripJsonFences = exports.scrubCredentials = exports.withAIRetry = exports.snapshotMatchingChildren = exports.findNewChromiumChildPid = exports.killProcessTree = exports.gracefulCloseWithTreeKill = exports.liveBrowserRegistry = exports.REPO_SWITCH_REMOVED_FRACTION = exports.MAX_MANIFEST_FILES = exports.toDiffSummary = exports.isLikelyTestFile = exports.sanitizeRepoRelPath = exports.sha256Hex = exports.hashFiles = exports.enumerateTestFiles = exports.buildNextManifest = exports.detectRepoSwitch = exports.classifyImportDiff = exports.parseImportManifest = exports.REPO_ANALYSIS_SCHEMA_VERSION = exports.validateSourceEndpoints = exports.validateAuthoredTest = exports.validateRepoMapFile = exports.validateRepoMemoryFile = exports.buildTestingStandardsSection = exports.buildRepoAuthorSystemRules = exports.buildRepoAuthorKickoff = exports.buildRepoAuthorContractMd = exports.buildRepoMapSystemRules = exports.buildRepoMapKickoff = exports.buildRepoMapContractMd = exports.buildRepoMemorySystemRules = exports.buildRepoMemoryKickoff = exports.buildRepoMemoryContractMd = exports.runRepoAnalysisOnRunner = exports.IMPORT_SCHEMA_VERSION = exports.validateImportResultFile = exports.validateImportedTest = exports.buildImportSystemRules = exports.buildImportKickoff = exports.buildImportContractMd = exports.MAX_ARCHIVE_BYTES = exports.isUnsafeEntryName = void 0;
346594
- exports.UNSUPPORTED_RENDERER_PREFIX = exports.UnsupportedRendererError = exports.buildCanvasModeAuthoringAddendum = exports.buildUnsupportedRendererMessage = exports.modelSupportsVision = exports.decideCanvasStrategy = void 0;
347012
+ exports.acquireRepoForImport = exports.runFrameworkImportOnRunner = exports.getAgenticEngine = exports.resolveRefFromSnapshot = exports.replayAuthRecipeIntoBrowser = exports.runRegisterCaptureRun = exports.runLoginCaptureRun = exports.filterLoginCaptureCredentials = exports.buildLoginCaptureCredentials = exports.isReservedRunnerEnvVar = exports.RESERVED_RUNNER_ENV_VARS = exports.AUTH_MARKER_VERSION = exports.buildHealAuthStamp = exports.extractAuthRegion = exports.upsertAuthBlock = exports.buildAuthRecipe = exports.commandToReplayStep = exports.buildReplayPlan = exports.absoluteRoute = exports.renderReplaySettleArrow = exports.renderReplayClickArrow = exports.renderAuthFn = exports.filterFeatureGroupsToPathScope = exports.isAuthFlowRoute = exports.deriveSurveyPathScope = exports.canonicalizeRoute = exports.executeGrokPerBatchHeal = exports.executeGrokPerTestHeal = exports.headerVal = exports.classifyAuthType = exports.isTextExcerptable = exports.isStaticAssetByExt = exports.isApiCall = exports.buildApiCallSummary = exports.computeMobileHealOutcome = exports.detectMobileStepSecretLeak = exports.redactMobileSecretsFromText = exports.MOBILE_LIVE_HEAL_SYSTEM_PROMPT = exports.MOBILE_SCRIPT_HEAL_SYSTEM_PROMPT = exports.executeMobileLiveHeal = exports.executeMobileTestWithHealing = exports.executeMobileScriptDrivenHeal = exports.prepareMobileScenarioAppState = exports.runMobileGeneratePhase = exports.runMobileAIPlanner = exports.buildMobileSiteMap = exports.formatMobileEvidenceForPlanner = exports.MOBILE_SCENARIO_SYSTEM_PROMPT = exports.MOBILE_SURVEY_SYSTEM_PROMPT = exports.runMobileExplorePhase = void 0;
347013
+ exports.isSafeCanonicalRedirect = exports.resolveBaseUrlPreflight = exports.checkBaseUrlRedirect = exports.stripJsonFences = exports.scrubCredentials = exports.withAIRetry = exports.snapshotMatchingChildren = exports.findNewChromiumChildPid = exports.killProcessTree = exports.gracefulCloseWithTreeKill = exports.liveBrowserRegistry = exports.REPO_SWITCH_REMOVED_FRACTION = exports.MAX_MANIFEST_FILES = exports.toDiffSummary = exports.isLikelyTestFile = exports.sanitizeRepoRelPath = exports.sha256Hex = exports.hashFiles = exports.enumerateTestFiles = exports.buildNextManifest = exports.detectRepoSwitch = exports.classifyImportDiff = exports.parseImportManifest = exports.REPO_ANALYSIS_SCHEMA_VERSION = exports.validateSourceEndpoints = exports.validateAuthoredTest = exports.validateRepoMapFile = exports.validateRepoMemoryFile = exports.buildTestingStandardsSection = exports.buildRepoAuthorSystemRules = exports.buildRepoAuthorKickoff = exports.buildRepoAuthorContractMd = exports.buildRepoMapSystemRules = exports.buildRepoMapKickoff = exports.buildRepoMapContractMd = exports.buildRepoMemorySystemRules = exports.buildRepoMemoryKickoff = exports.buildRepoMemoryContractMd = exports.runRepoAnalysisOnRunner = exports.IMPORT_SCHEMA_VERSION = exports.validateImportResultFile = exports.validateImportedTest = exports.buildImportSystemRules = exports.buildImportKickoff = exports.buildImportContractMd = exports.MAX_ARCHIVE_BYTES = exports.isUnsafeEntryName = exports.extractZipSafely = exports.downloadArchiveCapped = exports.detectArchiveKind = void 0;
347014
+ exports.UNSUPPORTED_RENDERER_PREFIX = exports.UnsupportedRendererError = exports.buildCanvasModeAuthoringAddendum = exports.buildUnsupportedRendererMessage = exports.modelSupportsVision = exports.decideCanvasStrategy = exports.decideAppRenderType = exports.detectCanvasApp = exports.detectRateLimitBlock = void 0;
346595
347015
  __exportStar(require_ai_provider(), exports);
346596
347016
  __exportStar(require_mobile_types(), exports);
346597
347017
  __exportStar(require_mobile_source_parser(), exports);
@@ -346699,6 +347119,16 @@ var require_dist2 = __commonJS({
346699
347119
  Object.defineProperty(exports, "canonicalizeRoute", { enumerable: true, get: function() {
346700
347120
  return exploration_parser_js_1.canonicalizeRoute;
346701
347121
  } });
347122
+ var survey_scope_js_1 = require_survey_scope();
347123
+ Object.defineProperty(exports, "deriveSurveyPathScope", { enumerable: true, get: function() {
347124
+ return survey_scope_js_1.deriveSurveyPathScope;
347125
+ } });
347126
+ Object.defineProperty(exports, "isAuthFlowRoute", { enumerable: true, get: function() {
347127
+ return survey_scope_js_1.isAuthFlowRoute;
347128
+ } });
347129
+ Object.defineProperty(exports, "filterFeatureGroupsToPathScope", { enumerable: true, get: function() {
347130
+ return survey_scope_js_1.filterFeatureGroupsToPathScope;
347131
+ } });
346702
347132
  var auth_recipe_js_1 = require_auth_recipe();
346703
347133
  Object.defineProperty(exports, "renderAuthFn", { enumerable: true, get: function() {
346704
347134
  return auth_recipe_js_1.renderAuthFn;
@@ -346972,7 +347402,7 @@ var require_package4 = __commonJS({
346972
347402
  "package.json"(exports, module) {
346973
347403
  module.exports = {
346974
347404
  name: "@validate.qa/runner",
346975
- version: "1.0.47",
347405
+ version: "1.0.49",
346976
347406
  description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
346977
347407
  bin: {
346978
347408
  "validate-runner": "dist/cli.js"
@@ -356637,7 +357067,7 @@ ${result2.logs}`);
356637
357067
  });
356638
357068
  } finally {
356639
357069
  if (runAuthPath && authPaths) {
356640
- if ((0, import_runner_core12.getValidLocalAuthState)(runAuthPath)) {
357070
+ if ((0, import_runner_core12.isValidLocalAuthStateFile)(runAuthPath)) {
356641
357071
  const releasePromotionLock = releaseAuthLock ? void 0 : await (0, import_runner_core12.acquireLocalAuthStateLock)(authPaths.lockKey, authPaths.lockPath);
356642
357072
  try {
356643
357073
  const captured = (0, import_runner_core12.promoteLocalAuthStateCapture)(