@trusty-squire/mcp 1.1.28 → 1.1.29-rc.2

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 (86) hide show
  1. package/dist/api-client.d.ts +6 -0
  2. package/dist/api-client.d.ts.map +1 -1
  3. package/dist/api-client.js.map +1 -1
  4. package/dist/bot/act/act.d.ts.map +1 -1
  5. package/dist/bot/act/act.js +15 -11
  6. package/dist/bot/act/act.js.map +1 -1
  7. package/dist/bot/browser.d.ts +0 -29
  8. package/dist/bot/browser.d.ts.map +1 -1
  9. package/dist/bot/browser.js +2 -747
  10. package/dist/bot/browser.js.map +1 -1
  11. package/dist/bot/capture/capture.d.ts +7 -15
  12. package/dist/bot/capture/capture.d.ts.map +1 -1
  13. package/dist/bot/capture/capture.js +71 -253
  14. package/dist/bot/capture/capture.js.map +1 -1
  15. package/dist/bot/card-release-approval.d.ts.map +1 -1
  16. package/dist/bot/card-release-approval.js +11 -2
  17. package/dist/bot/card-release-approval.js.map +1 -1
  18. package/dist/bot/credential-capture.d.ts +10 -0
  19. package/dist/bot/credential-capture.d.ts.map +1 -1
  20. package/dist/bot/credential-capture.js +5 -0
  21. package/dist/bot/credential-capture.js.map +1 -1
  22. package/dist/bot/credential-shape.d.ts +0 -5
  23. package/dist/bot/credential-shape.d.ts.map +1 -1
  24. package/dist/bot/credential-shape.js +8 -132
  25. package/dist/bot/credential-shape.js.map +1 -1
  26. package/dist/bot/drive-feedback.d.ts +2 -8
  27. package/dist/bot/drive-feedback.d.ts.map +1 -1
  28. package/dist/bot/drive-feedback.js +7 -49
  29. package/dist/bot/drive-feedback.js.map +1 -1
  30. package/dist/bot/jev-client.d.ts +3 -0
  31. package/dist/bot/jev-client.d.ts.map +1 -1
  32. package/dist/bot/jev-client.js +14 -5
  33. package/dist/bot/jev-client.js.map +1 -1
  34. package/dist/bot/operate-drive.d.ts +13 -63
  35. package/dist/bot/operate-drive.d.ts.map +1 -1
  36. package/dist/bot/operate-drive.js +136 -592
  37. package/dist/bot/operate-drive.js.map +1 -1
  38. package/dist/bot/page-driver.js +1 -1
  39. package/dist/bot/page-driver.js.map +1 -1
  40. package/dist/bot/provision-session.d.ts +3 -1
  41. package/dist/bot/provision-session.d.ts.map +1 -1
  42. package/dist/bot/provision-session.js +1 -1
  43. package/dist/bot/provision-session.js.map +1 -1
  44. package/dist/bot/session/lifecycle.d.ts.map +1 -1
  45. package/dist/bot/session/lifecycle.js +14 -13
  46. package/dist/bot/session/lifecycle.js.map +1 -1
  47. package/dist/bot/skill-hint.d.ts.map +1 -1
  48. package/dist/bot/skill-hint.js +4 -3
  49. package/dist/bot/skill-hint.js.map +1 -1
  50. package/dist/install/broker-service.d.ts.map +1 -1
  51. package/dist/install/broker-service.js +13 -15
  52. package/dist/install/broker-service.js.map +1 -1
  53. package/dist/install/cli.d.ts.map +1 -1
  54. package/dist/install/cli.js +43 -32
  55. package/dist/install/cli.js.map +1 -1
  56. package/dist/install/connect-report.d.ts +7 -7
  57. package/dist/install/connect-report.d.ts.map +1 -1
  58. package/dist/install/connect-report.js +31 -25
  59. package/dist/install/connect-report.js.map +1 -1
  60. package/dist/server.d.ts.map +1 -1
  61. package/dist/server.js +42 -3
  62. package/dist/server.js.map +1 -1
  63. package/dist/tools/fetch-credential.d.ts.map +1 -1
  64. package/dist/tools/fetch-credential.js +3 -1
  65. package/dist/tools/fetch-credential.js.map +1 -1
  66. package/dist/tools/provision-drive-schema.d.ts +0 -1
  67. package/dist/tools/provision-drive-schema.d.ts.map +1 -1
  68. package/dist/tools/provision-drive-schema.js +125 -238
  69. package/dist/tools/provision-drive-schema.js.map +1 -1
  70. package/dist/tools/provision-drive.d.ts +217 -73
  71. package/dist/tools/provision-drive.d.ts.map +1 -1
  72. package/dist/tools/provision-drive.js +133 -180
  73. package/dist/tools/provision-drive.js.map +1 -1
  74. package/dist/tools/store-credential.d.ts.map +1 -1
  75. package/dist/tools/store-credential.js +5 -1
  76. package/dist/tools/store-credential.js.map +1 -1
  77. package/dist/tools/use-credential.js +2 -2
  78. package/package.json +1 -1
  79. package/dist/bot/credential-text.d.ts +0 -4
  80. package/dist/bot/credential-text.d.ts.map +0 -1
  81. package/dist/bot/credential-text.js +0 -296
  82. package/dist/bot/credential-text.js.map +0 -1
  83. package/dist/bot/extraction.d.ts +0 -18
  84. package/dist/bot/extraction.d.ts.map +0 -1
  85. package/dist/bot/extraction.js +0 -54
  86. package/dist/bot/extraction.js.map +0 -1
@@ -9,20 +9,20 @@
9
9
  // facts bag. A search or query field may receive a phrase Jev assigns from the
10
10
  // goal's own words or the facts; nothing is composed.
11
11
  //
12
- // Confidence: no gates anywhere, including DONE. Validation of the answer
13
- // shape stays. The purchase approval is the payment gate. Safety net is
12
+ // Confidence below DRIVE_CONFIDENCE_THRESHOLD hands control back to the
13
+ // operator. Answer-shape validation and purchase approval stay. Safety net is
14
14
  // validate_choice, fingerprint-bound consume-once, and three consecutive
15
15
  // non-wait actions with no page change (re-snapshot once before a strike).
16
16
  import { appendFileSync } from "node:fs";
17
- import { JevUnavailableError, JevRequestError, askJev, } from "./jev-client.js";
18
- import { act, awaitVerification, generatePassword, observe, startProvisionSession, TargetStaleError, } from "./provision-session.js";
17
+ import { JevUnavailableError, JevRequestError, JevInvalidResponseError, askJev, } from "./jev-client.js";
18
+ import { act, awaitVerification, observe, startProvisionSession, TargetStaleError, } from "./provision-session.js";
19
19
  import { resolveInboxSearch } from "./capture/verification.js";
20
20
  import { liveProviderSessionsForSession, sessionForCall } from "./session/lifecycle.js";
21
21
  import { compactV2EpochDoc, compactV2RefAllocator, invalidateCompactV2Snapshot, observedThreeDsChallenge, rememberCompactV2SourcePage, } from "./observe/observe.js";
22
22
  import { COMPACT_V2_HANDLE_LENGTH, controlLabelV2, isCompactV2Handle, safePageSemanticsV2, safeStageV2, wireRoleToSafeRoleV2, } from "./compact-observation-v2.js";
23
23
  import { lastSelectOptions, } from "./session/model.js";
24
24
  import { captureFrameSnapshot, driveRowsFromSnapshot, frameDynamicsSignature, mergeSnapshots, snapshotSelectOptions, snapshotToObservation, } from "./drive-snapshot.js";
25
- import { DriveEvaluateTimeout, evaluateBound } from "./drive-evaluate.js";
25
+ import { evaluateBound } from "./drive-evaluate.js";
26
26
  import { DriveRefBridge } from "./drive-ref-bridge.js";
27
27
  import { dispatchDriveAct } from "./act/act.js";
28
28
  import { frameOriginOf } from "./browser-use-capture.js";
@@ -31,10 +31,9 @@ import { rememberDriveIdentities, resolveIdentityScope } from "./act/identity.js
31
31
  import { approvalItemWithNote, readPageCheckoutTexts, resolveDriveApprovalAmount, } from "./checkout-total.js";
32
32
  import { attemptOperateCaptchaAutoSolve, captchaAutoSolveHasWork, injectPendingCaptchaToken, } from "./captcha-solve.js";
33
33
  import { RES_POLL_INTERVAL_MS, RES_TIMEOUT_MS, hasVisibleCheckboxCaptchaWidget, solveVisibleCaptcha, } from "./captcha.js";
34
- import { findCredentialTokens, isMaskedDisplay, looksLikeCredentialValue, pickRelaxedNearCopyCredential, } from "./credential-shape.js";
35
- import { extractApiKeyFromText } from "./credential-text.js";
36
- import { extractCredentials } from "./capture/capture.js";
37
- import { DRIVE_FIXED_GO_BACK, DRIVE_FIXED_NONE_OF_THESE, DRIVE_TRAIL_CAP, DRIVE_TRAIL_STORE_CAP, classifyDriveOutcome, composeDrivePageText, feedbackPagePath, goalDoneWhen, goalPhase, goalPhaseAndDoneWhen, isKeyGoal, stripVolatileQuery, truncateDriveTrailText, } from "./drive-feedback.js";
34
+ import { findCredentialTokens, isMaskedDisplay } from "./credential-shape.js";
35
+ import { extractCredentials, storableCredentials } from "./capture/capture.js";
36
+ import { DRIVE_FIXED_GO_BACK, DRIVE_FIXED_NONE_OF_THESE, DRIVE_TRAIL_CAP, DRIVE_TRAIL_STORE_CAP, classifyDriveOutcome, composeDrivePageText, feedbackPagePath, goalDoneWhen, goalPhaseAndDoneWhen, stripVolatileQuery, truncateDriveTrailText, } from "./drive-feedback.js";
38
37
  export const DRIVE_CONFIDENCE_THRESHOLD = 0.6;
39
38
  export const DRIVE_DEFAULT_MAX_STEPS = 60;
40
39
  export const DRIVE_DEFAULT_MAX_SECONDS = 45;
