@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.js CHANGED
@@ -5041,6 +5041,191 @@ var init_auth_block_detector = __esm({
5041
5041
  }
5042
5042
  });
5043
5043
 
5044
+ // ../shared/dist/auth-state-freshness.js
5045
+ function isCsrfTokenName(name) {
5046
+ return /csrf|xsrf|forgery|authenticity|requestverification/.test(name.toLowerCase());
5047
+ }
5048
+ function isAuthTokenName(name) {
5049
+ const n = name.toLowerCase();
5050
+ if (isCsrfTokenName(n))
5051
+ return false;
5052
+ if (/session|auth|jwt|credential|identity|login|token/.test(n))
5053
+ return true;
5054
+ if (/(?:^|[^a-z0-9])(?:sid|sess|account|remember)(?:[^a-z0-9]|$)/.test(n))
5055
+ return true;
5056
+ return [
5057
+ "connect.sid",
5058
+ "jsessionid",
5059
+ "phpsessid",
5060
+ "aspnet",
5061
+ "asp.net",
5062
+ "aspxauth",
5063
+ "sb-",
5064
+ "supabase",
5065
+ "amplify",
5066
+ "cognito",
5067
+ "auth0",
5068
+ "okta",
5069
+ "kc-",
5070
+ "li_at",
5071
+ "ltpatoken",
5072
+ "wordpress_logged_in"
5073
+ ].some((frag) => n.includes(frag));
5074
+ }
5075
+ function isSessionClassName(name) {
5076
+ const n = name.toLowerCase();
5077
+ if (isCsrfTokenName(n))
5078
+ return false;
5079
+ if (/session|sess/.test(n))
5080
+ return true;
5081
+ if (/(?:^|[^a-z0-9])sid(?:[^a-z0-9]|$)/.test(n))
5082
+ return true;
5083
+ return [
5084
+ "connect.sid",
5085
+ "jsessionid",
5086
+ "phpsessid",
5087
+ "aspxauth",
5088
+ "li_at",
5089
+ "ltpatoken",
5090
+ "wordpress_logged_in"
5091
+ ].some((frag) => n.includes(frag));
5092
+ }
5093
+ function decodeBase64UrlToUtf8(segment) {
5094
+ if (typeof atob !== "function")
5095
+ return void 0;
5096
+ const base64 = segment.replace(/-/g, "+").replace(/_/g, "/");
5097
+ const padded = base64.length % 4 === 0 ? base64 : base64 + "=".repeat(4 - base64.length % 4);
5098
+ try {
5099
+ const binary = atob(padded);
5100
+ let percentEncoded = "";
5101
+ for (let i = 0; i < binary.length; i += 1) {
5102
+ percentEncoded += `%${binary.charCodeAt(i).toString(16).padStart(2, "0")}`;
5103
+ }
5104
+ return decodeURIComponent(percentEncoded);
5105
+ } catch {
5106
+ return void 0;
5107
+ }
5108
+ }
5109
+ function jwtExpiryMs(value) {
5110
+ const parts = value.split(".");
5111
+ if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2])
5112
+ return void 0;
5113
+ const json = decodeBase64UrlToUtf8(parts[1]);
5114
+ if (json === void 0)
5115
+ return void 0;
5116
+ try {
5117
+ const payload = JSON.parse(json);
5118
+ const exp = payload?.exp;
5119
+ return typeof exp === "number" && Number.isFinite(exp) && exp > 0 ? exp * 1e3 : void 0;
5120
+ } catch {
5121
+ return void 0;
5122
+ }
5123
+ }
5124
+ function localStorageAuthExpiryMs(value) {
5125
+ const direct = jwtExpiryMs(value);
5126
+ if (direct !== void 0)
5127
+ return direct;
5128
+ let obj;
5129
+ try {
5130
+ obj = JSON.parse(value);
5131
+ } catch {
5132
+ return void 0;
5133
+ }
5134
+ if (!obj || typeof obj !== "object")
5135
+ return void 0;
5136
+ const record = obj;
5137
+ const nested = record.currentSession ?? record.session;
5138
+ const jwtCandidates = [
5139
+ record.access_token,
5140
+ record.accessToken,
5141
+ record.token,
5142
+ record.id_token,
5143
+ record.idToken,
5144
+ nested?.access_token,
5145
+ nested?.accessToken
5146
+ ];
5147
+ for (const candidate of jwtCandidates) {
5148
+ if (typeof candidate === "string") {
5149
+ const exp = jwtExpiryMs(candidate);
5150
+ if (exp !== void 0)
5151
+ return exp;
5152
+ }
5153
+ }
5154
+ const expiresAt = record.expires_at ?? record.expiresAt ?? nested?.expires_at;
5155
+ if (typeof expiresAt === "number" && Number.isFinite(expiresAt) && expiresAt > 0) {
5156
+ return expiresAt < 1e12 ? expiresAt * 1e3 : expiresAt;
5157
+ }
5158
+ return void 0;
5159
+ }
5160
+ function collectAuthTokens(parsed) {
5161
+ const tokens = [];
5162
+ if (!parsed || typeof parsed !== "object")
5163
+ return tokens;
5164
+ const cookies = parsed.cookies;
5165
+ if (Array.isArray(cookies)) {
5166
+ for (const raw of cookies) {
5167
+ if (!raw || typeof raw !== "object")
5168
+ continue;
5169
+ const name = typeof raw.name === "string" ? raw.name : "";
5170
+ const value = typeof raw.value === "string" ? raw.value : "";
5171
+ if (isCsrfTokenName(name))
5172
+ continue;
5173
+ const jwtExp = value ? jwtExpiryMs(value) : void 0;
5174
+ if (jwtExp !== void 0) {
5175
+ tokens.push({ expiryMs: jwtExp, sessionClass: true });
5176
+ continue;
5177
+ }
5178
+ if (isAuthTokenName(name) && typeof raw.expires === "number" && Number.isFinite(raw.expires) && raw.expires > 0) {
5179
+ tokens.push({ expiryMs: raw.expires * 1e3, sessionClass: isSessionClassName(name) });
5180
+ }
5181
+ }
5182
+ }
5183
+ const origins = parsed.origins;
5184
+ if (Array.isArray(origins)) {
5185
+ for (const origin of origins) {
5186
+ if (!origin || typeof origin !== "object" || !Array.isArray(origin.localStorage))
5187
+ continue;
5188
+ for (const entry of origin.localStorage) {
5189
+ if (!entry || typeof entry !== "object")
5190
+ continue;
5191
+ const key = typeof entry.name === "string" ? entry.name : "";
5192
+ const value = typeof entry.value === "string" ? entry.value : "";
5193
+ if (!key || !value || isCsrfTokenName(key) || !isAuthTokenName(key))
5194
+ continue;
5195
+ const exp = localStorageAuthExpiryMs(value);
5196
+ if (exp !== void 0)
5197
+ tokens.push({ expiryMs: exp, sessionClass: true });
5198
+ }
5199
+ }
5200
+ }
5201
+ return tokens;
5202
+ }
5203
+ function storageStateExpiredBySkew(stateJson, nowMs, skewMs = AUTH_STATE_EXPIRY_SKEW_MS) {
5204
+ let parsed;
5205
+ try {
5206
+ parsed = JSON.parse(stateJson);
5207
+ } catch {
5208
+ return false;
5209
+ }
5210
+ const tokens = collectAuthTokens(parsed);
5211
+ if (tokens.length === 0)
5212
+ return false;
5213
+ const margin = Number.isFinite(skewMs) ? Math.max(0, skewMs) : AUTH_STATE_EXPIRY_SKEW_MS;
5214
+ const deadline = nowMs + margin;
5215
+ const anyExpired = tokens.some((t) => t.expiryMs <= deadline);
5216
+ if (!anyExpired)
5217
+ return false;
5218
+ const liveSession = tokens.some((t) => t.expiryMs > deadline && t.sessionClass);
5219
+ return !liveSession;
5220
+ }
5221
+ var AUTH_STATE_EXPIRY_SKEW_MS;
5222
+ var init_auth_state_freshness = __esm({
5223
+ "../shared/dist/auth-state-freshness.js"() {
5224
+ "use strict";
5225
+ AUTH_STATE_EXPIRY_SKEW_MS = 3e4;
5226
+ }
5227
+ });
5228
+
5044
5229
  // ../shared/dist/skip-coverage-detector.js
5045
5230
  function detectSkippedApiCoverage(steps) {
5046
5231
  const allSkipped = steps.length > 0 && steps.every((s) => s.status === "skipped");
@@ -5430,6 +5615,7 @@ __export(dist_exports, {
5430
5615
  API_AUTH_HEADERS_ENV_KEY: () => API_AUTH_HEADERS_ENV_KEY,
5431
5616
  API_PREFIX: () => API_PREFIX,
5432
5617
  AUDIO_CHUNK_DURATION_MS: () => AUDIO_CHUNK_DURATION_MS,
5618
+ AUTH_STATE_EXPIRY_SKEW_MS: () => AUTH_STATE_EXPIRY_SKEW_MS,
5433
5619
  AUTH_TAG_COOKIE_STATE: () => AUTH_TAG_COOKIE_STATE,
5434
5620
  AUTH_TAG_LOGIN_BEARER: () => AUTH_TAG_LOGIN_BEARER,
5435
5621
  AUTH_TAG_LOGIN_COOKIE: () => AUTH_TAG_LOGIN_COOKIE,
@@ -5547,6 +5733,7 @@ __export(dist_exports, {
5547
5733
  isApiTest: () => isApiTest,
5548
5734
  isAppOriginResolutionEnabled: () => isAppOriginResolutionEnabled,
5549
5735
  isAuthReadinessGuardEnabled: () => isAuthReadinessGuardEnabled,
5736
+ isAuthTokenName: () => isAuthTokenName,
5550
5737
  isBlockedEnvVar: () => isBlockedEnvVar,
5551
5738
  isCountedFailure: () => isCountedFailure,
5552
5739
  isCreditPackAmountCents: () => isCreditPackAmountCents,
@@ -5569,6 +5756,7 @@ __export(dist_exports, {
5569
5756
  isValidSttProvider: () => isValidSttProvider,
5570
5757
  isValidTtsModel: () => isValidTtsModel,
5571
5758
  isValidTtsVoiceId: () => isValidTtsVoiceId,
5759
+ jwtExpiryMs: () => jwtExpiryMs,
5572
5760
  mediumForMobilePlatform: () => mediumForMobilePlatform,
5573
5761
  mobileCredentialPlaceholder: () => mobileCredentialPlaceholder,
5574
5762
  mobilePlatformFromMedium: () => mobilePlatformFromMedium,
@@ -5600,6 +5788,7 @@ __export(dist_exports, {
5600
5788
  stepIsDivergentForPlatform: () => stepIsDivergentForPlatform,
5601
5789
  stepsMissingPlatformLocators: () => stepsMissingPlatformLocators,
5602
5790
  stepsNeedingReviewForPlatform: () => stepsNeedingReviewForPlatform,
5791
+ storageStateExpiredBySkew: () => storageStateExpiredBySkew,
5603
5792
  stripEnvironmentalPrefix: () => stripEnvironmentalPrefix,
5604
5793
  stripLoginScaffolding: () => stripLoginScaffolding,
5605
5794
  sttModelsForProvider: () => sttModelsForProvider,
@@ -5630,6 +5819,7 @@ var init_dist = __esm({
5630
5819
  init_origin_domain();
5631
5820
  init_auth_plan();
5632
5821
  init_auth_block_detector();
5822
+ init_auth_state_freshness();
5633
5823
  init_skip_coverage_detector();
5634
5824
  init_redact_needles();
5635
5825
  init_voice_config();
@@ -77098,7 +77288,7 @@ var require_discover_ai_planner = __commonJS({
77098
77288
  "../runner-core/dist/services/discover-ai-planner.js"(exports2) {
77099
77289
  "use strict";
77100
77290
  Object.defineProperty(exports2, "__esModule", { value: true });
77101
- exports2.PLANNER_STRENGTHEN_GATE_PROMPT = exports2.SCENARIO_SYSTEM_PROMPT = void 0;
77291
+ exports2.PLANNER_STRENGTHEN_GATE_PROMPT = exports2.SCENARIO_SYSTEM_PROMPT = exports2.SURVEY_SYSTEM_PROMPT = void 0;
77102
77292
  exports2.getAIPlannerMaxTokens = getAIPlannerMaxTokens;
77103
77293
  exports2.getSnapshotConfidence = getSnapshotConfidence;
77104
77294
  exports2.compressSiteMap = compressSiteMap;
@@ -77634,7 +77824,7 @@ Plan these LOGGED-OUT instead (keep their own establishment steps; do NOT assume
77634
77824
  }
77635
77825
  return sections.join("\n\n");
77636
77826
  }
77637
- 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.
77827
+ exports2.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.
77638
77828
 
77639
77829
  ## Output Discipline
77640
77830
  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.
@@ -77654,6 +77844,7 @@ Return ONLY one valid JSON object matching the schema below. Do NOT include <thi
77654
77844
  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).
77655
77845
  7. Feature descriptions MUST reference actual element labels from the evidence
77656
77846
  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.
77847
+ 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.
77657
77848
 
77658
77849
  ## Output JSON Schema
77659
77850
  {
@@ -78406,7 +78597,7 @@ Keep the exact JSON schema { "featureGroups":[...], "scenarios":[...], "reasonin
78406
78597
  const agentModel = options.modelOverride ? (0, ai_provider_js_1.resolveModelForTask)(options.modelOverride, "agent") : (0, ai_provider_js_1.getModel)("agent");
78407
78598
  const plannerMaxTokens = getAIPlannerMaxTokens(agentModel);
78408
78599
  logs2.push(`AI planner [${context.mode}]: ${siteMap.pages.length} pages, ${evidence.recordedJourneys.length} journeys, budget ${context.testBudget}`);
78409
- const systemPrompt = options.systemPromptOverride ?? (context.mode === "survey" ? SURVEY_SYSTEM_PROMPT : exports2.SCENARIO_SYSTEM_PROMPT);
78600
+ const systemPrompt = options.systemPromptOverride ?? (context.mode === "survey" ? exports2.SURVEY_SYSTEM_PROMPT : exports2.SCENARIO_SYSTEM_PROMPT);
78410
78601
  const userPrompt = options.userPromptOverride ?? formatEvidenceForPlanner(compressed, evidence, context);
78411
78602
  logs2.push(`AI planner: ~${Math.ceil(userPrompt.length / 3.5)} input tokens`);
78412
78603
  const aiClient = options.aiClient ?? (options.modelOverride ? (0, ai_provider_js_1.getRunnerClientForModel)(options.modelOverride) : (0, ai_provider_js_1.getRunnerAIClient)());
@@ -321377,6 +321568,168 @@ var require_evidence_harvester = __commonJS({
321377
321568
  }
321378
321569
  });
321379
321570
 
321571
+ // ../runner-core/dist/services/survey-scope.js
321572
+ var require_survey_scope = __commonJS({
321573
+ "../runner-core/dist/services/survey-scope.js"(exports2) {
321574
+ "use strict";
321575
+ Object.defineProperty(exports2, "__esModule", { value: true });
321576
+ exports2.deriveSurveyPathScope = deriveSurveyPathScope;
321577
+ exports2.isAuthFlowRoute = isAuthFlowRoute;
321578
+ exports2.buildSurveyScopeBoundary = buildSurveyScopeBoundary;
321579
+ exports2.filterFeatureGroupsToPathScope = filterFeatureGroupsToPathScope;
321580
+ var exploration_parser_js_1 = require_exploration_parser();
321581
+ function deriveSurveyPathScope(baseUrl) {
321582
+ const trimmed = (baseUrl ?? "").trim();
321583
+ if (!trimmed)
321584
+ return null;
321585
+ let parsed;
321586
+ try {
321587
+ parsed = new URL(trimmed);
321588
+ } catch {
321589
+ return null;
321590
+ }
321591
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
321592
+ return null;
321593
+ const hashRoute = parsed.hash.startsWith("#/") ? parsed.hash : "";
321594
+ const basePath = (0, exploration_parser_js_1.normalizeRouteForFocusScope)(`${parsed.pathname}${hashRoute}`);
321595
+ if (basePath === "/" || basePath === "/#")
321596
+ return null;
321597
+ return { origin: parsed.origin, basePath };
321598
+ }
321599
+ var AUTH_FLOW_SEGMENTS = /* @__PURE__ */ new Set([
321600
+ "auth",
321601
+ "oauth",
321602
+ "oauth2",
321603
+ "callback",
321604
+ "login",
321605
+ "log-in",
321606
+ "signin",
321607
+ "sign-in",
321608
+ "signup",
321609
+ "sign-up",
321610
+ "register",
321611
+ "logout",
321612
+ "log-out",
321613
+ "signout",
321614
+ "sign-out",
321615
+ "sso",
321616
+ "saml",
321617
+ "oidc"
321618
+ ]);
321619
+ function isAuthFlowRoute(route2) {
321620
+ const normalized = (0, exploration_parser_js_1.normalizeRouteForFocusScope)(route2).toLowerCase();
321621
+ return normalized.split(/[/#]/).some((segment) => AUTH_FLOW_SEGMENTS.has(segment));
321622
+ }
321623
+ function routePathForScope(route2, origin) {
321624
+ const trimmed = (route2 ?? "").trim();
321625
+ if (trimmed.startsWith("//")) {
321626
+ try {
321627
+ const parsed = new URL(`https:${trimmed}`);
321628
+ if (parsed.host !== new URL(origin).host)
321629
+ return null;
321630
+ return `${parsed.pathname}${parsed.search}${parsed.hash}`;
321631
+ } catch {
321632
+ return null;
321633
+ }
321634
+ }
321635
+ if (/^https?:\/\//i.test(trimmed)) {
321636
+ try {
321637
+ const parsed = new URL(trimmed);
321638
+ if (parsed.origin !== origin)
321639
+ return null;
321640
+ return `${parsed.pathname}${parsed.search}${parsed.hash}`;
321641
+ } catch {
321642
+ return trimmed;
321643
+ }
321644
+ }
321645
+ if (trimmed.startsWith("/") && trimmed.includes("..")) {
321646
+ try {
321647
+ const resolved = new URL(trimmed, "https://scope.invalid");
321648
+ return `${resolved.pathname}${resolved.search}${resolved.hash}`;
321649
+ } catch {
321650
+ return trimmed;
321651
+ }
321652
+ }
321653
+ return trimmed;
321654
+ }
321655
+ function buildSurveyScopeBoundary(baseUrl) {
321656
+ const scope = deriveSurveyPathScope(baseUrl);
321657
+ const lines = [
321658
+ '- **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'
321659
+ ];
321660
+ if (scope) {
321661
+ 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`);
321662
+ }
321663
+ return lines.join("\n");
321664
+ }
321665
+ function filterFeatureGroupsToPathScope(featureGroups, baseUrl) {
321666
+ const scope = deriveSurveyPathScope(baseUrl);
321667
+ if (!scope) {
321668
+ return { kept: featureGroups, droppedGroups: [], trimmedRoutes: [], trimmedPages: [], basePath: null };
321669
+ }
321670
+ const withinBase = (route2) => {
321671
+ const path = routePathForScope(route2, scope.origin);
321672
+ if (path === null)
321673
+ return false;
321674
+ if ((0, exploration_parser_js_1.routeIsWithinFocusAreas)(path, [scope.basePath]))
321675
+ return true;
321676
+ const norm = (0, exploration_parser_js_1.normalizeRouteForFocusScope)(path);
321677
+ return norm === `${scope.basePath}#` || norm.startsWith(`${scope.basePath}#/`);
321678
+ };
321679
+ const allowedAuthFlow = (route2) => {
321680
+ const path = routePathForScope(route2, scope.origin);
321681
+ return path !== null && isAuthFlowRoute(path);
321682
+ };
321683
+ const kept = [];
321684
+ const droppedGroups = [];
321685
+ const trimmedRoutes = [];
321686
+ const trimmedPages = [];
321687
+ for (const group of featureGroups) {
321688
+ const routes = Array.isArray(group.routes) ? group.routes : [];
321689
+ const keptRoutes = [];
321690
+ const removedRoutes = [];
321691
+ let anchored = false;
321692
+ for (const route2 of routes) {
321693
+ if (withinBase(route2)) {
321694
+ anchored = true;
321695
+ keptRoutes.push(route2);
321696
+ } else if (allowedAuthFlow(route2)) {
321697
+ keptRoutes.push(route2);
321698
+ } else {
321699
+ removedRoutes.push(route2);
321700
+ }
321701
+ }
321702
+ if (!anchored) {
321703
+ droppedGroups.push({ name: group.name, routes: [...routes] });
321704
+ continue;
321705
+ }
321706
+ let pages = group.pages;
321707
+ const removedPages = [];
321708
+ if (Array.isArray(group.pages)) {
321709
+ const keptPages = [];
321710
+ for (const page of group.pages) {
321711
+ if (withinBase(page.route) || allowedAuthFlow(page.route))
321712
+ keptPages.push(page);
321713
+ else
321714
+ removedPages.push(page.route);
321715
+ }
321716
+ pages = keptPages;
321717
+ }
321718
+ if (removedRoutes.length === 0 && removedPages.length === 0) {
321719
+ kept.push(group);
321720
+ } else {
321721
+ kept.push({ ...group, routes: keptRoutes, ...group.pages !== void 0 ? { pages } : {} });
321722
+ if (removedRoutes.length > 0)
321723
+ trimmedRoutes.push({ name: group.name, routes: removedRoutes });
321724
+ if (removedPages.length > 0)
321725
+ trimmedPages.push({ name: group.name, routes: removedPages });
321726
+ }
321727
+ }
321728
+ return { kept, droppedGroups, trimmedRoutes, trimmedPages, basePath: scope.basePath };
321729
+ }
321730
+ }
321731
+ });
321732
+
321380
321733
  // ../runner-core/dist/services/discover-explorer.js
321381
321734
  var require_discover_explorer = __commonJS({
321382
321735
  "../runner-core/dist/services/discover-explorer.js"(exports2) {
@@ -321418,6 +321771,7 @@ var require_discover_explorer = __commonJS({
321418
321771
  var evidence_harvester_js_1 = require_evidence_harvester();
321419
321772
  var exploration_parser_js_1 = require_exploration_parser();
321420
321773
  var survey_link_frontier_js_1 = require_survey_link_frontier();
321774
+ var survey_scope_js_1 = require_survey_scope();
321421
321775
  var snapshot_context_compactor_js_2 = require_snapshot_context_compactor();
321422
321776
  Object.defineProperty(exports2, "formatMCPToolResult", { enumerable: true, get: function() {
321423
321777
  return snapshot_context_compactor_js_2.formatMCPToolResult;
@@ -321845,6 +322199,7 @@ If a snapshot's readiness section exposes \`loading_indicators\`, \`skeleton_con
321845
322199
  - **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
321846
322200
  - **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
321847
322201
  - **Stay on the same origin** as the base URL. Note external links in elements but do not follow them
322202
+ ${(0, survey_scope_js_1.buildSurveyScopeBoundary)(baseUrl)}
321848
322203
  - **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
321849
322204
  - **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
321850
322205
  - **Parameterized routes**: /users/1 and /users/2 are the same page template. Visit ONE instance, not all of them
@@ -325343,6 +325698,8 @@ var require_agentic_survey_prompts = __commonJS({
325343
325698
  exports2.validateSurveyorOutput = validateSurveyorOutput;
325344
325699
  exports2.buildSurveyAgentsMd = buildSurveyAgentsMd;
325345
325700
  exports2.buildSurveyBriefMd = buildSurveyBriefMd;
325701
+ exports2.buildClaudeSurveyKickoffPrompt = buildClaudeSurveyKickoffPrompt;
325702
+ var survey_scope_js_1 = require_survey_scope();
325346
325703
  var CRITICALITY_VALUES = ["critical", "high", "medium", "low"];
325347
325704
  function isString(v) {
325348
325705
  return typeof v === "string";
@@ -325522,9 +325879,40 @@ var require_agentic_survey_prompts = __commonJS({
325522
325879
  '- Click "delete" or destructive admin actions.',
325523
325880
  "- Leave the baseUrl domain except briefly for OAuth providers.",
325524
325881
  "",
325882
+ "## Navigation scope",
325883
+ "",
325884
+ "The Base URL in BRIEF.md is the ENTRY SCOPE \u2014 its full path, not just",
325885
+ "its domain. Open the exact Base URL first. From there, navigate ONLY to:",
325886
+ "",
325887
+ "- URLs the app actually presented to you: a link, button, or form",
325888
+ " visible in a snapshot of an in-scope page. (`browser_navigate` to a",
325889
+ " URL you saw as a link href is fine \u2014 composing one yourself is not.)",
325890
+ "- Redirects the app itself performs during those interactions,",
325891
+ " including login/OAuth/SSO callback routes needed to complete a flow.",
325892
+ "- The Base URL itself, to return to the start of the flow.",
325893
+ "",
325894
+ "NEVER invent a URL. Do NOT:",
325895
+ "- Parent-walk: truncating the Base URL path (base `/shop/checkout` \u2192",
325896
+ " visiting `/shop` or `/`) is out of scope even though it is same-origin.",
325897
+ "- Sibling-walk: do not visit or catalog neighbours of the Base URL path",
325898
+ " that in-scope pages never linked to.",
325899
+ '- Guess "common" routes (`/admin`, `/about`) or probe made-up routes',
325900
+ " (`/nonexistent`) to see what the error handling does. Do not create",
325901
+ " featureGroups for generic error/404 pages unless the app itself",
325902
+ " routes users there as part of a real flow.",
325903
+ "- Any other URL surgery: if the UI did not present it and the app did",
325904
+ " not redirect you to it, it does not exist for this survey.",
325905
+ "",
325906
+ "When the Base URL path is non-root (e.g. `/shop/checkout`, `/portal/billing`),",
325907
+ "the operator is auditing THAT flow in isolation: every featureGroup must",
325908
+ "describe that flow. Auth pages the flow pushes you through belong inside",
325909
+ "that flow's featureGroup \u2014 do not mint separate featureGroups for them.",
325910
+ "When the Base URL is the origin root, survey the whole app \u2014 the",
325911
+ "no-guessing rule still applies: reach pages through the UI.",
325912
+ "",
325525
325913
  "## Exploration philosophy",
325526
325914
  "",
325527
- "- Breadth before depth \u2014 map the whole surface before drilling in.",
325915
+ "- Breadth before depth \u2014 map the whole in-scope surface before drilling in.",
325528
325916
  "- Authenticated and unauthenticated surfaces are different products;",
325529
325917
  " explore both when credentials are available.",
325530
325918
  "- Prefer observing over interacting: open a page, snapshot, note the",
@@ -325619,6 +326007,17 @@ var require_agentic_survey_prompts = __commonJS({
325619
326007
  lines.push("");
325620
326008
  lines.push(`**Base URL:** ${input.baseUrl}`);
325621
326009
  lines.push("");
326010
+ const pathScope = (0, survey_scope_js_1.deriveSurveyPathScope)(input.baseUrl);
326011
+ if (pathScope) {
326012
+ lines.push(`**Entry scope:** \`${pathScope.basePath}\` \u2014 this is a SCOPED survey of one flow.`);
326013
+ lines.push("");
326014
+ lines.push(`Everything you catalog must live under \`${pathScope.basePath}\`, or be a page`);
326015
+ lines.push("this flow itself pushes you through (its own login page, an auth/OAuth");
326016
+ lines.push("callback, a post-submit redirect). Parent and sibling paths on this host");
326017
+ lines.push("are OTHER fixtures/apps and are out of scope \u2014 do not visit them, do not");
326018
+ lines.push("catalog them, do not mint featureGroups for them.");
326019
+ lines.push("");
326020
+ }
325622
326021
  if (input.appBrief && input.appBrief.trim().length > 0) {
325623
326022
  lines.push("## App brief from the operator");
325624
326023
  lines.push("");
@@ -325688,6 +326087,37 @@ var require_agentic_survey_prompts = __commonJS({
325688
326087
  lines.push("");
325689
326088
  return lines.join("\n");
325690
326089
  }
326090
+ function buildClaudeSurveyKickoffPrompt(resultFilePath) {
326091
+ return [
326092
+ "Read AGENTS.md, then read BRIEF.md.",
326093
+ "",
326094
+ `Your deliverable is a SurveyorOutput JSON file at ${resultFilePath}.`,
326095
+ "Follow this procedure exactly:",
326096
+ "",
326097
+ "STEP 1 \u2014 Orient: navigate to the EXACT Base URL from BRIEF.md and",
326098
+ " snapshot it, then open 2-3 pages its UI visibly links to (e.g. a login",
326099
+ " link it presents). Navigate only to URLs the page actually presented \u2014",
326100
+ ' never guessed parent, sibling, or "common" routes (see "Navigation',
326101
+ ' scope" in AGENTS.md).',
326102
+ `STEP 2 \u2014 IMMEDIATELY use the Write tool to save a first COMPLETE draft of`,
326103
+ ` the SurveyorOutput JSON to ${resultFilePath}, based on what you have seen`,
326104
+ " so far. featureGroups must be a non-empty array. Do NOT skip this step",
326105
+ " and do NOT explore further until this file exists.",
326106
+ "STEP 3 \u2014 Continue exploring breadth-first. Visit each remaining page AT",
326107
+ " MOST ONCE \u2014 never re-navigate to or re-snapshot a page already seen.",
326108
+ ` After every 3-4 new pages, OVERWRITE ${resultFilePath} with an updated,`,
326109
+ " complete JSON object.",
326110
+ "STEP 4 \u2014 Stop once you have covered the main in-scope pages (around 20",
326111
+ ` page visits is plenty) or stop finding new features. Do a final OVERWRITE`,
326112
+ ` of ${resultFilePath} with the complete SurveyorOutput, then exit.`,
326113
+ "",
326114
+ "Budget: about 40 tool calls total. If you ever sense you are running low,",
326115
+ `immediately overwrite ${resultFilePath} with your best current JSON and`,
326116
+ "exit \u2014 a complete-but-shallow file always beats no file. Never write an",
326117
+ "empty featureGroups array and never omit the key. No markdown fences, no",
326118
+ "prose in the file \u2014 a single JSON object only."
326119
+ ].join("\n");
326120
+ }
325691
326121
  }
325692
326122
  });
325693
326123
 
@@ -326238,6 +326668,9 @@ ${(0, inbox_tools_js_1.buildInboxAgentsGuidance)(inboxReg.email, inboxReg.passwo
326238
326668
  const kickoffPrompt = [
326239
326669
  "Read AGENTS.md, then read BRIEF.md.",
326240
326670
  "Use the playwright MCP tools to explore the site.",
326671
+ 'Follow the "Navigation scope" rules in AGENTS.md: start at the exact Base',
326672
+ 'URL and never navigate to guessed parent, sibling, or "common" routes \u2014',
326673
+ "only URLs the pages actually present (or redirects the app performs).",
326241
326674
  `When done, write your JSON output to ${resultFilePath} as a single JSON object matching the SurveyorOutput schema in AGENTS.md.`,
326242
326675
  "Then exit."
326243
326676
  ].join("\n");
@@ -329954,32 +330387,7 @@ ${(0, inbox_tools_js_1.buildInboxAgentsGuidance)(inboxReg.email, inboxReg.passwo
329954
330387
  await (0, promises_1.writeFile)((0, node_path_1.join)(runDir, "AGENTS.md"), surveyAgentsMd, "utf8");
329955
330388
  await (0, promises_1.writeFile)((0, node_path_1.join)(runDir, "BRIEF.md"), (0, agentic_survey_prompts_js_1.buildSurveyBriefMd)(briefInput), "utf8");
329956
330389
  log2("Wrote AGENTS.md + BRIEF.md");
329957
- const kickoffPrompt = [
329958
- "Read AGENTS.md, then read BRIEF.md.",
329959
- "",
329960
- `Your deliverable is a SurveyorOutput JSON file at ${resultFilePath}.`,
329961
- "Follow this procedure exactly:",
329962
- "",
329963
- "STEP 1 \u2014 Orient: navigate to the base URL and 2-3 obvious pages (e.g. the",
329964
- " login page and the main dashboard).",
329965
- `STEP 2 \u2014 IMMEDIATELY use the Write tool to save a first COMPLETE draft of`,
329966
- ` the SurveyorOutput JSON to ${resultFilePath}, based on what you have seen`,
329967
- " so far. featureGroups must be a non-empty array. Do NOT skip this step",
329968
- " and do NOT explore further until this file exists.",
329969
- "STEP 3 \u2014 Continue exploring breadth-first. Visit each remaining page AT",
329970
- " MOST ONCE \u2014 never re-navigate to or re-snapshot a page already seen.",
329971
- ` After every 3-4 new pages, OVERWRITE ${resultFilePath} with an updated,`,
329972
- " complete JSON object.",
329973
- "STEP 4 \u2014 Stop once you have covered the main pages (around 20 page visits",
329974
- ` is plenty) or stop finding new features. Do a final OVERWRITE of`,
329975
- ` ${resultFilePath} with the complete SurveyorOutput, then exit.`,
329976
- "",
329977
- "Budget: about 40 tool calls total. If you ever sense you are running low,",
329978
- `immediately overwrite ${resultFilePath} with your best current JSON and`,
329979
- "exit \u2014 a complete-but-shallow file always beats no file. Never write an",
329980
- "empty featureGroups array and never omit the key. No markdown fences, no",
329981
- "prose in the file \u2014 a single JSON object only."
329982
- ].join("\n");
330390
+ const kickoffPrompt = (0, agentic_survey_prompts_js_1.buildClaudeSurveyKickoffPrompt)(resultFilePath);
329983
330391
  const credentialScrubMap = {};
329984
330392
  for (const c of credentials) {
329985
330393
  credentialScrubMap[c.email] = "<email>";
@@ -333991,7 +334399,7 @@ var require_local_auth_state = __commonJS({
333991
334399
  exports2.ensurePrivateDirectory = ensurePrivateDirectory2;
333992
334400
  exports2.isValidStorageState = isValidStorageState;
333993
334401
  exports2.isValidStorageStateJson = isValidStorageStateJson;
333994
- exports2.isValidLocalAuthStateFile = isValidLocalAuthStateFile;
334402
+ exports2.isValidLocalAuthStateFile = isValidLocalAuthStateFile2;
333995
334403
  exports2.cleanupLocalAuthStateFiles = cleanupLocalAuthStateFiles;
333996
334404
  exports2.cleanupExpiredLocalAuthStateFiles = cleanupExpiredLocalAuthStateFiles2;
333997
334405
  exports2.localAuthStatePaths = localAuthStatePaths2;
@@ -334005,6 +334413,7 @@ var require_local_auth_state = __commonJS({
334005
334413
  var node_fs_1 = require("fs");
334006
334414
  var node_crypto_1 = require("crypto");
334007
334415
  var node_path_1 = require("path");
334416
+ var shared_1 = (init_dist(), __toCommonJS(dist_exports));
334008
334417
  var auth_recipe_js_1 = require_auth_recipe();
334009
334418
  exports2.LOCAL_AUTH_STATE_MAX_AGE_MS = 60 * 60 * 1e3;
334010
334419
  exports2.LOCAL_AUTH_STATE_WAIT_MS = 3 * 60 * 1e3;
@@ -334134,7 +334543,7 @@ var require_local_auth_state = __commonJS({
334134
334543
  return false;
334135
334544
  return isValidStorageState(value);
334136
334545
  }
334137
- function isValidLocalAuthStateFile(path) {
334546
+ function isValidLocalAuthStateFile2(path) {
334138
334547
  try {
334139
334548
  return isStorageStateShape(JSON.parse((0, node_fs_1.readFileSync)(path, "utf8")));
334140
334549
  } catch {
@@ -334192,11 +334601,22 @@ var require_local_auth_state = __commonJS({
334192
334601
  lockPath: `${cachePath}.lock`
334193
334602
  };
334194
334603
  }
334604
+ function localAuthStateExpiredByAuthMaterial(path, now) {
334605
+ try {
334606
+ return (0, shared_1.storageStateExpiredBySkew)((0, node_fs_1.readFileSync)(path, "utf8"), now);
334607
+ } catch {
334608
+ return false;
334609
+ }
334610
+ }
334195
334611
  function getValidLocalAuthState2(path, now = Date.now(), maxAgeMs = exports2.LOCAL_AUTH_STATE_MAX_AGE_MS) {
334196
334612
  if (!(0, node_fs_1.existsSync)(path))
334197
334613
  return void 0;
334198
334614
  try {
334199
- if (now - (0, node_fs_1.statSync)(path).mtimeMs >= maxAgeMs || !isValidLocalAuthStateFile(path)) {
334615
+ if (now - (0, node_fs_1.statSync)(path).mtimeMs >= maxAgeMs || !isValidLocalAuthStateFile2(path)) {
334616
+ removeFile(path);
334617
+ return void 0;
334618
+ }
334619
+ if (localAuthStateExpiredByAuthMaterial(path, now)) {
334200
334620
  removeFile(path);
334201
334621
  return void 0;
334202
334622
  }
@@ -334243,7 +334663,7 @@ var require_local_auth_state = __commonJS({
334243
334663
  removeFile(capturePath);
334244
334664
  return false;
334245
334665
  }
334246
- if (!(0, node_fs_1.existsSync)(capturePath) || !isValidLocalAuthStateFile(capturePath)) {
334666
+ if (!(0, node_fs_1.existsSync)(capturePath) || !isValidLocalAuthStateFile2(capturePath)) {
334247
334667
  removeFile(capturePath);
334248
334668
  return false;
334249
334669
  }
@@ -334257,7 +334677,7 @@ var require_local_auth_state = __commonJS({
334257
334677
  return false;
334258
334678
  }
334259
334679
  if ((0, node_fs_1.existsSync)(cachePath)) {
334260
- if (!replaceExisting && isValidLocalAuthStateFile(cachePath)) {
334680
+ if (!replaceExisting && isValidLocalAuthStateFile2(cachePath)) {
334261
334681
  removeFile(capturePath);
334262
334682
  return true;
334263
334683
  }
@@ -334274,7 +334694,7 @@ var require_local_auth_state = __commonJS({
334274
334694
  }
334275
334695
  return true;
334276
334696
  } catch {
334277
- const winnerIsValid = (0, node_fs_1.existsSync)(cachePath) && isValidLocalAuthStateFile(cachePath);
334697
+ const winnerIsValid = (0, node_fs_1.existsSync)(cachePath) && isValidLocalAuthStateFile2(cachePath);
334278
334698
  removeFile(capturePath);
334279
334699
  return winnerIsValid;
334280
334700
  }
@@ -346584,9 +347004,9 @@ var require_dist2 = __commonJS({
346584
347004
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
346585
347005
  };
346586
347006
  Object.defineProperty(exports2, "__esModule", { value: true });
346587
- exports2.extractZipSafely = exports2.downloadArchiveCapped = exports2.detectArchiveKind = exports2.acquireRepoForImport = exports2.runFrameworkImportOnRunner = exports2.getAgenticEngine = exports2.resolveRefFromSnapshot = exports2.replayAuthRecipeIntoBrowser = exports2.runRegisterCaptureRun = exports2.runLoginCaptureRun = exports2.filterLoginCaptureCredentials = exports2.buildLoginCaptureCredentials = exports2.isReservedRunnerEnvVar = exports2.RESERVED_RUNNER_ENV_VARS = exports2.AUTH_MARKER_VERSION = exports2.buildHealAuthStamp = exports2.extractAuthRegion = exports2.upsertAuthBlock = exports2.buildAuthRecipe = exports2.commandToReplayStep = exports2.buildReplayPlan = exports2.absoluteRoute = exports2.renderReplaySettleArrow = exports2.renderReplayClickArrow = exports2.renderAuthFn = exports2.canonicalizeRoute = exports2.executeGrokPerBatchHeal = exports2.executeGrokPerTestHeal = exports2.headerVal = exports2.classifyAuthType = exports2.isTextExcerptable = exports2.isStaticAssetByExt = exports2.isApiCall = exports2.buildApiCallSummary = exports2.computeMobileHealOutcome = exports2.detectMobileStepSecretLeak = exports2.redactMobileSecretsFromText = exports2.MOBILE_LIVE_HEAL_SYSTEM_PROMPT = exports2.MOBILE_SCRIPT_HEAL_SYSTEM_PROMPT = exports2.executeMobileLiveHeal = exports2.executeMobileTestWithHealing = exports2.executeMobileScriptDrivenHeal = exports2.prepareMobileScenarioAppState = exports2.runMobileGeneratePhase = exports2.runMobileAIPlanner = exports2.buildMobileSiteMap = exports2.formatMobileEvidenceForPlanner = exports2.MOBILE_SCENARIO_SYSTEM_PROMPT = exports2.MOBILE_SURVEY_SYSTEM_PROMPT = exports2.runMobileExplorePhase = void 0;
346588
- exports2.decideAppRenderType = exports2.detectCanvasApp = exports2.detectRateLimitBlock = exports2.isSafeCanonicalRedirect = exports2.resolveBaseUrlPreflight = exports2.checkBaseUrlRedirect = exports2.stripJsonFences = exports2.scrubCredentials = exports2.withAIRetry = exports2.snapshotMatchingChildren = exports2.findNewChromiumChildPid = exports2.killProcessTree = exports2.gracefulCloseWithTreeKill = exports2.liveBrowserRegistry = exports2.REPO_SWITCH_REMOVED_FRACTION = exports2.MAX_MANIFEST_FILES = exports2.toDiffSummary = exports2.isLikelyTestFile = exports2.sanitizeRepoRelPath = exports2.sha256Hex = exports2.hashFiles = exports2.enumerateTestFiles = exports2.buildNextManifest = exports2.detectRepoSwitch = exports2.classifyImportDiff = exports2.parseImportManifest = exports2.REPO_ANALYSIS_SCHEMA_VERSION = exports2.validateSourceEndpoints = exports2.validateAuthoredTest = exports2.validateRepoMapFile = exports2.validateRepoMemoryFile = exports2.buildTestingStandardsSection = exports2.buildRepoAuthorSystemRules = exports2.buildRepoAuthorKickoff = exports2.buildRepoAuthorContractMd = exports2.buildRepoMapSystemRules = exports2.buildRepoMapKickoff = exports2.buildRepoMapContractMd = exports2.buildRepoMemorySystemRules = exports2.buildRepoMemoryKickoff = exports2.buildRepoMemoryContractMd = exports2.runRepoAnalysisOnRunner = exports2.IMPORT_SCHEMA_VERSION = exports2.validateImportResultFile = exports2.validateImportedTest = exports2.buildImportSystemRules = exports2.buildImportKickoff = exports2.buildImportContractMd = exports2.MAX_ARCHIVE_BYTES = exports2.isUnsafeEntryName = void 0;
346589
- exports2.UNSUPPORTED_RENDERER_PREFIX = exports2.UnsupportedRendererError = exports2.buildCanvasModeAuthoringAddendum = exports2.buildUnsupportedRendererMessage = exports2.modelSupportsVision = exports2.decideCanvasStrategy = void 0;
347007
+ exports2.acquireRepoForImport = exports2.runFrameworkImportOnRunner = exports2.getAgenticEngine = exports2.resolveRefFromSnapshot = exports2.replayAuthRecipeIntoBrowser = exports2.runRegisterCaptureRun = exports2.runLoginCaptureRun = exports2.filterLoginCaptureCredentials = exports2.buildLoginCaptureCredentials = exports2.isReservedRunnerEnvVar = exports2.RESERVED_RUNNER_ENV_VARS = exports2.AUTH_MARKER_VERSION = exports2.buildHealAuthStamp = exports2.extractAuthRegion = exports2.upsertAuthBlock = exports2.buildAuthRecipe = exports2.commandToReplayStep = exports2.buildReplayPlan = exports2.absoluteRoute = exports2.renderReplaySettleArrow = exports2.renderReplayClickArrow = exports2.renderAuthFn = exports2.filterFeatureGroupsToPathScope = exports2.isAuthFlowRoute = exports2.deriveSurveyPathScope = exports2.canonicalizeRoute = exports2.executeGrokPerBatchHeal = exports2.executeGrokPerTestHeal = exports2.headerVal = exports2.classifyAuthType = exports2.isTextExcerptable = exports2.isStaticAssetByExt = exports2.isApiCall = exports2.buildApiCallSummary = exports2.computeMobileHealOutcome = exports2.detectMobileStepSecretLeak = exports2.redactMobileSecretsFromText = exports2.MOBILE_LIVE_HEAL_SYSTEM_PROMPT = exports2.MOBILE_SCRIPT_HEAL_SYSTEM_PROMPT = exports2.executeMobileLiveHeal = exports2.executeMobileTestWithHealing = exports2.executeMobileScriptDrivenHeal = exports2.prepareMobileScenarioAppState = exports2.runMobileGeneratePhase = exports2.runMobileAIPlanner = exports2.buildMobileSiteMap = exports2.formatMobileEvidenceForPlanner = exports2.MOBILE_SCENARIO_SYSTEM_PROMPT = exports2.MOBILE_SURVEY_SYSTEM_PROMPT = exports2.runMobileExplorePhase = void 0;
347008
+ exports2.isSafeCanonicalRedirect = exports2.resolveBaseUrlPreflight = exports2.checkBaseUrlRedirect = exports2.stripJsonFences = exports2.scrubCredentials = exports2.withAIRetry = exports2.snapshotMatchingChildren = exports2.findNewChromiumChildPid = exports2.killProcessTree = exports2.gracefulCloseWithTreeKill = exports2.liveBrowserRegistry = exports2.REPO_SWITCH_REMOVED_FRACTION = exports2.MAX_MANIFEST_FILES = exports2.toDiffSummary = exports2.isLikelyTestFile = exports2.sanitizeRepoRelPath = exports2.sha256Hex = exports2.hashFiles = exports2.enumerateTestFiles = exports2.buildNextManifest = exports2.detectRepoSwitch = exports2.classifyImportDiff = exports2.parseImportManifest = exports2.REPO_ANALYSIS_SCHEMA_VERSION = exports2.validateSourceEndpoints = exports2.validateAuthoredTest = exports2.validateRepoMapFile = exports2.validateRepoMemoryFile = exports2.buildTestingStandardsSection = exports2.buildRepoAuthorSystemRules = exports2.buildRepoAuthorKickoff = exports2.buildRepoAuthorContractMd = exports2.buildRepoMapSystemRules = exports2.buildRepoMapKickoff = exports2.buildRepoMapContractMd = exports2.buildRepoMemorySystemRules = exports2.buildRepoMemoryKickoff = exports2.buildRepoMemoryContractMd = exports2.runRepoAnalysisOnRunner = exports2.IMPORT_SCHEMA_VERSION = exports2.validateImportResultFile = exports2.validateImportedTest = exports2.buildImportSystemRules = exports2.buildImportKickoff = exports2.buildImportContractMd = exports2.MAX_ARCHIVE_BYTES = exports2.isUnsafeEntryName = exports2.extractZipSafely = exports2.downloadArchiveCapped = exports2.detectArchiveKind = void 0;
347009
+ exports2.UNSUPPORTED_RENDERER_PREFIX = exports2.UnsupportedRendererError = exports2.buildCanvasModeAuthoringAddendum = exports2.buildUnsupportedRendererMessage = exports2.modelSupportsVision = exports2.decideCanvasStrategy = exports2.decideAppRenderType = exports2.detectCanvasApp = exports2.detectRateLimitBlock = void 0;
346590
347010
  __exportStar(require_ai_provider(), exports2);
346591
347011
  __exportStar(require_mobile_types(), exports2);
346592
347012
  __exportStar(require_mobile_source_parser(), exports2);
@@ -346694,6 +347114,16 @@ var require_dist2 = __commonJS({
346694
347114
  Object.defineProperty(exports2, "canonicalizeRoute", { enumerable: true, get: function() {
346695
347115
  return exploration_parser_js_1.canonicalizeRoute;
346696
347116
  } });
347117
+ var survey_scope_js_1 = require_survey_scope();
347118
+ Object.defineProperty(exports2, "deriveSurveyPathScope", { enumerable: true, get: function() {
347119
+ return survey_scope_js_1.deriveSurveyPathScope;
347120
+ } });
347121
+ Object.defineProperty(exports2, "isAuthFlowRoute", { enumerable: true, get: function() {
347122
+ return survey_scope_js_1.isAuthFlowRoute;
347123
+ } });
347124
+ Object.defineProperty(exports2, "filterFeatureGroupsToPathScope", { enumerable: true, get: function() {
347125
+ return survey_scope_js_1.filterFeatureGroupsToPathScope;
347126
+ } });
346697
347127
  var auth_recipe_js_1 = require_auth_recipe();
346698
347128
  Object.defineProperty(exports2, "renderAuthFn", { enumerable: true, get: function() {
346699
347129
  return auth_recipe_js_1.renderAuthFn;
@@ -346967,7 +347397,7 @@ var require_package4 = __commonJS({
346967
347397
  "package.json"(exports2, module2) {
346968
347398
  module2.exports = {
346969
347399
  name: "@validate.qa/runner",
346970
- version: "1.0.47",
347400
+ version: "1.0.49",
346971
347401
  description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
346972
347402
  bin: {
346973
347403
  "validate-runner": "dist/cli.js"
@@ -356629,7 +357059,7 @@ ${result2.logs}`);
356629
357059
  });
356630
357060
  } finally {
356631
357061
  if (runAuthPath && authPaths) {
356632
- if ((0, import_runner_core12.getValidLocalAuthState)(runAuthPath)) {
357062
+ if ((0, import_runner_core12.isValidLocalAuthStateFile)(runAuthPath)) {
356633
357063
  const releasePromotionLock = releaseAuthLock ? void 0 : await (0, import_runner_core12.acquireLocalAuthStateLock)(authPaths.lockKey, authPaths.lockPath);
356634
357064
  try {
356635
357065
  const captured = (0, import_runner_core12.promoteLocalAuthStateCapture)(