@@ -132,8 +131,8 @@ export const DRIVE_RULES = [
132
131
  "If a needed control is covered, act on whatever covers it first.",
133
132
  "Prefer controls that match the current page phase implied by the URL and headings.",
134
133
  "A disabled submit means a required field is still empty until every fillable is populated. Disable is not a gate.",
135
- "If the goal names an API key or token, open the control that leads there before stopping on onboarding or a dashboard.",
136
- "The state names the current phase and the exact done_when condition; done_when is the only definition of complete.",
134
+ "Use the goal's full text to decide what result is required; no goal type is inferred for you.",
135
+ "The done_when condition includes every stated requirement in the goal.",
137
136
  "The trail records what each earlier action actually did. Do not repeat an action whose trail outcome is no_change, not_executed, or bounced_back.",
138
137
  "Pick NONE_OF_THESE instead of a low-confidence click when nothing on this page advances the goal.",
139
138
  "Pick GO_BACK when the trail shows this page was reached by mistake.",
@@ -405,17 +404,21 @@ export function redactSecretShapedValue(value, sessionId) {
405
404
  }
406
405
  return value;
407
406
  }
408
- function maskDriveOutput(session, value) {
409
- const cardMasked = typeof session.browser.maskOperatorOutput === "function"
407
+ /** Exact-value card mask only. This is all the model (Jev) input carries. */
408
+ function maskCardValues(session, value) {
409
+ return typeof session.browser.maskOperatorOutput === "function"
410
410
  ? session.browser.maskOperatorOutput(value)
411
411
  : value;
412
- return redactSecretShapedValue(cardMasked, session.id);
412
+ }
413
+ /** Trace files can outlive the session audience, so screen their contents. */
414
+ function maskPersistedDriveTrace(session, value) {
415
+ return redactSecretShapedValue(maskCardValues(session, value), session.id);
413
416
  }
414
417
  function appendDriveTrace(session, entry) {
415
418
  const path = process.env.DRIVE_TRACE_PATH;
416
419
  if (path === undefined || path.length === 0)
417
420
  return;
418
- appendFileSync(path, `${JSON.stringify(maskDriveOutput(session, entry))}\n`);
421
+ appendFileSync(path, `${JSON.stringify(maskPersistedDriveTrace(session, entry))}\n`);
419
422
  }
420
423
  async function nativeSelectSnapshot(session) {
421
424
  const page = session.browser.page;
@@ -679,12 +682,12 @@ export function cycleReason(url, repeated = []) {
679
682
  return `cycling through ${repeated.join(", ")}`;
680
683
  return `cycling through ${pagePathKey(url)}`;
681
684
  }
682
- export function isPendingPageAction(row, goal = "") {
685
+ export function isPendingPageAction(row, _goal = "") {
683
686
  if (isRevealOrCopyRow(row) || isCreateEntryRow(row))
684
687
  return true;
685
688
  if (isSubmitLikeRow(row) && !isProgressSubmitRow(row) && !isOauthChromeRow(row))
686
689
  return true;
687
- return (goal.length > 0 && rowCarriesGoalNoun(row, goal) && isClickableRow(row) && !isSectionNavRow(row));
690
+ return false;
688
691
  }
689
692
  export function pageHasUntriedPendingAction(rows, drive, pageUrl, goal) {
690
693
  const failed = new Set(drive.failedActionKeys ?? []);
@@ -773,39 +776,8 @@ export function inferPagePhase(url, headings = []) {
773
776
  return "login";
774
777
  return "unknown";
775
778
  }
776
- function goalWantsSignup(goal) {
777
- if (goal === undefined || goal.length === 0)
778
- return false;
779
- return /sign\s*up|register|create\s+(?:an?\s+)?account/.test(goal.toLowerCase());
780
- }
781
- function goalWantsKey(goal) {
782
- if (goal === undefined || goal.length === 0)
783
- return false;
784
- return /api\s*key|access\s*token|credential/.test(goal.toLowerCase());
785
- }
786
- function isCreateAccountRow(row) {
787
- const label = readableLabel(row).toLowerCase();
788
- return /create\s+(?:an?\s+)?account|sign\s*up|register/.test(label);
789
- }
790
- function isLoginRow(row) {
791
- const label = readableLabel(row).toLowerCase();
792
- return /log\s*in|sign\s*in/.test(label) && !isCreateAccountRow(row);
793
- }
794
- function isKeyNavRow(row) {
795
- if (isListFilterRow(row))
796
- return false;
797
- const label = readableLabel(row).toLowerCase();
798
- return /api\s*key|access\s*token|create\s+(?:a\s+)?(?:key|token)|credentials?|settings/.test(label);
799
- }
800
- function isNavChromeRow(row) {
801
- if (isSubmitLikeRow(row) || isFillableRow(row) || isChoiceRow(row) || isConsentRow(row)) {
802
- return false;
803
- }
804
- return row[1] === "l" || row[1] === "link" || row[1] === "tb" || row[1] === "tab";
805
- }
806
779
  export function candidateAimScore(candidate, input) {
807
780
  const row = candidate.row;
808
- const phase = input.phase ?? inferPagePhase(input.pageUrl ?? "", input.headings ?? []);
809
781
  const filledIds = filledFormIds(input.rows, input.filledRefs ?? []);
810
782
  const formId = rowFormId(row);
811
783
  const occluder = rowOccluder(row);
@@ -828,76 +800,14 @@ export function candidateAimScore(candidate, input) {
828
800
  if (failed.has(actionFailureKey(row, input.pageUrl ?? "")) || failed.has(row[0])) {
829
801
  score -= 70;
830
802
  }
831
- if (phase === "signup") {
832
- if (isSubmitLikeRow(row) || isFillableRow(row) || isChoiceRow(row))
833
- score += 40;
834
- if (isOauthChromeRow(row))
835
- score -= 40;
836
- if (isSearchRow(row) && rowListChoice(row) === undefined)
837
- score -= 30;
838
- if (isLoginRow(row))
839
- score -= 20;
840
- }
841
- else if (phase === "login") {
842
- if (goalWantsSignup(input.goal) && isCreateAccountRow(row))
843
- score += 80;
844
- else if (isLoginRow(row) || isSubmitLikeRow(row))
845
- score += 20;
846
- if (isOauthChromeRow(row) && goalWantsSignup(input.goal))
847
- score -= 30;
848
- }
849
- else if (phase === "verify") {
850
- if (/verify|confirm|email|inbox|gmail|mail/.test(readableLabel(row).toLowerCase()))
851
- score += 50;
852
- }
853
- else if (phase === "onboarding") {
854
- if (isChoiceRow(row) || rowListChoice(row) !== undefined)
855
- score += 40;
856
- if (isSearchRow(row) && rowListChoice(row) === undefined)
857
- score -= 40;
858
- }
859
- else if (phase === "keys" || (phase === "unknown" && goalWantsKey(input.goal))) {
860
- if (isKeyNavRow(row))
861
- score += 80;
862
- }
863
- else if (phase === "checkout") {
864
- if (isButtonLikeRow(row) || isPaymentRow(row) || isCvvRow(row))
865
- score += 40;
866
- }
867
- if (isNavChromeRow(row) && (phase === "signup" || phase === "onboarding" || phase === "login")) {
868
- score -= 10;
869
- }
870
- if (isAlreadyHereNav(row, input.pageUrl ?? ""))
871
- score -= 80;
872
- if (isAppRootOrLogoRow(row, input.pageUrl ?? "") ||
873
- isSamePageAnchorRow(row, input.pageUrl ?? "")) {
874
- score -= 80;
875
- }
876
- if (isCreateEntryRow(row) && listedItemRows(input.rows, input.pageUrl ?? "").length > 0) {
877
- score -= 50;
878
- }
879
- if (listedItemRows(input.rows, input.pageUrl ?? "").some((entry) => entry[0] === row[0])) {
880
- score += 70;
881
- }
882
- if (isRevealOrCopyRow(row) && goalWantsKey(input.goal))
883
- score += 80;
884
- if (isListFilterRow(row) && goalWantsKey(input.goal))
885
- score -= 50;
886
- if (goalWantsKey(input.goal) &&
887
- isEligibleSectionNavRow(row, input.pageUrl ?? "") &&
888
- !isAlreadyHereNav(row, input.pageUrl ?? "") &&
889
- !rowMatchesGoalSeek(row, input.goal ?? "")) {
890
- score += listedItemRows(input.rows, input.pageUrl ?? "").length > 0 ? -20 : 40;
891
- }
892
803
  return score;
893
804
  }
894
805
  export function rankDriveCandidates(candidates, input) {
895
- const phase = inferPagePhase(input.pageUrl ?? "", input.headings ?? []);
896
806
  return candidates
897
807
  .map((candidate, index) => ({
898
808
  candidate,
899
809
  index,
900
- score: candidateAimScore(candidate, { ...input, phase }),
810
+ score: candidateAimScore(candidate, input),
901
811
  }))
902
812
  .sort((left, right) => right.score - left.score || left.index - right.index)
903
813
  .map((entry) => entry.candidate);
@@ -927,16 +837,9 @@ const BUTTON_LIKE_ROLES = new Set(["b", "button"]);
927
837
  export function isButtonLikeRow(row) {
928
838
  return BUTTON_LIKE_ROLES.has(row[1]);
929
839
  }
930
- /** Whether an offscreen row is still worth offering.
931
- *
932
- * Shopify parks "Pay now" below the fold (LIVE #6, top≈1458 in a 720px
933
- * viewport) and the act path scrolls, so a checkout's buttons stay reachable.
934
- * The page, not the label, is the test: a label allowlist would drop a
935
- * localized "Payer maintenant". Off a checkout no offscreen button is offered,
936
- * so a product page's own "Buy now" stays unclickable.
937
- */
938
- export function offscreenRowStaysOffered(row, pageUrl) {
939
- return isButtonLikeRow(row) && isCheckoutUrl(pageUrl);
840
+ /** The act path scrolls an offscreen target into view before acting. */
841
+ export function offscreenRowStaysOffered(_row, _pageUrl) {
842
+ return true;
940
843
  }
941
844
  /** Whether the drive already asked to pay since the card went in.
942
845
  *
@@ -983,7 +886,7 @@ export function pageHasListedWork(rows, typeTextCount, selectCount) {
983
886
  * call (a card-fill goal never submits at all).
984
887
  */
985
888
  function isPaymentSubstituteRow(row) {
986
- return isClickableRow(row) && !isFillableRow(row) && !isOffscreenRow(row);
889
+ return isClickableRow(row) && !isFillableRow(row);
987
890
  }
988
891
  export function paymentSubmitControlMissing(input) {
989
892
  if (!input.includePayment ||
@@ -1016,9 +919,6 @@ function urlPathname(url) {
1016
919
  export function isCheckoutUrl(url) {
1017
920
  return /(?:^|\/)(?:checkouts?|payment)(?:\/|$)/.test(urlPathname(url));
1018
921
  }
1019
- export function goalSeeksKey(goal) {
1020
- return /api\s*key|access\s*token|credential/.test(goal.toLowerCase());
1021
- }
1022
922
  export function isListFilterRow(row) {
1023
923
  const label = readableLabel(row).toLowerCase();
1024
924
  const chooser = row[1] === "c" ||
@@ -1082,21 +982,6 @@ export function isOffProductNavRow(row, pageUrl) {
1082
982
  return /\/(?:docs?|documentation|reference|help|blog|guides?)(?:\/|$)/.test(href);
1083
983
  }
1084
984
  }
1085
- export function isGoalDestinationRow(row) {
1086
- if (isListFilterRow(row) ||
1087
- isContextPickerRow(row) ||
1088
- isFillableRow(row) ||
1089
- isConsentRow(row) ||
1090
- isCodeSampleRow(row)) {
1091
- return false;
1092
- }
1093
- return (row[1] === "l" ||
1094
- row[1] === "link" ||
1095
- row[1] === "b" ||
1096
- row[1] === "button" ||
1097
- row[1] === "tb" ||
1098
- row[1] === "tab");
1099
- }
1100
985
  const SECTION_NAV_SKIP = /home|docs|documentation|logout|sign\s*out|help|pricing|blog|status|privacy|terms/;
1101
986
  const SECTION_NAV_NOTICE = /more information|reactivat|provide more|complete your|verify your identity/;
1102
987
  const PAGE_LEVEL_ANSWER = /more information|reactivat|provide more|account (?:is |has been )?(?:suspend|restrict|locked|disabled|on hold)|needs (?:more )?(?:information|attention)|complete (?:your )?(?:profile|verification|account)/i;
@@ -1276,33 +1161,17 @@ export function rowShowsSecretEvidence(row) {
1276
1161
  export function pageShowsRevealedKey(rows, _pageText = "") {
1277
1162
  return rows.some((row) => rowShowsSecretEvidence(row));
1278
1163
  }
1279
- export function driveKeyGoalComplete(evidence) {
1280
- // Existing keys may stay masked on a list after a newly created key is
1281
- // shown once. A full captured secret satisfies this goal independently of
1282
- // those unread older entries.
1283
- return Object.entries(evidence.credentials).some(([field, value]) => looksLikeCredentialValue(value) ||
1284
- extractApiKeyFromText(value) === value ||
1285
- (field === "api_key" && pickRelaxedNearCopyCredential([value]) === value));
1286
- }
1287
- export async function driveKeyEvidence(sessionId) {
1288
- try {
1289
- const extracted = await extractCredentials(sessionId);
1290
- return {
1291
- credentials: extracted.credentials,
1292
- maskedRemaining: extracted.masked_remaining ?? [],
1293
- };
1294
- }
1295
- catch {
1296
- // The completion check must never crash the loop; an unavailable page is
1297
- // simply "no credential yet", and the loop carries on.
1298
- return { credentials: {}, maskedRemaining: [] };
1299
- }
1300
- }
1301
- /** "still masked: a, b" for a stuck reason, or undefined when nothing was. */
1302
- export function maskedRemainingReason(evidence) {
1303
- return evidence.maskedRemaining.length > 0
1304
- ? `still masked: ${evidence.maskedRemaining.join(", ")}`
1305
- : undefined;
1164
+ function pageShowsCredentialValue(rows) {
1165
+ if (pageShowsRevealedKey(rows))
1166
+ return true;
1167
+ // A labeled value can be a key even when it has no recognizable prefix or
1168
+ // length. Shape is never the criterion for capturing or storing it.
1169
+ return rows.some((row) => {
1170
+ if (!/^(?:api[- ]?key|access token|token|secret)$/i.test(readableLabel(row)))
1171
+ return false;
1172
+ const value = /(?:^|\|)n=([^|]+)/.exec(row[2] ?? "")?.[1];
1173
+ return value !== undefined && value.length > 0 && !isMaskedDisplay(value);
1174
+ });
1306
1175
  }
1307
1176
  export function isKeyCreateRow(row) {
1308
1177
  if (isFillableRow(row) || isConsentRow(row) || isOauthChromeRow(row))
@@ -1314,12 +1183,20 @@ export function isKeyCreateRow(row) {
1314
1183
  const thing = /(?:^|[\s\-_])(?:api[\s\-_]*)?(?:key|token|secret|credential)s?(?:[\s\-_]|$)/.test(label);
1315
1184
  return (create && thing) || (isCreateEntryRow(row) && thing);
1316
1185
  }
1317
- /** An existing credential may be read, but the drive never mutates it. */
1318
- function isExistingCredentialMutationRow(row) {
1319
- return /\b(?:regenerat\w*|rotat\w*|reset|revoke|delet\w*|remove)\b/i.test(readableLabel(row));
1186
+ /** Hand an existing credential mutation to the operator before dispatch. */
1187
+ export function isExistingCredentialMutationRow(row, pageUrl, rows = [], selectedOption = "", headings = []) {
1188
+ const label = `${readableLabel(row)} ${selectedOption}`;
1189
+ if (!/\b(?:regenerat\w*|rotat\w*|reset|revoke|delet\w*|remove)\b/i.test(label)) {
1190
+ return false;
1191
+ }
1192
+ const credentialNoun = /\b(?:(?:api[- ]?)?keys?|tokens?|secrets?|credentials?)\b/i;
1193
+ return credentialNoun.test(label) ||
1194
+ /(?:^|\/)(?:api[-_]?keys?|keys?|tokens?|credentials?)(?:\/|$)/.test(urlPathname(pageUrl)) ||
1195
+ headings.some((heading) => credentialNoun.test(heading)) ||
1196
+ rows.some((entry) => /^(?:h[1-6]|heading)$/.test(entry[1]) && credentialNoun.test(readableLabel(entry)));
1320
1197
  }
1321
- function isKeyCreateOpener(row, goal) {
1322
- return isKeyGoal(goal) && isKeyCreateRow(row) && rowFormId(row) === undefined;
1198
+ function isKeyCreateOpener(row) {
1199
+ return isKeyCreateRow(row) && rowFormId(row) === undefined;
1323
1200
  }
1324
1201
  /** Map the act layer's own refusal reason to the trail's short vocabulary:
1325
1202
  * covered, stale, refused, dialog. */
@@ -1424,41 +1301,21 @@ export function attachRevealedSecretMarker(observation, rows) {
1424
1301
  return { observation, rows: [...rows], attached: false };
1425
1302
  }
1426
1303
  const length = lengths.length > 0 ? Math.max(...lengths) : 16;
1427
- const redact = (text) => redactSecretShapedTokens(text).text;
1428
- const nextRows = rows.map((row) => {
1429
- const role = redact(row[1]);
1430
- const facts = row[2] === undefined ? undefined : redact(row[2]);
1431
- if (role === row[1] && facts === row[2])
1432
- return row;
1433
- return (facts === undefined ? [row[0], role] : [row[0], role, facts]);
1434
- });
1304
+ // The marker tells the model a key is on the page; the page content itself
1305
+ // reaches it unaltered.
1306
+ const nextRows = [...rows];
1435
1307
  if (!nextRows.some((row) => row[0] === REVEALED_SECRET_REF)) {
1436
1308
  nextRows.push(revealedSecretMarkerRow(length, tokens.size));
1437
1309
  }
1438
1310
  const headings = [
1439
- ...(observation.semantic?.headings ?? []).map(redact),
1311
+ ...(observation.semantic?.headings ?? []),
1440
1312
  revealedSecretMarkerRow(length, tokens.size)[2],
1441
1313
  ];
1442
1314
  return {
1443
1315
  observation: {
1444
1316
  ...observation,
1445
- ...(observation.dom === undefined ? {} : { dom: redact(observation.dom) }),
1446
1317
  safe_table: nextRows,
1447
- semantic: {
1448
- ...observation.semantic,
1449
- ...(observation.semantic?.title === undefined
1450
- ? {}
1451
- : { title: redact(observation.semantic.title) }),
1452
- headings,
1453
- ...(observation.semantic?.blockers === undefined
1454
- ? {}
1455
- : {
1456
- blockers: observation.semantic.blockers.map((blocker) => ({
1457
- ...blocker,
1458
- text: redact(blocker.text),
1459
- })),
1460
- }),
1461
- },
1318
+ semantic: { ...observation.semantic, headings },
1462
1319
  },
1463
1320
  rows: nextRows,
1464
1321
  attached: true,
@@ -1479,58 +1336,9 @@ export function isEntityNameRow(row) {
1479
1336
  export function isLogoutRow(row) {
1480
1337
  return /log\s*out|sign\s*out|signout|logout/.test(readableLabel(row).toLowerCase());
1481
1338
  }
1482
- export function alreadySignedInReason() {
1483
- return "already signed in as another account";
1484
- }
1485
- const EMAIL_IN_TEXT = /[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}/gi;
1486
- export function emailsInText(text) {
1487
- const seen = new Set();
1488
- const out = [];
1489
- for (const match of text.matchAll(EMAIL_IN_TEXT)) {
1490
- const email = match[0].toLowerCase();
1491
- if (seen.has(email))
1492
- continue;
1493
- seen.add(email);
1494
- out.push(email);
1495
- }
1496
- return out;
1497
- }
1498
- /** Goal asks to use an already-signed-in third-party identity. */
1499
- export function goalSeeksThirdPartySignin(goal) {
1500
- if (goalExcludesOauth(goal))
1501
- return false;
1502
- return /continue with (?:google|github)|sign(?:\s*up|\s*in) with (?:google|github)|third[- ]party sign-?in|account already signed in/.test(goal.toLowerCase());
1503
- }
1504
1339
  export function shouldInspectSubmitResponse(action, row) {
1505
1340
  return action.kind === "click" && row !== undefined && isSubmitLikeRow(row);
1506
1341
  }
1507
- /**
1508
- * A check-your-email / verify page — visible copy and OTP fields only.
1509
- * The word "verif" in a URL or link label is not this: product sections
1510
- * about identity verifications are ordinary signed-in pages.
1511
- */
1512
- export function pageLooksLikeEmailVerification(rows, _pageUrl = "", pageText = "") {
1513
- if (rows.some((row) => isOtpRow(row) && isFillableRow(row)))
1514
- return true;
1515
- return emailVerificationCopy(`${pageText} ${headingCopy(rows)}`);
1516
- }
1517
- /** Page copy names an email the goal did not. Unknown or matching identity is not this. */
1518
- export function pageShowsForeignIdentity(rows, pageText, goal) {
1519
- const wanted = emailsInText(goal);
1520
- if (wanted.length === 0)
1521
- return false;
1522
- const wantedSet = new Set(wanted);
1523
- const pageEmails = emailsInText(`${pageText} ${rows.map((row) => readableLabel(row)).join(" ")}`);
1524
- return pageEmails.some((email) => !wantedSet.has(email));
1525
- }
1526
- export function isPreexistingSessionPage(input) {
1527
- if (goalSeeksThirdPartySignin(input.goal))
1528
- return false;
1529
- if (pageShowsForeignIdentity(input.rows, input.pageText, input.goal))
1530
- return true;
1531
- return (input.submittedThisDrive !== true &&
1532
- pageLooksLikeEmailVerification(input.rows, input.pageUrl, input.pageText));
1533
- }
1534
1342
  export function invalidFieldReason(rows) {
1535
1343
  const invalid = rows.find((row) => isInvalidRow(row));
1536
1344
  if (invalid === undefined)
@@ -1642,55 +1450,7 @@ function shouldRecordVisit(row, pageUrl) {
1642
1450
  const kind = row[1];
1643
1451
  return kind === "l" || kind === "link" || kind === "tb" || kind === "tab";
1644
1452
  }
1645
- function decisionIsActionable(decision) {
1646
- return decision.kind === "act";
1647
- }
1648
- export function rowCarriesGoalNoun(row, goal) {
1649
- if (!goalSeeksKey(goal) || isConsentRow(row) || isContextPickerRow(row))
1650
- return false;
1651
- const label = readableLabel(row).toLowerCase();
1652
- return /api\s*key|access\s*token|create\s+(?:a\s+)?(?:key|token)|credentials?|\b(?:api[-_]?keys?|tokens?)\b/.test(label);
1653
- }
1654
- export function rowMatchesGoalSeek(row, goal) {
1655
- if (rowCarriesGoalNoun(row, goal))
1656
- return true;
1657
- if (!goalSeeksKey(goal) || isConsentRow(row) || isContextPickerRow(row))
1658
- return false;
1659
- return /\bsettings\b/.test(readableLabel(row).toLowerCase());
1660
- }
1661
- export function pageIsPostAuthSetup(url) {
1662
- return /(?:^|\/)(?:welcome|onboarding|getting[-_]?started|confirm|dashboard|console|home|teams|app)(?:\/|$)/.test(urlPathname(url));
1663
- }
1664
- export function pageLooksLikeKeyDestination(url) {
1665
- return /(?:^|\/)(?:api[-_]?keys?|tokens?|credentials?|settings)(?:\/|$)/.test(urlPathname(url));
1666
- }
1667
- export function clickGoalSeekScore(row, goal, pageUrl) {
1668
- if (!goalSeeksKey(goal))
1669
- return 0;
1670
- const onSetup = pageIsPostAuthSetup(pageUrl);
1671
- const onKeys = pageLooksLikeKeyDestination(pageUrl);
1672
- if (!onSetup && !onKeys)
1673
- return 0;
1674
- if (isContextPickerRow(row) ||
1675
- isAlreadyHereNav(row, pageUrl) ||
1676
- isOffProductNavRow(row, pageUrl)) {
1677
- return 0;
1678
- }
1679
- if (isRevealOrCopyRow(row))
1680
- return 3;
1681
- if (isDeeperDestination(rowHref(row), pageUrl) || isListedEntityRow(row))
1682
- return 2;
1683
- if (rowMatchesGoalSeek(row, goal) && isGoalDestinationRow(row))
1684
- return 2;
1685
- if (rowMatchesGoalSeek(row, goal))
1686
- return 1;
1687
- if (onSetup && isSubmitLikeRow(row) && !isDisabledRow(row))
1688
- return 1;
1689
- return 0;
1690
- }
1691
- export function isCandidateRow(row, includePayment, pageUrl = "") {
1692
- if (isOffscreenRow(row) && !offscreenRowStaysOffered(row, pageUrl))
1693
- return false;
1453
+ export function isCandidateRow(row, includePayment, _pageUrl = "") {
1694
1454
  if (isDisabledRow(row) && !isSubmitLikeRow(row))
1695
1455
  return false;
1696
1456
  if (!includePayment && (isPaymentRow(row) || isCvvRow(row)))
@@ -1851,34 +1611,13 @@ export function isOauthChromeRow(row) {
1851
1611
  return /\b(?:microsoft|hasura|sso|oauth)\b/.test(label) ||
1852
1612
  /\/broker\/[^/]+\/login\b/i.test(row[2] ?? "");
1853
1613
  }
1854
- /** Goal text that rules out third-party sign-in as the path to take. */
1855
- export function goalExcludesOauth(goal) {
1856
- const text = goal.toLowerCase();
1857
- if (/\b(?:without|not|don't|dont|exclude|avoid|no)\b[\s\S]{0,48}\b(?:google|github|sso|oauth|third[- ]party)\b/.test(text)) {
1858
- return true;
1859
- }
1860
- if (/\b(?:google|github|sso|oauth|third[- ]party)\b[\s\S]{0,48}\b(?:excluded|forbidden|disabled)\b/.test(text)) {
1861
- return true;
1862
- }
1863
- return (/\b(?:email|password)\b/.test(text) &&
1864
- /\b(?:only|instead|without|not|exclude|avoid)\b/.test(text));
1865
- }
1866
- /** Sign-up chrome is only third-party links — no email, password, or native submit. */
1867
- export function pageOffersOnlyThirdPartySignup(rows) {
1868
- if (!rows.some((row) => isOauthChromeRow(row)))
1869
- return false;
1870
- return formSurfaceRows(rows).length === 0;
1871
- }
1872
- export function noOtherSignupPathReason() {
1873
- return "The page offers no other sign-up path besides third-party sign-in links.";
1874
- }
1875
1614
  export function isChoiceRow(row) {
1876
1615
  const role = row[1];
1877
1616
  return role === "c" || role === "checkbox" || role === "combobox" || isPickerRow(row);
1878
1617
  }
1879
1618
  export function formSurfaceRows(rows) {
1880
1619
  return rows.filter((row) => {
1881
- if (isOauthChromeRow(row) || isOffscreenRow(row))
1620
+ if (isOauthChromeRow(row))
1882
1621
  return false;
1883
1622
  return isFillableRow(row) || isSubmitLikeRow(row) || isChoiceRow(row);
1884
1623
  });
@@ -2246,26 +1985,15 @@ export function applyReleasedCardFacts(facts, card) {
2246
1985
  }
2247
1986
  return next;
2248
1987
  }
2249
- export function ensureGeneratedFacts(rows, facts, context = {}) {
1988
+ export function ensureGeneratedFacts(_rows, facts, _context = {}) {
2250
1989
  const next = { ...facts };
2251
1990
  const first = next.first_name?.trim() ?? "";
2252
1991
  const last = next.last_name?.trim() ?? "";
2253
1992
  if (next.name === undefined && first.length > 0 && last.length > 0) {
2254
1993
  next.name = `${first} ${last}`;
2255
1994
  }
2256
- const phase = inferPagePhase(context.pageUrl ?? "", context.headings);
2257
- const hasLoginAction = rows.some((row) => isButtonLikeRow(row) && isLoginRow(row));
2258
- const hasLoginHeading = context.headings?.some((heading) => /\b(?:log|sign)\s*in\b/i.test(heading)) === true;
2259
- const hasSignupAction = rows.some((row) => isButtonLikeRow(row) && isCreateAccountRow(row));
2260
- const hasConfirmPassword = rows.some((row) => isFillableRow(row) && isPasswordRow(row) &&
2261
- /(?:^|[_ ])(?:confirm|confirmation|repeat|reenter|verify)(?:[_ ]|$)/.test(`${normalizeKey(fieldNameForRow(row))} ${normalizeKey(readableLabel(row))}`));
2262
- if (phase !== "login" && (phase === "signup" || (!hasLoginAction && !hasLoginHeading)) &&
2263
- (phase === "signup" || hasSignupAction || hasConfirmPassword || goalWantsSignup(context.goal))) {
2264
- if (next.password === undefined && rows.some((row) => isFillableRow(row) && !isActedRow(row) && !isPaymentRow(row) && !isCvvRow(row) &&
2265
- isPasswordRow(row) && matchingFactKeys(next, row).length === 0)) {
2266
- next.password = generatePassword();
2267
- }
2268
- }
1995
+ // Password creation belongs to operate_login's sealed signup path. A required
1996
+ // password without a fact reaches the drive's needs_value handoff.
2269
1997
  return next;
2270
1998
  }
2271
1999
  export function slugifyCriteriaKey(seed) {
@@ -2538,19 +2266,16 @@ export function clickableCandidates(rows, includePayment, skippedRefs = [], page
2538
2266
  description: actionDescription(candidate.row, rows, "CLICK"),
2539
2267
  }));
2540
2268
  }
2541
- export function fillableCandidates(rows, facts, includePayment, filledRefs = [], pageUrl = "") {
2269
+ export function fillableCandidates(rows, facts, includePayment, filledRefs = [], _pageUrl = "") {
2542
2270
  const filled = new Set(filledRefs);
2543
2271
  const used = new Set();
2544
2272
  const candidates = [];
2545
- const allowOffscreen = pageUrl.length === 0 || isCheckoutUrl(pageUrl);
2546
2273
  for (const row of rows) {
2547
2274
  if (!isFillableRow(row) || isDisabledRow(row) || isActedRow(row))
2548
2275
  continue;
2549
2276
  // An invalid field is not done even if we already typed it.
2550
2277
  if (filled.has(row[0]) && !isInvalidRow(row))
2551
2278
  continue;
2552
- if (isOffscreenRow(row) && !allowOffscreen)
2553
- continue;
2554
2279
  if (!includePayment && (isPaymentRow(row) || isCvvRow(row)))
2555
2280
  continue;
2556
2281
  if (isPaymentRow(row) || isCvvRow(row))
@@ -2584,14 +2309,11 @@ export function typeableCandidates(rows, facts, includePayment, filledRefs = [],
2584
2309
  const used = new Set(typed.map((candidate) => candidate.slug));
2585
2310
  const extra = [];
2586
2311
  const filled = new Set(filledRefs);
2587
- const allowOffscreen = pageUrl.length === 0 || isCheckoutUrl(pageUrl);
2588
2312
  for (const row of rows) {
2589
2313
  if (!isFillableRow(row) || isSelectRow(row) || isDisabledRow(row) || isActedRow(row))
2590
2314
  continue;
2591
2315
  if (filled.has(row[0]) || seen.has(row[0]))
2592
2316
  continue;
2593
- if (isOffscreenRow(row) && !allowOffscreen)
2594
- continue;
2595
2317
  if (isPaymentRow(row) || isCvvRow(row))
2596
2318
  continue;
2597
2319
  // A checkout site-search box is not a fill. Offering it after the card
@@ -2616,11 +2338,10 @@ export function typeableCandidates(rows, facts, includePayment, filledRefs = [],
2616
2338
  }
2617
2339
  return [...typed, ...extra];
2618
2340
  }
2619
- export function selectCandidates(rows, facts, includePayment, filledRefs = [], pageUrl = "") {
2341
+ export function selectCandidates(rows, facts, includePayment, filledRefs = [], _pageUrl = "") {
2620
2342
  const filled = new Set(filledRefs);
2621
2343
  const used = new Set();
2622
2344
  const candidates = [];
2623
- const allowOffscreen = pageUrl.length === 0 || isCheckoutUrl(pageUrl);
2624
2345
  for (const row of rows) {
2625
2346
  if (!isFillableRow(row) || !isSelectRow(row) || isDisabledRow(row) || isActedRow(row))
2626
2347
  continue;
@@ -2628,8 +2349,6 @@ export function selectCandidates(rows, facts, includePayment, filledRefs = [], p
2628
2349
  continue;
2629
2350
  if (rowAlreadyShowsFact(row, facts, matchingFactKeys(facts, row)))
2630
2351
  continue;
2631
- if (isOffscreenRow(row) && !allowOffscreen)
2632
- continue;
2633
2352
  if (isPaymentRow(row) || isCvvRow(row))
2634
2353
  continue;
2635
2354
  const role = ROLE_LETTERS[row[1]] ?? row[1];
@@ -2659,7 +2378,7 @@ export function selectTargets(candidates, facts, pageOptions = new Map(), maskTe
2659
2378
  if (texts.has(text))
2660
2379
  return;
2661
2380
  texts.add(text);
2662
- const label = redactSecretShapedTokens(maskText(text)).text;
2381
+ const label = maskText(text);
2663
2382
  const optionSlug = uniqueCriteriaSlug(label, used);
2664
2383
  used.add(optionSlug);
2665
2384
  targets.push({
@@ -2818,7 +2537,7 @@ export function requiredFactTypeAction(rows, facts, filledRefs = [], pageUrl = "
2818
2537
  }
2819
2538
  return undefined;
2820
2539
  }
2821
- export function requiredFactComboboxAction(rows, facts, filledRefs = [], pageUrl = "", _goal) {
2540
+ export function requiredFactComboboxAction(rows, facts, filledRefs = [], _pageUrl = "", _goal) {
2822
2541
  const filled = new Set(filledRefs);
2823
2542
  for (const row of rows) {
2824
2543
  if (row[1] !== "combobox" || isDisabledRow(row) || isActedRow(row) || filled.has(row[0])) {
@@ -2844,14 +2563,11 @@ export function requiredFactComboboxAction(rows, facts, filledRefs = [], pageUrl
2844
2563
  }
2845
2564
  return undefined;
2846
2565
  }
2847
- export function requiredFillableMissingFact(rows, facts, filledRefs = [], pageUrl = "", goal = "") {
2566
+ export function requiredFillableMissingFact(rows, facts, filledRefs = [], _pageUrl = "", goal = "") {
2848
2567
  const filled = new Set(filledRefs);
2849
- const allowOffscreen = pageUrl.length === 0 || isCheckoutUrl(pageUrl);
2850
2568
  for (const row of rows) {
2851
2569
  if (!isFillableRow(row) || isDisabledRow(row) || isActedRow(row) || filled.has(row[0]))
2852
2570
  continue;
2853
- if (isOffscreenRow(row) && !allowOffscreen)
2854
- continue;
2855
2571
  if (isPaymentRow(row) || isCvvRow(row) || isOtpRow(row) || allowsGoalValueAssignment(row))
2856
2572
  continue;
2857
2573
  if (isFormValueField(row, rows, goal) &&
@@ -2886,10 +2602,8 @@ export function compactRowsText(url, stage, rows) {
2886
2602
  return `${header}\n${JSON.stringify(rows)}`;
2887
2603
  }
2888
2604
  export function nextActionInstructions(goal) {
2889
- const phase = goalPhase(goal);
2890
2605
  const doneWhen = goalDoneWhen(goal);
2891
2606
  return (`You are driving a browser to: ${goal}. ` +
2892
- (phase === undefined ? "" : `Current phase: ${phase}. `) +
2893
2607
  `The goal is complete only when ${doneWhen}. ` +
2894
2608
  "Pick the single next operation that advances it. " +
2895
2609
  "Pick DONE only when that done_when condition is on the page now. " +
@@ -3032,39 +2746,25 @@ export function elementState(candidate, extras = {}) {
3032
2746
  export function buildJevState(goal, factKeys, history, url, title, candidates, pageText = "", feedback = {}) {
3033
2747
  const recent = history.slice(-DRIVE_HISTORY_CAP);
3034
2748
  const pageUrl = feedback.pageUrl ?? url;
3035
- const layerOpen = pageOcclusionLayer(candidates.map((candidate) => candidate.row)) !== undefined;
3036
2749
  const tried = new Set(feedback.triedHere ?? []);
3037
2750
  const seenElementIds = new Set();
3038
2751
  const elements = candidates
3039
- .slice(0, DRIVE_MAX_CANDIDATES)
3040
- // Rows outside the main region only matter when a dialog or overlay is
3041
- // open. Hidden content is a distractor, not a lead.
3042
- .filter((candidate) => {
3043
- const region = driveElementLayer(candidate.row, pageUrl);
3044
- return layerOpen || (region !== "dialog" && region !== "overlay");
3045
- })
3046
2752
  .map((candidate) => {
3047
2753
  const leads = driveElementLeads(candidate.row, pageUrl);
3048
- seenElementIds.add(candidate.slug);
2754
+ seenElementIds.add(candidate.ref);
3049
2755
  return elementState(candidate, {
3050
2756
  tried: tried.has(stableControlKey(candidate.row, pageUrl)),
3051
2757
  ...(leads === undefined ? {} : { leads }),
3052
2758
  layer: driveElementLayer(candidate.row, pageUrl),
3053
2759
  });
3054
2760
  });
3055
- // A control the loop already acted on is annotated `tried`, never silently
3056
- // pruned: the decider should see why the option is weak. The question
3057
- // criteria still withhold it, so it cannot be chosen again.
2761
+ // Include every observed row. A row absent from the action choices still
2762
+ // gives the decider page context, including disabled and previously tried
2763
+ // controls, documentation links, and fields below the fold.
3058
2764
  for (const row of feedback.rows ?? []) {
3059
- if (!tried.has(stableControlKey(row, pageUrl)))
3060
- continue;
3061
2765
  const region = driveElementLayer(row, pageUrl);
3062
- if (!layerOpen && (region === "dialog" || region === "overlay"))
3063
- continue;
3064
2766
  if (seenElementIds.has(row[0]))
3065
2767
  continue;
3066
- if (elements.length >= DRIVE_MAX_CANDIDATES)
3067
- break;
3068
2768
  const leads = driveElementLeads(row, pageUrl);
3069
2769
  seenElementIds.add(row[0]);
3070
2770
  elements.push(elementState({
@@ -3074,7 +2774,7 @@ export function buildJevState(goal, factKeys, history, url, title, candidates, p
3074
2774
  description: readableLabel(row),
3075
2775
  row,
3076
2776
  }, {
3077
- tried: true,
2777
+ tried: tried.has(stableControlKey(row, pageUrl)),
3078
2778
  ...(leads === undefined ? {} : { leads }),
3079
2779
  layer: region,
3080
2780
  }));
@@ -3104,7 +2804,7 @@ export function operationCriteria(operations) {
3104
2804
  const criteria = {};
3105
2805
  for (const operation of operations) {
3106
2806
  if (operation === "CLICK")
3107
- criteria.CLICK = "click a visible control";
2807
+ criteria.CLICK = "click a control on the page";
3108
2808
  else if (operation === "TYPE_TEXT")
3109
2809
  criteria.TYPE_TEXT = "type a provided fact into a field";
3110
2810
  else if (operation === "SELECT")
@@ -3155,43 +2855,17 @@ export function selectOptionsFromElements(elements) {
3155
2855
  return out;
3156
2856
  }
3157
2857
  export function driveTargetSets(rows, facts, includePayment, filledRefs = [], pageUrl = "", pageOptions = new Map(), maskText = (text) => text, skippedClickRefs = [], aim = {}) {
3158
- const remaining = { n: DRIVE_MAX_CANDIDATES };
2858
+ // Bound each action family independently so a long form cannot consume the
2859
+ // entire candidate budget and make another action kind disappear.
2860
+ const candidateBudget = () => ({ n: DRIVE_MAX_CANDIDATES });
3159
2861
  const skipped = new Set(skippedClickRefs);
3160
- const offeredProviders = new Set(rows.map(oauthProviderForRow).filter((provider) => provider !== undefined));
3161
- const liveProviders = new Set([...offeredProviders].filter((provider) => aim.liveProviders?.includes(provider)));
3162
- const hideFilters = goalSeeksKey(aim.goal ?? "");
3163
- const hasInAppWork = rows.some((row) => isGoalDestinationRow(row) &&
3164
- !isOffProductNavRow(row, pageUrl) &&
3165
- !isAppRootOrLogoRow(row, pageUrl) &&
3166
- !isContextPickerRow(row));
3167
- const visited = aim.visitedSectionKeys ?? [];
3168
- const untriedEntries = hideFilters ? untriedListedItemRows(rows, visited, pageUrl) : [];
3169
2862
  const layer = pageOcclusionLayer(rows);
3170
2863
  const failed = new Set(aim.failedKeys ?? []);
3171
2864
  const keepRow = (row) => {
3172
- const provider = oauthProviderForRow(row);
3173
- if (offeredProviders.size > 1 && liveProviders.size > 0 && isOauthChromeRow(row) &&
3174
- (provider === undefined || !liveProviders.has(provider)))
3175
- return false;
3176
- if (isCodeSampleRow(row))
3177
- return false;
3178
- if (isKeyGoal(aim.goal ?? "") && isExistingCredentialMutationRow(row))
3179
- return false;
3180
2865
  if (failed.has(actionFailureKey(row, pageUrl)) || failed.has(row[0]))
3181
2866
  return false;
3182
2867
  if (layer !== undefined && !isLayerCandidateRow(row, rows, layer))
3183
2868
  return false;
3184
- if (hideFilters && isContextPickerRow(row))
3185
- return false;
3186
- if (hasInAppWork && isOffProductNavRow(row, pageUrl))
3187
- return false;
3188
- if (isAppRootOrLogoRow(row, pageUrl) || isSamePageAnchorRow(row, pageUrl))
3189
- return false;
3190
- if (visited.includes(sectionIdentity(row, pageUrl)))
3191
- return false;
3192
- if (hideFilters && untriedEntries.length > 0 && isSiblingSectionNavRow(row, pageUrl, rows)) {
3193
- return false;
3194
- }
3195
2869
  return true;
3196
2870
  };
3197
2871
  const aimInput = {
@@ -3202,17 +2876,10 @@ export function driveTargetSets(rows, facts, includePayment, filledRefs = [], pa
3202
2876
  failedKeys: aim.failedKeys ?? [],
3203
2877
  ...(aim.goal === undefined ? {} : { goal: aim.goal }),
3204
2878
  };
3205
- const typeText = takeCapped(rankDriveCandidates(typeableCandidates(rows, facts, includePayment, filledRefs, pageUrl, aim.goal).filter((candidate) => !skipped.has(candidate.ref) && keepRow(candidate.row)), aimInput), remaining);
3206
- const select = takeCapped(rankDriveCandidates(selectTargets(selectCandidates(rows, facts, includePayment, filledRefs, pageUrl).filter((candidate) => !skipped.has(candidate.ref) && keepRow(candidate.row)), facts, pageOptions, maskText), aimInput), remaining);
3207
- const click = takeCapped(rankDriveCandidates(clickableCandidates(rows, includePayment, skippedClickRefs, pageUrl, filledRefs).filter((candidate) => keepRow(candidate.row)), aimInput)
3208
- .map((candidate, index) => ({
3209
- candidate,
3210
- index,
3211
- score: clickGoalSeekScore(candidate.row, aim.goal ?? "", pageUrl),
3212
- }))
3213
- .sort((left, right) => right.score - left.score || left.index - right.index)
3214
- .map((entry) => entry.candidate), remaining);
3215
- const scroll = takeCapped(rankDriveCandidates(scrollTargets(rows), aimInput), remaining);
2879
+ const typeText = takeCapped(rankDriveCandidates(typeableCandidates(rows, facts, includePayment, filledRefs, pageUrl, aim.goal).filter((candidate) => !skipped.has(candidate.ref) && keepRow(candidate.row)), aimInput), candidateBudget());
2880
+ const select = takeCapped(rankDriveCandidates(selectTargets(selectCandidates(rows, facts, includePayment, filledRefs, pageUrl).filter((candidate) => !skipped.has(candidate.ref) && keepRow(candidate.row)), facts, pageOptions, maskText), aimInput), candidateBudget());
2881
+ const click = takeCapped(rankDriveCandidates(clickableCandidates(rows, includePayment, skippedClickRefs, pageUrl, filledRefs).filter((candidate) => keepRow(candidate.row)), aimInput), candidateBudget());
2882
+ const scroll = takeCapped(rankDriveCandidates(scrollTargets(rows), aimInput), candidateBudget());
3216
2883
  const operations = [];
3217
2884
  if (click.length > 0)
3218
2885
  operations.push("CLICK");
@@ -3222,31 +2889,12 @@ export function driveTargetSets(rows, facts, includePayment, filledRefs = [], pa
3222
2889
  operations.push("SELECT");
3223
2890
  if (scroll.length > 0)
3224
2891
  operations.push("SCROLL");
3225
- // Listed work only counts while some of it is actually offered: when every
3226
- // candidate is suppressed, withholding WAIT and BLOCKED too would leave DONE
3227
- // as the only admissible answer and force a false "complete".
3228
- const goalWork = (pageIsPostAuthSetup(pageUrl) || pageLooksLikeKeyDestination(pageUrl)) &&
3229
- rows.some((row) => !isDisabledRow(row) &&
3230
- !isOffscreenRow(row) &&
3231
- rowMatchesGoalSeek(row, aim.goal ?? "") &&
3232
- click.some((candidate) => candidate.ref === row[0]));
3233
- const listedWork = operations.length > 0 && (pageHasListedWork(rows, typeText.length, select.length) || goalWork);
3234
- const pageText = (aim.headings ?? []).join(" ");
3235
- const inboxReady = !skipped.has("INBOX") &&
3236
- aim.submittedThisDrive === true &&
3237
- (pageSuggestsInboxWait(rows, pageUrl, pageText) ||
3238
- (goalSeeksVerification(aim.goal ?? "") && !listedWork));
3239
- if (inboxReady)
2892
+ if (!skipped.has("INBOX"))
3240
2893
  operations.push("INBOX");
3241
- // WAIT is withheld only where the repeat-cap recorded it as dead — a
3242
- // model-chosen wait that left a page with rows unchanged. An empty snapshot
3243
- // never records one, so a payment settling behind a blank processor screen
3244
- // keeps its wait for as long as the budgets allow.
3245
- if (!listedWork && !inboxReady && !skipped.has("WAIT"))
2894
+ if (!skipped.has("WAIT"))
3246
2895
  operations.push("WAIT");
3247
2896
  operations.push("DONE");
3248
- if (!listedWork && !inboxReady)
3249
- operations.push("BLOCKED");
2897
+ operations.push("BLOCKED");
3250
2898
  return { operations, TYPE_TEXT: typeText, SELECT: select, CLICK: click, SCROLL: scroll };
3251
2899
  }
3252
2900
  function criteriaFromCandidates(candidates, operation) {
@@ -3270,7 +2918,6 @@ export function emailCodeCandidates(rows, filledRefs = []) {
3270
2918
  .filter((row) => isFillableRow(row) &&
3271
2919
  !isSelectRow(row) &&
3272
2920
  !isDisabledRow(row) &&
3273
- !isOffscreenRow(row) &&
3274
2921
  !isActedRow(row) &&
3275
2922
  !filled.has(row[0]) &&
3276
2923
  rowValueMissing(row) &&
@@ -3471,10 +3118,7 @@ export function pageSuggestsInboxWait(rows, _pageUrl = "", pageText = "") {
3471
3118
  const hay = `${pageText} ${headingCopy(rows)} ${rows.map((row) => readableLabel(row)).join(" ")}`.toLowerCase();
3472
3119
  return emailVerificationCopy(hay) || /open gmail|#search\//.test(hay);
3473
3120
  }
3474
- export function goalSeeksVerification(goal) {
3475
- return /verif(?:y|ication)|confirm(?:ation)?(?:\s+your)?\s+e-?mail|check your e-?mail/.test(goal.toLowerCase());
3476
- }
3477
- export function inboxSpecialPlan(rows, decisionKind, _clicked, remainingFillCount, pageUrl = "", pageText = "", goal = "", submittedThisDrive = false) {
3121
+ export function inboxSpecialPlan(rows, decisionKind, _clicked, remainingFillCount, pageUrl = "", pageText = "", _goal = "", submittedThisDrive = false) {
3478
3122
  if (decisionKind !== "stuck" && decisionKind !== "wait")
3479
3123
  return undefined;
3480
3124
  const waitingForMail = pageSuggestsInboxWait(rows, pageUrl, pageText);
@@ -3500,12 +3144,8 @@ export function inboxSpecialPlan(rows, decisionKind, _clicked, remainingFillCoun
3500
3144
  !isOtpRow(row) &&
3501
3145
  !isOauthChromeRow(row) &&
3502
3146
  !isOffscreenRow(row))) {
3503
- if (submittedThisDrive && goalSeeksVerification(goal))
3504
- return { kind: "link" };
3505
3147
  return undefined;
3506
3148
  }
3507
- if (submittedThisDrive && goalSeeksVerification(goal))
3508
- return { kind: "link" };
3509
3149
  return undefined;
3510
3150
  }
3511
3151
  export function inboxVerificationDecision(verification, planKind) {
@@ -4005,7 +3645,7 @@ function publicDriveHandoff(session, handoff) {
4005
3645
  }
4006
3646
  return value;
4007
3647
  };
4008
- return maskDriveOutput(session, visit(handoff));
3648
+ return maskCardValues(session, visit(handoff));
4009
3649
  }
4010
3650
  function maskedRefsOf(drive) {
4011
3651
  if (!Array.isArray(drive.maskedValueRefs))
@@ -4272,11 +3912,34 @@ async function actSafely(deps, sessionId, action) {
4272
3912
  };
4273
3913
  }
4274
3914
  }
4275
- export function resumeAnswerOptions(rows, facts, goal, includePayment, pageUrl, liveProviders) {
3915
+ function resumeAllowsRow(row, rows, liveProviders) {
3916
+ const liveProviderOffered = rows.some((candidate) => {
3917
+ const provider = oauthProviderForRow(candidate);
3918
+ return provider !== undefined && liveProviders?.includes(provider) === true;
3919
+ });
3920
+ if (!liveProviderOffered || !isOauthChromeRow(row))
3921
+ return true;
3922
+ const provider = oauthProviderForRow(row);
3923
+ return provider !== undefined && liveProviders?.includes(provider) === true;
3924
+ }
3925
+ function resumeTargetSets(rows, facts, goal, includePayment, pageUrl, liveProviders) {
4276
3926
  const sets = driveTargetSets(rows, facts, includePayment, [], pageUrl, new Map(), (text) => text, [], {
4277
3927
  goal,
4278
3928
  ...(liveProviders === undefined ? {} : { liveProviders }),
4279
3929
  });
3930
+ // Jev sees every row. The human handoff offers only providers already live
3931
+ // in this browser when one of those providers is present on the page.
3932
+ const click = sets.CLICK.filter((candidate) => resumeAllowsRow(candidate.row, rows, liveProviders));
3933
+ return {
3934
+ ...sets,
3935
+ CLICK: click,
3936
+ operations: click.length === 0
3937
+ ? sets.operations.filter((operation) => operation !== "CLICK")
3938
+ : sets.operations,
3939
+ };
3940
+ }
3941
+ export function resumeAnswerOptions(rows, facts, goal, includePayment, pageUrl, liveProviders) {
3942
+ const sets = resumeTargetSets(rows, facts, goal, includePayment, pageUrl, liveProviders);
4280
3943
  // The question builder applies the decision-budget cap to these same sets.
4281
3944
  // A handoff must offer exactly the target keys its resume validator accepts.
4282
3945
  buildDriveQuestions(rows, facts, goal, includePayment, [], pageUrl, new Map(), sets);
@@ -4298,21 +3961,14 @@ export function resumeAction(answer, rows, facts, goal, cardRef, pageUrl, livePr
4298
3961
  if (answer === "WAIT" || answer === "wait")
4299
3962
  return { kind: "wait", confidence: 1 };
4300
3963
  const includePayment = cardRef !== undefined;
4301
- const sets = driveTargetSets(rows, facts, includePayment, [], pageUrl, new Map(), (text) => text, [], {
4302
- goal,
4303
- ...(liveProviders === undefined ? {} : { liveProviders }),
4304
- });
3964
+ const sets = resumeTargetSets(rows, facts, goal, includePayment, pageUrl, liveProviders);
4305
3965
  const questions = buildDriveQuestions(rows, facts, goal, includePayment, [], pageUrl, new Map(), sets);
4306
3966
  const validKeys = Object.keys(resumeAnswerOptions(rows, facts, goal, includePayment, pageUrl, liveProviders));
4307
3967
  const offered = [...sets.CLICK, ...sets.TYPE_TEXT, ...sets.SELECT].find((entry) => entry.slug === answer || entry.ref === answer);
4308
3968
  const fallbackRow = findRow(rows, answer, pageUrl);
4309
- const provider = fallbackRow === undefined ? undefined : oauthProviderForRow(fallbackRow);
4310
- const liveProviderOffered = rows.some((row) => {
4311
- const offeredProvider = oauthProviderForRow(row);
4312
- return offeredProvider !== undefined && liveProviders?.includes(offeredProvider) === true;
4313
- });
4314
- const row = offered?.row ?? (fallbackRow !== undefined && isOauthChromeRow(fallbackRow) && liveProviderOffered &&
4315
- (provider === undefined || !liveProviders?.includes(provider)) ? undefined : fallbackRow);
3969
+ const row = offered?.row ?? (fallbackRow !== undefined && resumeAllowsRow(fallbackRow, rows, liveProviders)
3970
+ ? fallbackRow
3971
+ : undefined);
4316
3972
  if (row === undefined) {
4317
3973
  return {
4318
3974
  kind: "invalid_answer",
@@ -4559,7 +4215,7 @@ async function driveLoop(input) {
4559
4215
  let liveProviders;
4560
4216
  const detectOfferedProviderSessions = async () => {
4561
4217
  if (liveProviders === undefined &&
4562
- new Set(rows.map(oauthProviderForRow).filter((provider) => provider !== undefined)).size > 1) {
4218
+ rows.filter(isOauthChromeRow).length > 1) {
4563
4219
  liveProviders = await liveProviderSessionsForSession(sessionId);
4564
4220
  }
4565
4221
  };
@@ -4779,39 +4435,25 @@ async function driveLoop(input) {
4779
4435
  if (completeSnap !== "ok")
4780
4436
  return completeSnap;
4781
4437
  const fresh = driveProgressFingerprint(observation, rows, drive, session);
4782
- // Completion for a key goal is decided from fresh extraction below. A
4783
- // binding from the preceding page must not veto a readable credential.
4784
- if (!isKeyGoal(drive.goal) &&
4785
- drive.boundFingerprint !== null &&
4438
+ if (drive.boundFingerprint !== null &&
4786
4439
  fresh !== drive.boundFingerprint) {
4787
4440
  drive.consumedActionKey = null;
4788
4441
  automaticDecisionRefused = true;
4789
4442
  return "continue";
4790
4443
  }
4791
- // A key goal requires a secret-shaped value from the same capture flow
4792
- // as operate_extract. The model decides whether it satisfies the goal.
4793
- const keyEvidence = isKeyGoal(drive.goal) ? await driveKeyEvidence(sessionId) : undefined;
4794
- if (keyEvidence !== undefined && !driveKeyGoalComplete(keyEvidence)) {
4795
- noteOutcome({
4796
- beforeUrl: observation.url,
4797
- afterUrl: observation.url,
4798
- action: "DONE",
4799
- step: drive.trajectory.length,
4800
- executed: false,
4801
- notExecutedReason: "refused",
4802
- beforeFingerprint: drive.boundFingerprint ?? fresh,
4803
- afterFingerprint: fresh,
4804
- });
4805
- const masked = maskedRemainingReason(keyEvidence);
4806
- drive.history.push(masked === undefined
4807
- ? "DONE refused: extraction found no secret-shaped credential"
4808
- : `DONE refused: ${masked}`);
4444
+ // Try the known Copy source on every DONE, even if the value has no
4445
+ // recognizable shape or label in the observation. Page evidence only
4446
+ // decides whether a failed extraction must hand back for explicit capture.
4447
+ const extracted = await extractCredentials(sessionId).catch(() => null);
4448
+ if (storableCredentials(extracted?.credentials ?? {}) === null &&
4449
+ pageShowsCredentialValue(rows)) {
4450
+ drive.history.push("DONE refused: a visible credential has no captured source");
4809
4451
  drive.consumedActionKey = null;
4810
4452
  const stallKey = pageProgressKey(observation.url, rows, drive.filledRefs, observation.semantic?.headings ?? []);
4811
4453
  drive.stallKeys ??= [];
4812
4454
  if (drive.stallKeys.includes(stallKey)) {
4813
4455
  return finish("stuck", {
4814
- reason: masked ?? "no stored credential is on the page",
4456
+ reason: "no key with a known source: read the page with operate_observe and point capture at the key",
4815
4457
  });
4816
4458
  }
4817
4459
  drive.stallKeys.push(stallKey);
@@ -5082,6 +4724,13 @@ async function driveLoop(input) {
5082
4724
  automaticDecisionRefused = true;
5083
4725
  return "continue";
5084
4726
  }
4727
+ if ((decision.action.kind === "click" || decision.action.kind === "select") &&
4728
+ liveRow !== undefined &&
4729
+ isExistingCredentialMutationRow(liveRow, observation.url, rows, decision.action.kind === "select" ? decision.action.text : "", observation.semantic?.headings ?? [])) {
4730
+ return finish("stuck", {
4731
+ reason: `operator decision required before ${readableLabel(liveRow)} on ${observation.url}; no action was dispatched`,
4732
+ });
4733
+ }
5085
4734
  if ((decision.action.kind === "click" || decision.action.kind === "oauth_login") &&
5086
4735
  liveRow !== undefined &&
5087
4736
  isSubmitLikeRow(liveRow) &&
@@ -5533,7 +5182,7 @@ async function driveLoop(input) {
5533
5182
  // progress to fill, not a failed response to a submitted form.
5534
5183
  if (clicked !== undefined &&
5535
5184
  isSubmitLikeRow(clicked) &&
5536
- !isKeyCreateOpener(clicked, drive.goal)) {
5185
+ !isKeyCreateOpener(clicked)) {
5537
5186
  captchaAfterSubmit = true;
5538
5187
  drive.submitBeforeText = textBeforeClick;
5539
5188
  drive.submitExcludeLabels = excludeBeforeClick;
@@ -5573,7 +5222,7 @@ async function driveLoop(input) {
5573
5222
  }
5574
5223
  }
5575
5224
  if (shouldInspectSubmitResponse(decision.action, clickedBefore) &&
5576
- !isKeyCreateOpener(clickedBefore, drive.goal)) {
5225
+ !isKeyCreateOpener(clickedBefore)) {
5577
5226
  const navigated = pagePathKey(observation.url) !== pagePathKey(urlBeforeClick);
5578
5227
  const fieldError = invalidFieldReason(rows);
5579
5228
  if (fieldError !== undefined) {
@@ -5662,7 +5311,7 @@ async function driveLoop(input) {
5662
5311
  binding: decisionTargetBinding(clicked, urlBeforeClick),
5663
5312
  url: urlBeforeClick,
5664
5313
  };
5665
- if (isSubmitLikeRow(clicked) && !isKeyCreateOpener(clicked, drive.goal)) {
5314
+ if (isSubmitLikeRow(clicked) && !isKeyCreateOpener(clicked)) {
5666
5315
  drive.submittedThisDrive = true;
5667
5316
  }
5668
5317
  }
@@ -5772,13 +5421,15 @@ async function driveLoop(input) {
5772
5421
  }
5773
5422
  const ask = async (state, questions) => {
5774
5423
  try {
5775
- const jev = await dependencies.askJev(api, maskDriveOutput(session, state), maskDriveOutput(session, questions), context?.signal);
5424
+ const jev = await dependencies.askJev(api, maskCardValues(session, state), maskCardValues(session, questions), context?.signal);
5776
5425
  drive.jevCalls += 1;
5777
5426
  return jev;
5778
5427
  }
5779
5428
  catch (error) {
5780
- if (error instanceof JevUnavailableError || error instanceof JevRequestError) {
5781
- return finish("jev_unavailable", { jevRetried: error.message });
5429
+ if (error instanceof JevUnavailableError ||
5430
+ error instanceof JevRequestError ||
5431
+ error instanceof JevInvalidResponseError) {
5432
+ return finish("jev_unavailable", { jevRetried: error.message, reason: error.message });
5782
5433
  }
5783
5434
  throw error;
5784
5435
  }
@@ -5802,9 +5453,6 @@ async function driveLoop(input) {
5802
5453
  let missing = requiredFillableMissingFact(rows, drive.facts, drive.filledRefs, pageUrl, drive.goal);
5803
5454
  let pageOptions = lastSelectOptions.get(session) ?? selectOptionsFromElements(session.lastElements);
5804
5455
  const remainingFills = fillableCandidates(rows, drive.facts, includePayment, drive.filledRefs, pageUrl);
5805
- let hasGoalDestination = rows.some((row) => rowCarriesGoalNoun(row, args.goal) &&
5806
- isGoalDestinationRow(row) &&
5807
- !isOffProductNavRow(row, pageUrl));
5808
5456
  let terminalOnly = false;
5809
5457
  // A rejected pre-act choice must reach the model on the refreshed page.
5810
5458
  // None of the automatic choices below may consume that opportunity.
@@ -5923,49 +5571,6 @@ async function driveLoop(input) {
5923
5571
  },
5924
5572
  });
5925
5573
  }
5926
- if (isPreexistingSessionPage({
5927
- rows,
5928
- pageUrl,
5929
- pageText: pageTextFromObservation(observation, [observation.dom ?? ""]),
5930
- goal: args.goal,
5931
- submittedThisDrive: drive.submittedThisDrive === true,
5932
- })) {
5933
- if (drive.preexistingRestarted === true ||
5934
- rows.find((row) => isLogoutRow(row)) === undefined) {
5935
- return finish("stuck", { reason: alreadySignedInReason() });
5936
- }
5937
- const logout = rows.find((row) => isLogoutRow(row));
5938
- if (logout === undefined) {
5939
- return finish("stuck", { reason: alreadySignedInReason() });
5940
- }
5941
- drive.preexistingRestarted = true;
5942
- drive.boundFingerprint = driveProgressFingerprint(observation, rows, drive, session);
5943
- drive.consumedActionKey = null;
5944
- const applied = await applyDecision({
5945
- kind: "act",
5946
- action: { kind: "click", target: logout[0] },
5947
- actionKey: logout[0],
5948
- confidence: 1,
5949
- });
5950
- if (applied !== "continue")
5951
- return applied;
5952
- if (automaticDecisionRefused)
5953
- break automaticDecisions;
5954
- const safe = await actSafely(dependencies, sessionId, {
5955
- kind: "goto",
5956
- url: session.startUrl,
5957
- });
5958
- drive.lastDispatchFailure = safe.dispatchFailure ?? null;
5959
- drive.lastOauthBounceReason = null;
5960
- observation = safe.observation;
5961
- if (observation.needs_user !== undefined)
5962
- return finishOnWall(observation.needs_user);
5963
- const restarted = await snapshotOrTimeout(framesIfNeeded());
5964
- if (restarted !== "ok")
5965
- return restarted;
5966
- spendStep("preexisting_restart");
5967
- continue;
5968
- }
5969
5574
  // A same-document stage swap (Shopify one-page checkout) and a hydrating
5970
5575
  // checkout both leave the snapshot empty for a while, so spend the
5971
5576
  // re-observation budget before asking anything. Past it the ordinary
@@ -6226,9 +5831,6 @@ async function driveLoop(input) {
6226
5831
  else {
6227
5832
  return finish("stuck", { reason: missingPay });
6228
5833
  }
6229
- if (goalExcludesOauth(args.goal) && pageOffersOnlyThirdPartySignup(rows)) {
6230
- return finish("stuck", { reason: noOtherSignupPathReason() });
6231
- }
6232
5834
  drive.awaitingDecideAfterExplore = false;
6233
5835
  // Rule 1: undo a narrowing input the drive itself typed before the loop
6234
5836
  // decides anything else, then re-offer the control it removed.
@@ -6246,9 +5848,6 @@ async function driveLoop(input) {
6246
5848
  missing = requiredFillableMissingFact(rows, drive.facts, drive.filledRefs, pageUrl, drive.goal);
6247
5849
  pageOptions =
6248
5850
  lastSelectOptions.get(session) ?? selectOptionsFromElements(session.lastElements);
6249
- hasGoalDestination = rows.some((row) => rowCarriesGoalNoun(row, args.goal) &&
6250
- isGoalDestinationRow(row) &&
6251
- !isOffProductNavRow(row, pageUrl));
6252
5851
  terminalOnly = false;
6253
5852
  }
6254
5853
  if (drive.jevCalls >= DRIVE_MAX_JEV_CALLS) {
@@ -6265,7 +5864,7 @@ async function driveLoop(input) {
6265
5864
  ...new Set([...(drive.exhaustedActionKeys ?? []), ...(drive.staleClickRefs ?? [])]),
6266
5865
  ];
6267
5866
  await detectOfferedProviderSessions();
6268
- const sets = driveTargetSets(rows, drive.facts, includePayment, drive.filledRefs, pageUrl, pageOptions, (text) => maskDriveOutput(session, text), skippedActions, {
5867
+ const sets = driveTargetSets(rows, drive.facts, includePayment, drive.filledRefs, pageUrl, pageOptions, (text) => maskCardValues(session, text), skippedActions, {
6269
5868
  headings: observation.semantic?.headings ?? [],
6270
5869
  failedKeys: drive.failedActionKeys ?? [],
6271
5870
  goal: drive.goal,
@@ -6425,61 +6024,6 @@ async function driveLoop(input) {
6425
6024
  jev_question_count: questionCount,
6426
6025
  jev_state_bytes: stateBytes,
6427
6026
  };
6428
- const inboxPlan = inboxSpecialPlan(rows, decision.kind, lastNonWaitWasClick(drive.trajectory), remainingFills.length, observation.url, pageTextFromObservation(observation, [observation.dom ?? ""]), drive.goal, drive.submittedThisDrive === true);
6429
- if (inboxPlan !== undefined) {
6430
- const lastClick = [...drive.trajectory]
6431
- .reverse()
6432
- .find((step) => step.action === "click" || step.action === "oauth_login");
6433
- const applied = await applyDecision({
6434
- kind: "act",
6435
- action: inboxPlan.kind === "otp"
6436
- ? { kind: "type", target: inboxPlan.target, text: "" }
6437
- : { kind: "click", target: lastClick?.target ?? "inbox_link" },
6438
- actionKey: inboxPlan.kind === "otp" ? inboxPlan.target : "inbox_link",
6439
- confidence: 1,
6440
- special: "inbox",
6441
- }, jevMs);
6442
- if (applied !== "continue")
6443
- return applied;
6444
- spendStep("inbox_plan");
6445
- continue;
6446
- }
6447
- if (goalSeeksKey(args.goal) && !pageShowsRevealedKey(rows)) {
6448
- const visited = drive.visitedSectionKeys ?? [];
6449
- const untriedEntries = untriedListedItemRows(rows, visited, pageUrl);
6450
- const nextSection = nextExploreRow(rows, visited, pageUrl);
6451
- const picked = decisionIsActionable(decision)
6452
- ? findRow(rows, decision.actionKey, pageUrl)
6453
- : undefined;
6454
- const pickedIsReveal = picked !== undefined && isRevealOrCopyRow(picked);
6455
- const pickedIsPending = picked !== undefined && isPendingPageAction(picked, args.goal);
6456
- const pickedIsUntriedEntry = picked !== undefined && untriedEntries.some((entry) => entry[0] === picked[0]);
6457
- const pickedVisitedTab = picked !== undefined &&
6458
- isSiblingSectionNavRow(picked, pageUrl, rows) &&
6459
- visited.includes(sectionIdentity(picked, pageUrl));
6460
- const takeExplore = nextSection !== undefined &&
6461
- !pickedIsPending &&
6462
- ((untriedEntries.length > 0 && !pickedIsUntriedEntry && !pickedIsReveal) ||
6463
- pickedVisitedTab ||
6464
- (!decisionIsActionable(decision) && !hasGoalDestination));
6465
- if (takeExplore && nextSection !== undefined) {
6466
- const applied = await applyDecision({
6467
- kind: "act",
6468
- action: { kind: "click", target: nextSection[0] },
6469
- actionKey: nextSection[0],
6470
- confidence: 1,
6471
- });
6472
- if (applied !== "continue")
6473
- return applied;
6474
- drive.boundFingerprint = driveProgressFingerprint(observation, rows, drive, session);
6475
- drive.consumedActionKey = null;
6476
- spendStep("explore");
6477
- continue;
6478
- }
6479
- if (!decisionIsActionable(decision) && !hasGoalDestination && visited.length > 0) {
6480
- return finish("no_progress", { reason: sectionsTriedReason(visited) });
6481
- }
6482
- }
6483
6027
  const applied = await applyDecision(decision, jevMs, true);
6484
6028
  if (applied !== "continue")
6485
6029
  return applied;