@enricai/barnacle 1.6.1 → 1.6.3

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 (105) hide show
  1. package/README.md +36 -29
  2. package/dist/api/errors.d.ts +3 -3
  3. package/dist/api/errors.d.ts.map +1 -1
  4. package/dist/api/errors.js +4 -4
  5. package/dist/api/errors.js.map +1 -1
  6. package/dist/api/helpers/reply.d.ts +1 -1
  7. package/dist/api/helpers/reply.js +1 -1
  8. package/dist/cache/keyed-ttl-cache.d.ts +4 -4
  9. package/dist/cache/keyed-ttl-cache.js +2 -2
  10. package/dist/lib/applicant-payload.d.ts +4 -4
  11. package/dist/lib/applicant-payload.js +4 -4
  12. package/dist/lib/application-address.d.ts +3 -3
  13. package/dist/lib/application-address.js +5 -5
  14. package/dist/lib/application-address.js.map +1 -1
  15. package/dist/lib/application-resume.d.ts +4 -4
  16. package/dist/lib/application-resume.js +4 -4
  17. package/dist/lib/dispatch-metrics.js +1 -1
  18. package/dist/lib/llm/judges/verify-submit.d.ts +1 -1
  19. package/dist/lib/llm/judges/verify-submit.js +1 -1
  20. package/dist/lib/llm/schemas.js +3 -3
  21. package/dist/lib/phone.d.ts +1 -1
  22. package/dist/lib/phone.js +1 -1
  23. package/dist/lib/telemetry/call-types.d.ts +1 -1
  24. package/dist/lib/telemetry/call-types.js +1 -1
  25. package/dist/lib/tracking-click.d.ts +5 -3
  26. package/dist/lib/tracking-click.d.ts.map +1 -1
  27. package/dist/lib/tracking-click.js +5 -3
  28. package/dist/lib/tracking-click.js.map +1 -1
  29. package/dist/lib/us-states.d.ts +1 -1
  30. package/dist/lib/us-states.js +1 -1
  31. package/dist/plugins/loader.js +2 -2
  32. package/dist/scraper/cookie-jar.d.ts +2 -2
  33. package/dist/scraper/cookie-jar.js +2 -2
  34. package/dist/scraper/deep-query.d.ts +36 -0
  35. package/dist/scraper/deep-query.d.ts.map +1 -0
  36. package/dist/scraper/deep-query.js +87 -0
  37. package/dist/scraper/deep-query.js.map +1 -0
  38. package/dist/scraper/errors.d.ts +12 -14
  39. package/dist/scraper/errors.d.ts.map +1 -1
  40. package/dist/scraper/errors.js +12 -17
  41. package/dist/scraper/errors.js.map +1 -1
  42. package/dist/scraper/flow-runner.d.ts +97 -30
  43. package/dist/scraper/flow-runner.d.ts.map +1 -1
  44. package/dist/scraper/flow-runner.js +304 -104
  45. package/dist/scraper/flow-runner.js.map +1 -1
  46. package/dist/scraper/http-client.d.ts +11 -1
  47. package/dist/scraper/http-client.d.ts.map +1 -1
  48. package/dist/scraper/http-client.js +90 -3
  49. package/dist/scraper/http-client.js.map +1 -1
  50. package/dist/scraper/phantom-click.d.ts +45 -0
  51. package/dist/scraper/phantom-click.d.ts.map +1 -0
  52. package/dist/scraper/phantom-click.js +37 -0
  53. package/dist/scraper/phantom-click.js.map +1 -0
  54. package/dist/scraper/raw-fetch.d.ts +2 -2
  55. package/dist/scraper/raw-fetch.js +1 -1
  56. package/dist/scraper/require-response-field.d.ts +1 -1
  57. package/dist/scraper/require-response-field.js +1 -1
  58. package/dist/scraper/session-browserbase.d.ts +48 -0
  59. package/dist/scraper/session-browserbase.d.ts.map +1 -1
  60. package/dist/scraper/session-browserbase.js +10 -5
  61. package/dist/scraper/session-browserbase.js.map +1 -1
  62. package/dist/scraper/session-shared.d.ts +6 -0
  63. package/dist/scraper/session-shared.d.ts.map +1 -1
  64. package/dist/scraper/session-shared.js.map +1 -1
  65. package/dist/scraper/session-warmup.d.ts +1 -1
  66. package/dist/scraper/session-warmup.js +1 -1
  67. package/dist/scraper/submit-control.d.ts +51 -0
  68. package/dist/scraper/submit-control.d.ts.map +1 -0
  69. package/dist/scraper/submit-control.js +142 -0
  70. package/dist/scraper/submit-control.js.map +1 -0
  71. package/dist/scripts/migrate-telemetry-dir-names.d.ts +2 -2
  72. package/dist/scripts/migrate-telemetry-dir-names.d.ts.map +1 -1
  73. package/dist/scripts/migrate-telemetry-dir-names.js +1 -1
  74. package/dist/scripts/recon-browser.d.ts +25 -20
  75. package/dist/scripts/recon-browser.d.ts.map +1 -1
  76. package/dist/scripts/recon-browser.js +65 -56
  77. package/dist/scripts/recon-browser.js.map +1 -1
  78. package/dist/scripts/recon-generate.d.ts +52 -7
  79. package/dist/scripts/recon-generate.d.ts.map +1 -1
  80. package/dist/scripts/recon-generate.js +73 -31
  81. package/dist/scripts/recon-generate.js.map +1 -1
  82. package/dist/scripts/recon-http.d.ts +36 -5
  83. package/dist/scripts/recon-http.d.ts.map +1 -1
  84. package/dist/scripts/recon-http.js +77 -47
  85. package/dist/scripts/recon-http.js.map +1 -1
  86. package/dist/scripts/recon-replay-jobs.d.ts +32 -3
  87. package/dist/scripts/recon-replay-jobs.d.ts.map +1 -1
  88. package/dist/scripts/recon-replay-jobs.js +46 -29
  89. package/dist/scripts/recon-replay-jobs.js.map +1 -1
  90. package/dist/scripts/recon-shared.d.ts +45 -0
  91. package/dist/scripts/recon-shared.d.ts.map +1 -1
  92. package/dist/scripts/recon-shared.js +117 -0
  93. package/dist/scripts/recon-shared.js.map +1 -1
  94. package/dist/scripts/recon-summarize.d.ts +5 -1
  95. package/dist/scripts/recon-summarize.d.ts.map +1 -1
  96. package/dist/scripts/recon-summarize.js +19 -7
  97. package/dist/scripts/recon-summarize.js.map +1 -1
  98. package/dist/site-plugin.d.ts +4 -4
  99. package/dist/testing/persona-fixture.d.ts +4 -4
  100. package/dist/testing/persona-fixture.d.ts.map +1 -1
  101. package/dist/testing/persona-fixture.js +4 -4
  102. package/dist/testing/persona-fixture.js.map +1 -1
  103. package/dist/types/dispatch-metrics.d.ts +1 -1
  104. package/dist/types/dispatch-metrics.js +1 -1
  105. package/package.json +1 -1
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Shadow-DOM-piercing element resolver. Every other primitive in
3
+ * flow-runner.ts locates elements via `document.querySelectorAll` /
4
+ * `document.evaluate`, which cannot see inside a shadow root — so a
5
+ * submit-shaped control rendered by a web component (Angular Elements,
6
+ * Stencil, etc.) is invisible to the engine even though it's live on the
7
+ * page. This module composes a `page.evaluate` expression string (the
8
+ * repo's established interpolation pattern — see `INVALID_MARKER_EL_EXPR`
9
+ * in flow-runner.ts) that recurses through `el.shadowRoot` for OPEN roots
10
+ * to find and click such a control. Closed roots are unreachable from page
11
+ * script by design; the traversal treats them as a dead end rather than
12
+ * throwing.
13
+ */
14
+ /**
15
+ * Builds a self-contained `page.evaluate` expression string that locates
16
+ * the first submit-shaped control anywhere in the document — piercing open
17
+ * shadow roots — clicks it (setting focus, then dispatching bubbling
18
+ * mousedown/mouseup/click events, matching the repo's controlled-state
19
+ * click convention rather than a bare `el.click()`, see flow-runner.ts's
20
+ * checkbox/radio primitives), and returns a structured result so the
21
+ * caller can verify what happened without a second round-trip.
22
+ *
23
+ * Locate-only mode (`{ clickIfFound: false }`) is exposed for callers that
24
+ * want to probe for a deep submit control before deciding whether to act
25
+ * on it (e.g. to distinguish "no candidate anywhere" from "found but the
26
+ * click cascade should try a different technique first").
27
+ */
28
+ export declare function buildDeepSubmitClickExpr(options?: {
29
+ clickIfFound?: boolean;
30
+ }): string;
31
+ /** Structured result of {@link buildDeepSubmitClickExpr}'s `page.evaluate` call. */
32
+ export interface DeepSubmitClickResult {
33
+ found: boolean;
34
+ clicked: boolean;
35
+ }
36
+ //# sourceMappingURL=deep-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deep-query.d.ts","sourceRoot":"","sources":["../../src/scraper/deep-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA4CH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAerF;AAED,oFAAoF;AACpF,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB"}
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ /**
3
+ * Shadow-DOM-piercing element resolver. Every other primitive in
4
+ * flow-runner.ts locates elements via `document.querySelectorAll` /
5
+ * `document.evaluate`, which cannot see inside a shadow root — so a
6
+ * submit-shaped control rendered by a web component (Angular Elements,
7
+ * Stencil, etc.) is invisible to the engine even though it's live on the
8
+ * page. This module composes a `page.evaluate` expression string (the
9
+ * repo's established interpolation pattern — see `INVALID_MARKER_EL_EXPR`
10
+ * in flow-runner.ts) that recurses through `el.shadowRoot` for OPEN roots
11
+ * to find and click such a control. Closed roots are unreachable from page
12
+ * script by design; the traversal treats them as a dead end rather than
13
+ * throwing.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.buildDeepSubmitClickExpr = buildDeepSubmitClickExpr;
17
+ /**
18
+ * Text/attribute predicate for "this element is submit-shaped": a native
19
+ * `type="submit"` control, a `<button>` with no explicit `type` inside a
20
+ * `<form>` (the HTML default is submit), or a button-role element whose
21
+ * visible text/aria-label contains "submit". Kept as a standalone
22
+ * expression (not a RegExp) so it can be interpolated into a browser-
23
+ * context `page.evaluate` string, mirroring `INVALID_MARKER_EL_EXPR`.
24
+ */
25
+ const SUBMIT_SHAPED_EL_EXPR = `((el) => {
26
+ const tag = (el.tagName || "").toLowerCase();
27
+ const type = (el.getAttribute("type") || "").toLowerCase();
28
+ if ((tag === "button" || tag === "input") && type === "submit") return true;
29
+ if (tag === "button" && !el.getAttribute("type") && el.closest("form")) return true;
30
+ const role = (el.getAttribute("role") || "").toLowerCase();
31
+ const isButtonLike = tag === "button" || role === "button";
32
+ if (!isButtonLike) return false;
33
+ const norm = (s) => (s || "").replace(/\\s+/g, " ").trim().toLowerCase();
34
+ const text = norm(el.getAttribute("aria-label") || el.textContent || "");
35
+ return /\\bsubmit\\b/.test(text);
36
+ })`;
37
+ /**
38
+ * Recursive open-shadow-root walker: returns every element in `root`
39
+ * (light DOM or a shadow root) plus, for each child with an OPEN
40
+ * `shadowRoot`, every element inside that shadow tree, arbitrarily deep.
41
+ * A closed shadow root (`element.shadowRoot === null` from page script's
42
+ * perspective) is simply not descended into — it contributes no elements,
43
+ * it does not throw.
44
+ */
45
+ const DEEP_ELEMENTS_EXPR = `((root) => {
46
+ const out = [];
47
+ const walk = (node) => {
48
+ const kids = node.querySelectorAll ? Array.from(node.querySelectorAll("*")) : [];
49
+ for (const el of kids) {
50
+ out.push(el);
51
+ if (el.shadowRoot) walk(el.shadowRoot);
52
+ }
53
+ };
54
+ walk(root);
55
+ return out;
56
+ })`;
57
+ /**
58
+ * Builds a self-contained `page.evaluate` expression string that locates
59
+ * the first submit-shaped control anywhere in the document — piercing open
60
+ * shadow roots — clicks it (setting focus, then dispatching bubbling
61
+ * mousedown/mouseup/click events, matching the repo's controlled-state
62
+ * click convention rather than a bare `el.click()`, see flow-runner.ts's
63
+ * checkbox/radio primitives), and returns a structured result so the
64
+ * caller can verify what happened without a second round-trip.
65
+ *
66
+ * Locate-only mode (`{ clickIfFound: false }`) is exposed for callers that
67
+ * want to probe for a deep submit control before deciding whether to act
68
+ * on it (e.g. to distinguish "no candidate anywhere" from "found but the
69
+ * click cascade should try a different technique first").
70
+ */
71
+ function buildDeepSubmitClickExpr(options) {
72
+ const clickIfFound = options?.clickIfFound ?? true;
73
+ return `(() => {
74
+ const isSubmitShaped = ${SUBMIT_SHAPED_EL_EXPR};
75
+ const deepElements = ${DEEP_ELEMENTS_EXPR};
76
+ const candidates = deepElements(document).filter(isSubmitShaped);
77
+ if (candidates.length === 0) return { found: false, clicked: false };
78
+ const el = candidates[0];
79
+ if (!${JSON.stringify(clickIfFound)}) return { found: true, clicked: false };
80
+ if (typeof el.focus === "function") { try { el.focus(); } catch (e) {} }
81
+ el.dispatchEvent(new Event("mousedown", { bubbles: true }));
82
+ el.dispatchEvent(new Event("mouseup", { bubbles: true }));
83
+ el.dispatchEvent(new Event("click", { bubbles: true }));
84
+ return { found: true, clicked: true };
85
+ })()`;
86
+ }
87
+ //# sourceMappingURL=deep-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deep-query.js","sourceRoot":"","sources":["../../src/scraper/deep-query.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AAEH;;;;;;;GAOG;AACH,MAAM,qBAAqB,GAAG;;;;;;;;;;;GAW3B,CAAC;AAEJ;;;;;;;GAOG;AACH,MAAM,kBAAkB,GAAG;;;;;;;;;;;GAWxB,CAAC;AAEJ;;;;;;;;;;;;;GAaG;AACH,kCAAyC,OAAoC;IAC3E,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,IAAI,CAAC;IACnD,OAAO;6BACoB,qBAAqB;2BACvB,kBAAkB;;;;WAIlC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC;;;;;;OAMhC,CAAC;AACR,CAAC"}
@@ -59,15 +59,24 @@ export declare class UnknownScraperError extends ScraperError {
59
59
  constructor(message?: string);
60
60
  }
61
61
  /** Discriminator for {@link StepVerificationError}. See the class TSDoc for the per-variant semantics. */
62
- export type StepVerificationErrorKind = "cascade-exhausted" | "probe-absent" | "backend-error-unrecoverable" | "replan-cycle-detected" | "wizard-regression";
62
+ export type StepVerificationErrorKind = "cascade-exhausted" | "phantom-click-exhausted" | "probe-absent" | "backend-error-unrecoverable" | "replan-cycle-detected" | "wizard-regression";
63
63
  /**
64
- * Recon-only: a flow step in recon-browser.ts could not be acted on. Four
64
+ * Recon-only: a flow step in recon-browser.ts could not be acted on. Six
65
65
  * variants per `kind`:
66
66
  *
67
67
  * - "cascade-exhausted": the full 4-attempt self-healing cascade ran and
68
68
  * none of the attempts produced an observable effect. Expensive (the
69
69
  * cascade burned its full LLM/observe budget) — counted against the
70
70
  * cascade replan budget.
71
+ * - "phantom-click-exhausted": attempt 1 phantom-clicked (Stagehand
72
+ * reported success with zero network/url/DOM effect) and the cascade
73
+ * escalated straight to the deep submit-control locator on attempt 2,
74
+ * but every attempt through {@link MAX_STEP_ATTEMPTS} still produced no
75
+ * observable effect — the submit control is unreachable by any
76
+ * resolution strategy the cascade has, not just the light-DOM one.
77
+ * Distinguished from "cascade-exhausted" so a replan/triager can skip
78
+ * straight to a structural fix (e.g. a new locator strategy) instead of
79
+ * re-trying the same techniques.
71
80
  * - "probe-absent": the cheap page-state probe ran BEFORE the cascade
72
81
  * and observed zero candidates for the step's instruction. We skip the
73
82
  * cascade and ask for a replan immediately because the page state is
@@ -140,22 +149,11 @@ export declare class HttpRateLimitError extends ScraperError {
140
149
  * HTTP request nor a fresh Stagehand browser session can succeed. The caller must
141
150
  * back off and surface a "retry later" state rather than burning a Steel session.
142
151
  * Kept distinct from HttpRateLimitError so metrics/logs (classifyDispatchError)
143
- * can tell a requisition lock apart from a self-inflicted 429 rate limit.
152
+ * can tell a resource lock apart from a self-inflicted 429 rate limit.
144
153
  */
145
154
  export declare class HttpUrlLockedError extends ScraperError {
146
155
  constructor(message?: string);
147
156
  }
148
- /**
149
- * @deprecated Removed in 2.0.0. Oracle-specific and no longer thrown by the
150
- * engine — response-body sentinel detection now lives in the plugin via
151
- * `HttpClientOptions.classifyResponseBody`. A plugin that needs a retryable
152
- * token-expiry signal should define its own `ScraperError` subclass (see the
153
- * encompasshealth plugin). Retained only so existing importers of
154
- * `@enricai/barnacle/scraper/errors` keep resolving under a minor release.
155
- */
156
- export declare class OracleTokenExpiredError extends ScraperError {
157
- constructor(message?: string);
158
- }
159
157
  /**
160
158
  * Structured result returned (not thrown) by the hot path when it cannot
161
159
  * complete the application because the user must supply additional data.
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/scraper/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;GAIG;AACH,8BAAsB,YAAa,SAAQ,KAAK;IAC9C,SAAgB,SAAS,EAAE,OAAO,CAAC;IAEnC,YAAY,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAI9C;CACF;AAED;;;GAGG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,YAAY,OAAO,SAAkC,EAEpD;CACF;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,YAAY;IACjD,YAAY,OAAO,SAA+B,EAEjD;CACF;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;IACpD,YAAY,OAAO,SAA6B,EAE/C;CACF;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,YAAY;IACnD,YAAY,OAAO,SAA8B,EAEhD;CACF;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,YAAY;IACnD,YAAY,OAAO,SAA4B,EAE9C;CACF;AAED,0GAA0G;AAC1G,MAAM,MAAM,yBAAyB,GACjC,mBAAmB,GACnB,cAAc,GACd,6BAA6B,GAC7B,uBAAuB,GACvB,mBAAmB,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;IACrD,QAAQ,CAAC,IAAI,EAAE,yBAAyB,CAAC;IACzC,YACE,OAAO,SAA2D,EAClE,IAAI,GAAE,yBAA+C,EAItD;CACF;AAED;;;;GAIG;AACH,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,YAAY,OAAO,SAAkC,EAEpD;CACF;AAED;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;IACrD,YAAY,OAAO,SAAwC,EAE1D;CACF;AAED;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,YAAY,OAAO,SAA0B,EAE5C;CACF;AAED;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;IAClD,YAAY,OAAO,SAAiC,EAEnD;CACF;AAED;;;;;;;;;GASG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;IAClD,YAAY,OAAO,SAA2B,EAE7C;CACF;AAED;;;;;;;GAOG;AACH,qBAAa,uBAAwB,SAAQ,YAAY;IACvD,YAAY,OAAO,SAAiD,EAEnE;CACF;AAED;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,KAAK,CAAC;IAChB,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrD,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,qBAAa,sBAAuB,SAAQ,YAAY;IACtD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,YAAY,WAAW,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAI1D;CACF"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/scraper/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;GAIG;AACH,8BAAsB,YAAa,SAAQ,KAAK;IAC9C,SAAgB,SAAS,EAAE,OAAO,CAAC;IAEnC,YAAY,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAI9C;CACF;AAED;;;GAGG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,YAAY,OAAO,SAAkC,EAEpD;CACF;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,YAAY;IACjD,YAAY,OAAO,SAA+B,EAEjD;CACF;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;IACpD,YAAY,OAAO,SAA6B,EAE/C;CACF;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,YAAY;IACnD,YAAY,OAAO,SAA8B,EAEhD;CACF;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,YAAY;IACnD,YAAY,OAAO,SAA4B,EAE9C;CACF;AAED,0GAA0G;AAC1G,MAAM,MAAM,yBAAyB,GACjC,mBAAmB,GACnB,yBAAyB,GACzB,cAAc,GACd,6BAA6B,GAC7B,uBAAuB,GACvB,mBAAmB,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;IACrD,QAAQ,CAAC,IAAI,EAAE,yBAAyB,CAAC;IACzC,YACE,OAAO,SAA2D,EAClE,IAAI,GAAE,yBAA+C,EAItD;CACF;AAED;;;;GAIG;AACH,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,YAAY,OAAO,SAAkC,EAEpD;CACF;AAED;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;IACrD,YAAY,OAAO,SAAwC,EAE1D;CACF;AAED;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,YAAY,OAAO,SAA0B,EAE5C;CACF;AAED;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;IAClD,YAAY,OAAO,SAAiC,EAEnD;CACF;AAED;;;;;;;;;GASG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;IAClD,YAAY,OAAO,SAA2B,EAE7C;CACF;AAED;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,KAAK,CAAC;IAChB,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrD,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,qBAAa,sBAAuB,SAAQ,YAAY;IACtD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,YAAY,WAAW,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAI1D;CACF"}
@@ -13,7 +13,7 @@
13
13
  * and the diagnostic it represents.
14
14
  */
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.MissingFormMapKeyError = exports.OracleTokenExpiredError = exports.HttpUrlLockedError = exports.HttpRateLimitError = exports.HttpServerError = exports.HttpBotChallengeError = exports.HttpSchemaError = exports.StepVerificationError = exports.UnknownScraperError = exports.SessionTimeoutError = exports.SelectorFailureError = exports.EmptyResultsError = exports.CaptchaError = exports.ScraperError = void 0;
16
+ exports.MissingFormMapKeyError = exports.HttpUrlLockedError = exports.HttpRateLimitError = exports.HttpServerError = exports.HttpBotChallengeError = exports.HttpSchemaError = exports.StepVerificationError = exports.UnknownScraperError = exports.SessionTimeoutError = exports.SelectorFailureError = exports.EmptyResultsError = exports.CaptchaError = exports.ScraperError = void 0;
17
17
  /**
18
18
  * Base class for all scraper-internal errors. Each subclass encodes a
19
19
  * distinct recovery policy (retryable or not) that retry.ts and loader.ts
@@ -82,13 +82,22 @@ class UnknownScraperError extends ScraperError {
82
82
  }
83
83
  exports.UnknownScraperError = UnknownScraperError;
84
84
  /**
85
- * Recon-only: a flow step in recon-browser.ts could not be acted on. Four
85
+ * Recon-only: a flow step in recon-browser.ts could not be acted on. Six
86
86
  * variants per `kind`:
87
87
  *
88
88
  * - "cascade-exhausted": the full 4-attempt self-healing cascade ran and
89
89
  * none of the attempts produced an observable effect. Expensive (the
90
90
  * cascade burned its full LLM/observe budget) — counted against the
91
91
  * cascade replan budget.
92
+ * - "phantom-click-exhausted": attempt 1 phantom-clicked (Stagehand
93
+ * reported success with zero network/url/DOM effect) and the cascade
94
+ * escalated straight to the deep submit-control locator on attempt 2,
95
+ * but every attempt through {@link MAX_STEP_ATTEMPTS} still produced no
96
+ * observable effect — the submit control is unreachable by any
97
+ * resolution strategy the cascade has, not just the light-DOM one.
98
+ * Distinguished from "cascade-exhausted" so a replan/triager can skip
99
+ * straight to a structural fix (e.g. a new locator strategy) instead of
100
+ * re-trying the same techniques.
92
101
  * - "probe-absent": the cheap page-state probe ran BEFORE the cascade
93
102
  * and observed zero candidates for the step's instruction. We skip the
94
103
  * cascade and ask for a replan immediately because the page state is
@@ -177,7 +186,7 @@ exports.HttpRateLimitError = HttpRateLimitError;
177
186
  * HTTP request nor a fresh Stagehand browser session can succeed. The caller must
178
187
  * back off and surface a "retry later" state rather than burning a Steel session.
179
188
  * Kept distinct from HttpRateLimitError so metrics/logs (classifyDispatchError)
180
- * can tell a requisition lock apart from a self-inflicted 429 rate limit.
189
+ * can tell a resource lock apart from a self-inflicted 429 rate limit.
181
190
  */
182
191
  class HttpUrlLockedError extends ScraperError {
183
192
  constructor(message = "requisition url locked") {
@@ -185,20 +194,6 @@ class HttpUrlLockedError extends ScraperError {
185
194
  }
186
195
  }
187
196
  exports.HttpUrlLockedError = HttpUrlLockedError;
188
- /**
189
- * @deprecated Removed in 2.0.0. Oracle-specific and no longer thrown by the
190
- * engine — response-body sentinel detection now lives in the plugin via
191
- * `HttpClientOptions.classifyResponseBody`. A plugin that needs a retryable
192
- * token-expiry signal should define its own `ScraperError` subclass (see the
193
- * encompasshealth plugin). Retained only so existing importers of
194
- * `@enricai/barnacle/scraper/errors` keep resolving under a minor release.
195
- */
196
- class OracleTokenExpiredError extends ScraperError {
197
- constructor(message = "oracle token expired (ORA_IRC_TOKEN_EXPIRED)") {
198
- super(message, true);
199
- }
200
- }
201
- exports.OracleTokenExpiredError = OracleTokenExpiredError;
202
197
  /**
203
198
  * Thrown when code that translates stable semantic field keys (`address.city`,
204
199
  * `firstName`, `applicantGender`) into tenant-specific GUIDs via a runtime
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/scraper/errors.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AAEH;;;;GAIG;AACH,kBAAmC,SAAQ,KAAK;IAC9B,SAAS,CAAU;IAEnC,YAAY,OAAe,EAAE,SAAkB;QAC7C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC;QAC5B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;CACF;;AAED;;;GAGG;AACH,kBAA0B,SAAQ,YAAY;IAC5C,YAAY,OAAO,GAAG,+BAA+B;QACnD,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;;AAED;;;;GAIG;AACH,uBAA+B,SAAQ,YAAY;IACjD,YAAY,OAAO,GAAG,4BAA4B;QAChD,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;;AAED;;;;GAIG;AACH,0BAAkC,SAAQ,YAAY;IACpD,YAAY,OAAO,GAAG,0BAA0B;QAC9C,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvB,CAAC;CACF;;AAED;;;;GAIG;AACH,yBAAiC,SAAQ,YAAY;IACnD,YAAY,OAAO,GAAG,2BAA2B;QAC/C,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvB,CAAC;CACF;;AAED;;;GAGG;AACH,yBAAiC,SAAQ,YAAY;IACnD,YAAY,OAAO,GAAG,yBAAyB;QAC7C,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvB,CAAC;CACF;;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,2BAAmC,SAAQ,YAAY;IAC5C,IAAI,CAA4B;IACzC,YACE,OAAO,GAAG,wDAAwD,EAClE,IAAI,GAA8B,mBAAmB;QAErD,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;;AAED;;;;GAIG;AACH,qBAA6B,SAAQ,YAAY;IAC/C,YAAY,OAAO,GAAG,+BAA+B;QACnD,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;;AAED;;;GAGG;AACH,2BAAmC,SAAQ,YAAY;IACrD,YAAY,OAAO,GAAG,qCAAqC;QACzD,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;;AAED;;;;;GAKG;AACH,qBAA6B,SAAQ,YAAY;IAC/C,YAAY,OAAO,GAAG,uBAAuB;QAC3C,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;;AAED;;;;;GAKG;AACH,wBAAgC,SAAQ,YAAY;IAClD,YAAY,OAAO,GAAG,8BAA8B;QAClD,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;;AAED;;;;;;;;;GASG;AACH,wBAAgC,SAAQ,YAAY;IAClD,YAAY,OAAO,GAAG,wBAAwB;QAC5C,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;;AAED;;;;;;;GAOG;AACH,6BAAqC,SAAQ,YAAY;IACvD,YAAY,OAAO,GAAG,8CAA8C;QAClE,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvB,CAAC;CACF;;AAeD;;;;;;;;;;;;;GAaG;AACH,4BAAoC,SAAQ,YAAY;IAC7C,WAAW,CAAoB;IAC/B,OAAO,CAAS;IACzB,YAAY,WAA8B,EAAE,OAAe;QACzD,KAAK,CAAC,mCAAmC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;QACzF,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/scraper/errors.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AAEH;;;;GAIG;AACH,kBAAmC,SAAQ,KAAK;IAC9B,SAAS,CAAU;IAEnC,YAAY,OAAe,EAAE,SAAkB;QAC7C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC;QAC5B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;CACF;;AAED;;;GAGG;AACH,kBAA0B,SAAQ,YAAY;IAC5C,YAAY,OAAO,GAAG,+BAA+B;QACnD,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;;AAED;;;;GAIG;AACH,uBAA+B,SAAQ,YAAY;IACjD,YAAY,OAAO,GAAG,4BAA4B;QAChD,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;;AAED;;;;GAIG;AACH,0BAAkC,SAAQ,YAAY;IACpD,YAAY,OAAO,GAAG,0BAA0B;QAC9C,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvB,CAAC;CACF;;AAED;;;;GAIG;AACH,yBAAiC,SAAQ,YAAY;IACnD,YAAY,OAAO,GAAG,2BAA2B;QAC/C,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvB,CAAC;CACF;;AAED;;;GAGG;AACH,yBAAiC,SAAQ,YAAY;IACnD,YAAY,OAAO,GAAG,yBAAyB;QAC7C,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvB,CAAC;CACF;;AAWD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,2BAAmC,SAAQ,YAAY;IAC5C,IAAI,CAA4B;IACzC,YACE,OAAO,GAAG,wDAAwD,EAClE,IAAI,GAA8B,mBAAmB;QAErD,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;;AAED;;;;GAIG;AACH,qBAA6B,SAAQ,YAAY;IAC/C,YAAY,OAAO,GAAG,+BAA+B;QACnD,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;;AAED;;;GAGG;AACH,2BAAmC,SAAQ,YAAY;IACrD,YAAY,OAAO,GAAG,qCAAqC;QACzD,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;;AAED;;;;;GAKG;AACH,qBAA6B,SAAQ,YAAY;IAC/C,YAAY,OAAO,GAAG,uBAAuB;QAC3C,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;;AAED;;;;;GAKG;AACH,wBAAgC,SAAQ,YAAY;IAClD,YAAY,OAAO,GAAG,8BAA8B;QAClD,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;;AAED;;;;;;;;;GASG;AACH,wBAAgC,SAAQ,YAAY;IAClD,YAAY,OAAO,GAAG,wBAAwB;QAC5C,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;;AAeD;;;;;;;;;;;;;GAaG;AACH,4BAAoC,SAAQ,YAAY;IAC7C,WAAW,CAAoB;IAC/B,OAAO,CAAS;IACzB,YAAY,WAA8B,EAAE,OAAe;QACzD,KAAK,CAAC,mCAAmC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;QACzF,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF"}
@@ -15,6 +15,7 @@
15
15
  import type Anthropic from "@anthropic-ai/sdk";
16
16
  import type { Action, Page, Stagehand } from "@browserbasehq/stagehand";
17
17
  import { type LlmCallInput } from "../lib/telemetry/call-capture";
18
+ import { type PhantomClickVerdict } from "../scraper/phantom-click";
18
19
  import { type Capture } from "../scripts/recon-shared";
19
20
  import type { Logger } from "../types/logging";
20
21
  /**
@@ -102,7 +103,7 @@ interface StepSnapshot {
102
103
  /** One attempt's audit trail — included verbatim in the failure dump. */
103
104
  export interface AttemptRecord {
104
105
  attempt: number;
105
- technique: "act-string" | "observe-act" | "structured-click" | "observe-act-exclude" | "llm-rephrase";
106
+ technique: "act-string" | "observe-act" | "structured-click" | "observe-act-exclude" | "deep-submit-locator" | "llm-rephrase";
106
107
  instruction: string | null;
107
108
  triedSelectors: string[];
108
109
  actResultSuccess: boolean | null;
@@ -130,6 +131,15 @@ export interface AttemptRecord {
130
131
  * - `null`: failure path.
131
132
  */
132
133
  verifiedBy: "network" | "url" | "dom" | "submitted-state-dom" | null;
134
+ /**
135
+ * {@link classifyPhantomClick}'s verdict for this attempt, computed from
136
+ * the same pre/post snapshot pair `describeAttemptEffectSignals` already
137
+ * renders — `null` until the no-observable-effect branch runs (verified
138
+ * attempts never reach it). `"phantom"` (Stagehand claimed success but
139
+ * nothing observably happened) is what escalates the next attempt to
140
+ * `deep-submit-locator` instead of repeating a light-DOM technique.
141
+ */
142
+ phantomClickVerdict: PhantomClickVerdict | null;
133
143
  }
134
144
  export declare function snapshotPage(page: Page, signalCounter: {
135
145
  n: number;
@@ -137,11 +147,11 @@ export declare function snapshotPage(page: Page, signalCounter: {
137
147
  /**
138
148
  * Detect whether a 2xx response body indicates the server REJECTED the
139
149
  * application despite returning a 2xx HTTP status. Many ATSs use a "200 OK
140
- * with rejection envelope" pattern instead of a 4xx: AppCast returns
150
+ * with rejection envelope" pattern instead of a 4xx: one JSON-envelope ATS returns
141
151
  * `{not_qualified: true, error: "Not qualified reason: <field>"}`,
142
152
  * Greenhouse uses `{rejected: true, reason: "..."}`, Lever uses
143
153
  * `{qualified: false, reason: "..."}`, Workday uses
144
- * `{status: "rejected"}`. Empirically verified on AppCast: 4/6 historical
154
+ * `{status: "rejected"}`. Empirically verified on a JSON-envelope ATS: 4/6 historical
145
155
  * /integrated_apply 200s on this codebase had `not_qualified: true` and
146
156
  * we treated them as wins because the audit only checked HTTP status.
147
157
  *
@@ -247,7 +257,7 @@ export declare function latestCaptureIndex(recentCaptures: readonly string[]): n
247
257
  * read straight from `capturesDir` — NOT from the in-memory `recentCaptures`
248
258
  * array, which is front-evicted to `RECENT_CAPTURES_WINDOW` and therefore drops
249
259
  * a step's transition when >20 captures flood during the step (measured 43
250
- * across one HCA cascade). Scanning disk by filename index is eviction-proof:
260
+ * across one wizard-ATS cascade). Scanning disk by filename index is eviction-proof:
251
261
  * the transition file is always on disk regardless of array churn. `.decoded.json`
252
262
  * sidecars are excluded (the raw file carries `requestPostData`). Sorted by index
253
263
  * so callers scan in capture order. Returns [] when the dir is unreadable.
@@ -257,7 +267,7 @@ export declare function capturesAfterIndex(preIdx: number, capturesDir: string):
257
267
  * Detects a multi-page-wizard RESTART / backward navigation by scanning the
258
268
  * captures written during this step for a configured restart-signal pattern
259
269
  * (e.g. `init-apply`, `application_canceled=true`). The restart signal is often
260
- * a plain GET (Talemetry's `GET .../init-apply?...&application_canceled=true`),
270
+ * a plain GET (the wizard ATS's `GET .../init-apply?...&application_canceled=true`),
261
271
  * so it scans the raw capture files' `url` field (GETs are written to disk even
262
272
  * though they're dropped from `recentCaptureMeta`). Window scoped by
263
273
  * `preIdx` via {@link capturesAfterIndex} (eviction-proof). Returns the matching
@@ -267,7 +277,7 @@ export declare function capturesAfterIndex(preIdx: number, capturesDir: string):
267
277
  * Does any same-window network capture's REQUEST BODY match the configured
268
278
  * transition pattern? Proves an interior "advance"/"Next" step really moved the
269
279
  * wizard forward when advance and non-advance mutations share one endpoint URL
270
- * (e.g. Talemetry `/gq`: a real advance is a `TransitionWorklet` mutation, a
280
+ * (e.g. the wizard ATS's `/gq`: a real advance is a `TransitionWorklet` mutation, a
271
281
  * field edit is `EditQuestionItem` — same URL, only the body differs, so a
272
282
  * URL/meta-based check can't tell them apart). Window scoped by `preIdx` via
273
283
  * {@link capturesAfterIndex} (disk-scan by filename index — eviction-proof, so a
@@ -283,7 +293,7 @@ export declare function windowHasTransitionBody(params: {
283
293
  * Stricter sibling of {@link windowHasTransitionBody}: a same-window capture
284
294
  * whose request body matches the transition pattern AND whose parsed
285
295
  * `variables.input.type === "next"`. The mutation NAME alone is a weak
286
- * distinguisher — on Talemetry a `back` bounce is ALSO a `TransitionWorklet`
296
+ * distinguisher — on the wizard ATS a `back` bounce is ALSO a `TransitionWorklet`
287
297
  * mutation (its body contains the pattern too) and would wrongly count as an
288
298
  * advance; and the fast `WorkletPayload` autosave that precedes the real
289
299
  * transition is a different mutation with no `input.type`. Requiring the parsed
@@ -304,7 +314,7 @@ export declare function windowHasAdvanceTransition(params: {
304
314
  * the RC2 gate is unit-testable.
305
315
  *
306
316
  * An interior "Next" on an SPA where an advance and a mere field-edit share one
307
- * endpoint (Talemetry `/gq`: TransitionWorklet vs EditQuestionItem — same URL,
317
+ * endpoint (the wizard ATS's `/gq`: TransitionWorklet vs EditQuestionItem — same URL,
308
318
  * different body) can fire a network POST that is NOT a real advance; the
309
319
  * fallback's htmlDelta/textChanged/checked-radio signals are then validation
310
320
  * re-renders / field toggles that don't move the wizard. So for an opted-in
@@ -329,7 +339,7 @@ export declare function shouldVetoFallbackAdvance(params: {
329
339
  * transition fired — a URL change OR a real `type=next` (`networkIsRealAdvance`).
330
340
  * A DOM change alone is a validation re-render / field toggle that never moves
331
341
  * the wizard. Crucially this must veto even when a NON-advancing network POST
332
- * fired (Talemetry's `WorkletPayload` autosave): keying the veto on "no network
342
+ * fired (the wizard ATS's `WorkletPayload` autosave): keying the veto on "no network
333
343
  * at all" let a rephrase that triggered an autosave + DOM reflow false-verify an
334
344
  * advance, desyncing the flow from the wizard. Returns whether the DOM signal is
335
345
  * ALLOWED to verify: false = veto it. Non-advance/field steps, sites without the
@@ -386,7 +396,7 @@ export declare function describeAttemptEffectSignals(pre: StepSnapshot, post: St
386
396
  * keeps healing opportunistically.
387
397
  */
388
398
  export declare function shouldSkipTechnique(params: {
389
- technique: "act-string" | "observe-act" | "structured-click" | "observe-act-exclude" | "llm-rephrase";
399
+ technique: "act-string" | "observe-act" | "structured-click" | "observe-act-exclude" | "deep-submit-locator" | "llm-rephrase";
390
400
  priorAttempts: readonly {
391
401
  technique: string;
392
402
  triedSelectors: readonly string[];
@@ -398,6 +408,32 @@ export declare function shouldSkipTechnique(params: {
398
408
  * effect at all. Optional so existing callers are unchanged.
399
409
  */
400
410
  advanceUnmovedAfterAttempt1?: boolean;
411
+ /**
412
+ * True when attempt 1 reported success but the pre/post snapshot shows zero
413
+ * observable effect (see {@link classifyPhantomClick}) — a phantom click.
414
+ * Re-observing/re-clicking the light DOM will no-op identically (the target
415
+ * is almost certainly unreachable via `document.querySelectorAll`, e.g.
416
+ * inside a shadow root), so skip straight to the deep submit-control locator
417
+ * instead of burning attempts 2-4 repeating the same no-op. Optional so
418
+ * existing callers are unchanged. Gated by {@link submitShapedStep} below —
419
+ * on a non-submit control the deep submit-control locator cannot resolve
420
+ * the target either, so this short-circuit only applies when the step is
421
+ * submit-shaped.
422
+ */
423
+ phantomClickAfterAttempt1?: boolean;
424
+ /**
425
+ * True when the current step is a submit-shaped action (`isFinalStep ||
426
+ * submitStep`, the canonical gate used elsewhere in this file). The deep
427
+ * submit-control locator ranks submit-shaped candidates only and
428
+ * deliberately excludes Back/Cancel/Save-draft controls, so it is a
429
+ * guaranteed no-op on a non-submit control (e.g. a radio/checkbox). Scopes
430
+ * the phantom-click short-circuit above to submit-shaped steps only —
431
+ * on a non-submit step the fallback ladder (`structured-click`,
432
+ * `observe-act-exclude`) is left intact since those are the techniques
433
+ * that can actually click a radio or checkbox. Optional so existing
434
+ * callers are unchanged.
435
+ */
436
+ submitShapedStep?: boolean;
401
437
  }): {
402
438
  skip: boolean;
403
439
  reason: string;
@@ -445,7 +481,7 @@ export declare function isSubmitRevealedInvalid(params: {
445
481
  * landed within the poll window (`networkIsRealAdvance` false), the button works
446
482
  * but the wizard is refusing to advance (a precondition isn't met, e.g. a
447
483
  * required field the flow answers on a LATER step). Re-clicking the same button
448
- * only re-fires the autosave / a `back` bounce — measured across HCA runs as the
484
+ * only re-fires the autosave / a `back` bounce — measured across wizard-ATS runs as the
449
485
  * next→back oscillation. Break to replan instead, which can reorder a later step
450
486
  * forward. Conservative: any condition unmet → run the full cascade as before.
451
487
  * Never fires on final/submit steps (they own `isSubmitRevealedInvalid`) or on
@@ -486,7 +522,7 @@ export declare function rephraseWithLLM(client: Anthropic, originalStep: string,
486
522
  * invalid form fields and visible error messages so the rephrase LLM can
487
523
  * propose corrective fills instead of
488
524
  * just "click harder" — the previous limitation observed in the
489
- * telemetry of the appcast Encompass run (every rephrase converged on
525
+ * telemetry of the JSON-envelope ATS run (every rephrase converged on
490
526
  * "Click Submit Application using JavaScript" because the prompt had no
491
527
  * signal that the form was invalid).
492
528
  */
@@ -618,8 +654,8 @@ export declare function formatValidationRejectedReason(pair: ValidationRejection
618
654
  * Default: first 8KB. That window held for tenants whose form was at the
619
655
  * top of the page (early sweeps in 2026-06).
620
656
  *
621
- * For pages where the form HTML lives below 8KB (verified on AppCast's
622
- * applyboard SPA: ng-invalid first appears at byte ~15,500 after a header
657
+ * For pages where the form HTML lives below 8KB (verified on an Angular
658
+ * apply SPA: ng-invalid first appears at byte ~15,500 after a header
623
659
  * of Angular hydration JS + chrome), the 8KB cap silently produced
624
660
  * "FORM FIELDS CURRENTLY MARKED INVALID: (none)" in the replan prompt,
625
661
  * leaving the LLM with no evidence and causing it to hallucinate steps
@@ -630,7 +666,7 @@ export declare function formatValidationRejectedReason(pair: ValidationRejection
630
666
  *
631
667
  * Site-agnostic: the markers we look for (ng-invalid, mat-form-field-
632
668
  * invalid, is-invalid, <form) are framework-level CSS-class conventions
633
- * used across countless SPAs, not AppCast-specific.
669
+ * used across countless SPAs, not specific to any one ATS.
634
670
  */
635
671
  export declare function selectBodyExcerpt(body: string): string;
636
672
  /**
@@ -674,7 +710,7 @@ export interface LeafInvalidField {
674
710
  * matches `ng-invalid` because of bubbling, but its child `<app-input
675
711
  * class="ng-invalid">` ALSO matches; `:not(:has(...))` filters out the parent.
676
712
  *
677
- * Today's Encompass-Fitchburg smoke (run 1781478440322) showed E1's prompt
713
+ * A JSON-envelope ATS tenant's smoke (run 1781478440322) showed E1's prompt
678
714
  * instruction ("prefer the leaf, not the bubbled parent") only got Haiku from
679
715
  * 5 wrong fields → 1 wrong field — still surfaced `(unlabeled) <ol>` instead
680
716
  * of `<app-input autocomplete="zip-code">` at byte 95,033. All 3 replans + 4
@@ -703,8 +739,9 @@ export declare function renderLeafInvalidFields(fields: readonly LeafInvalidFiel
703
739
  /**
704
740
  * Scan recent capture files for failed submit-endpoint requests and pull
705
741
  * out structured field-level errors from the response body. The cascade
706
- * already saves every captured request to `CAPTURES_DIR` with its parsed
707
- * `responseBody`; this helper reads those files back, filters to captures
742
+ * already saves every captured request to the run's graphql capture dir
743
+ * (see {@link resolveReconRunDir}) with its parsed `responseBody`; this
744
+ * helper reads those files back, filters to captures
708
745
  * matching the configured submit pattern with status >= 400, and walks
709
746
  * common error-shape conventions (`{ errors: [{ field, message }] }`,
710
747
  * `{ validation/fieldErrors: { … } }`, `{ message }`).
@@ -724,7 +761,7 @@ export declare function extractSubmitFailureEvidence(recentCaptureFilenames: rea
724
761
  ownBackendHostnames: readonly string[], capturesDir?: string, mode?: "strict" | "any-4xx"): string;
725
762
  /**
726
763
  * Surface Google Analytics Measurement Protocol events captured during a
727
- * step's attempt window. AppCast (and most GA4-instrumented SPAs) emit
764
+ * step's attempt window. A JSON-envelope ATS (and most GA4-instrumented SPAs) emits
728
765
  * `view_secondPage`, `view_thankYouPage`, `form_submit` and similar events
729
766
  * via `https://www.google-analytics.com/g/collect` — the engine already
730
767
  * stores these in `recentCaptures[]` but no code reads them. Without
@@ -741,8 +778,8 @@ ownBackendHostnames: readonly string[], capturesDir?: string, mode?: "strict" |
741
778
  export declare function extractGaEventEvidence(recentCaptureFilenames: readonly string[], capturesDir?: string): string;
742
779
  /**
743
780
  * Render a long step list as a small head + tail window with an elision
744
- * marker. Replan prompts grew to ~114KB on the AppCast 331-step flow
745
- * (verified Fitchburg 2026-06-14 run), causing Sonnet 4.6 to TTFT-stall
781
+ * marker. Replan prompts grew to ~114KB on a JSON-envelope ATS's 331-step flow
782
+ * (verified on a 2026-06-14 tenant run), causing Sonnet 4.6 to TTFT-stall
746
783
  * out at 187s with `APIConnectionTimeoutError: Request timed out.` —
747
784
  * the ONLY non-API-quota replan failure across ~30+ historical calls.
748
785
  *
@@ -853,7 +890,7 @@ export declare function verifyFillReadback(page: Page, xpath: string, expectedVa
853
890
  * True when a control's text/aria-label denotes a resume-upload affordance
854
891
  * (the button that surfaces a hidden `<input type=file>` or opens a chooser).
855
892
  * Pure + exported for unit tests; the vocabulary is intentionally generic so it
856
- * benefits any MUI/React/dropzone ATS, not just Talemetry. Rejects negative
893
+ * benefits any MUI/React/dropzone ATS, not just the wizard ATS. Rejects negative
857
894
  * lookalikes ("upload later", "no file", a bare "submit") so the click-to-
858
895
  * surface path never fires a skip/decline/submit control.
859
896
  */
@@ -873,7 +910,7 @@ export declare function writeFixtureToTempFile(fixture: {
873
910
  * Parse a select/dropdown flow step into the option to choose and (when
874
911
  * present) the question label that scopes which dropdown it targets.
875
912
  *
876
- * Why: HCA/Talemetry render dropdowns as `MuiNativeSelect` native `<select>`
913
+ * Why: the Angular/MUI wizard ATS renders dropdowns as `MuiNativeSelect` native `<select>`
877
914
  * with `tabindex="-1"` — removed from the accessibility tree, so Stagehand
878
915
  * observe returns `[]` and the cascade can never select an option. The select
879
916
  * primitive answers these directly from the DOM, but needs the target option
@@ -899,7 +936,7 @@ export declare function parseSelectStep(instruction: string): {
899
936
  * Why this exists (sibling of `parseSelectStep`): `parseSelectStep`
900
937
  * deliberately excludes bare radio steps ("a bare 'click the Yes answer' is a
901
938
  * radio"), leaving radios with no DOM-direct primitive — they fall to the
902
- * observe cascade, which on HCA/Talemetry's MUI radio markup resolves the step
939
+ * observe cascade, which on the wizard ATS's MUI radio markup resolves the step
903
940
  * to a wrapper `<div>`/`<span>` (not the `<input type=radio>`) and commits via
904
941
  * a bare `el.click()` that never triggers React's controlled-state `onChange`.
905
942
  * The field stays `Mui-error` "required", Next no-ops, and the wizard walls at
@@ -919,7 +956,7 @@ export declare function parseRadioStep(instruction: string): {
919
956
  } | null;
920
957
  /**
921
958
  * Run a primitive's read-only DOM enumerate with a bounded settle-retry. SPA
922
- * wizards (Talemetry) frequently render the target widget a beat AFTER the flow
959
+ * wizards frequently render the target widget a beat AFTER the flow
923
960
  * step fires — the first evaluate sees an empty page, so the primitive would
924
961
  * give up even though the widget appears moments later. Re-run the enumerate up
925
962
  * to `PRIMITIVE_ENUMERATE_ATTEMPTS` times, waiting `PRIMITIVE_ENUMERATE_RETRY_MS`
@@ -940,7 +977,7 @@ export declare function pollEnumerate<T>(page: Page, expr: string, isPresent: (r
940
977
  * Bounded poll for the real advance-transition POST to appear in this step's
941
978
  * capture window. The verifiers snapshot once after `STEP_PAUSE_MS`, but the
942
979
  * genuine `TransitionWorklet(type="next")` POST can land hundreds of ms to 2s+
943
- * AFTER that snapshot (HCA fires a fast `WorkletPayload` autosave first). A
980
+ * AFTER that snapshot (the wizard ATS fires a fast `WorkletPayload` autosave first). A
944
981
  * one-shot check false-negatives the advance, retries the click, and the stale
945
982
  * retry fires a `back` — a next→back oscillation that never leaves the page.
946
983
  * Re-check {@link windowHasAdvanceTransition} every `intervalMs` until it matches
@@ -963,7 +1000,7 @@ export declare function waitForTransitionBody(params: {
963
1000
  * Pick an option to satisfy a REQUIRED select on a catch-all step, from the
964
1001
  * select's option TEXTS (placeholder already excluded upstream). Policy: take
965
1002
  * the first non-decline option (a plausible substantive answer — the operator
966
- * accepts LLM-plausible answers reaching HCA prod); fall back to the first
1003
+ * accepts LLM-plausible answers reaching ATS prod); fall back to the first
967
1004
  * option only if every option is a decline/placeholder. Returns null when there
968
1005
  * is nothing selectable. Pure + exported so the policy is unit-testable; the LLM
969
1006
  * path ({@link judgeSelectOptionWithLLM}) is preferred when a client is present,
@@ -992,7 +1029,7 @@ export type RadioGroupCandidate = {
992
1029
  };
993
1030
  /**
994
1031
  * Build an XPath predicate that matches an `<input>` by its `id`, safe for any
995
- * id value. MUI/Talemetry radio ids are base64-ish (no double-quote), so a plain
1032
+ * id value. MUI wizard radio ids are base64-ish (no double-quote), so a plain
996
1033
  * quoted literal suffices — but if an id ever contains a `"`, fall back to
997
1034
  * `concat(...)` so the XPath stays valid. Pure + exported for unit tests.
998
1035
  */
@@ -1002,7 +1039,7 @@ export declare function buildRadioIdXPath(id: string): string;
1002
1039
  * positionally. Pure (no DOM/LLM) so it is unit-testable — the crux of the
1003
1040
  * unlabeled-radio disambiguation.
1004
1041
  *
1005
- * Why this exists: HCA/Talemetry Basic Info has multiple UNLABELED yes/no groups
1042
+ * Why this exists: the wizard ATS's Basic Info has multiple UNLABELED yes/no groups
1006
1043
  * (visa-sponsorship, common-domicile), answered by consecutive flow steps. The
1007
1044
  * old in-browser matcher treated an unlabeled group (`label===""`) as matching
1008
1045
  * ANY question (`"".includes(q)`/`q.includes("")===0`), so two unlabeled "No"
@@ -1101,6 +1138,18 @@ export interface InvalidFormControl {
1101
1138
  * action+value object; anything else becomes null).
1102
1139
  */
1103
1140
  export declare function narrowInvalidFormControl(entry: unknown): InvalidFormControl | null;
1141
+ /**
1142
+ * Single source of truth for step-line prefixes. Exists because the cascade
1143
+ * here has only ever received `stepIndex`, while the orchestrator loop in
1144
+ * recon-browser owns the plan array — so half the step lines in a run printed
1145
+ * a `N/total` denominator and half printed a bare `N`.
1146
+ *
1147
+ * Takes a getter rather than a number: a global replan splices new steps into
1148
+ * the live plan array mid-run, so the total must be read when the line is
1149
+ * emitted, not when the step started. Callers with no total omit it and get
1150
+ * the bare form.
1151
+ */
1152
+ export declare function formatStepPrefix(stepIndex: number, totalSteps?: () => number): string;
1104
1153
  /**
1105
1154
  * Cheap pre-cascade reachability gate. Runs before the 5-attempt healing cascade
1106
1155
  * (and any global replan) so a step aimed at the wrong page state fails fast
@@ -1114,6 +1163,7 @@ export declare function probeStepBeforeAttempts(params: {
1114
1163
  stagehand: Stagehand;
1115
1164
  step: string;
1116
1165
  stepIndex: number;
1166
+ totalSteps?: () => number;
1117
1167
  logger: Logger;
1118
1168
  captureFn?: CaptureFn;
1119
1169
  }): Promise<"present" | "absent">;
@@ -1137,7 +1187,7 @@ export declare function executeStepWithHealing(params: {
1137
1187
  /**
1138
1188
  * When true, treat this step as the canonical submit click for the
1139
1189
  * `submitEndpointPattern` verifier even if it is NOT the last step in
1140
- * the flow. Set from the flow file's `submitStep: true`. AppCast's flow
1190
+ * the flow. Set from the flow file's `submitStep: true`. One JSON-envelope ATS flow
1141
1191
  * has its Submit click at index 55/328 — without this flag, the pre-
1142
1192
  * submit DOM probe (gated on isFinalStep alone) never fires on the real
1143
1193
  * Submit, so unfilled required fields produce silent submit failures.
@@ -1145,6 +1195,12 @@ export declare function executeStepWithHealing(params: {
1145
1195
  */
1146
1196
  submitStep: boolean;
1147
1197
  stepIndex: number;
1198
+ /**
1199
+ * Getter, not a number: a global replan splices new steps into the live plan
1200
+ * array mid-run, so the denominator must be read at log time or every line
1201
+ * after a replan prints a stale total.
1202
+ */
1203
+ totalSteps?: () => number;
1148
1204
  phase: string;
1149
1205
  signalCounter: {
1150
1206
  n: number;
@@ -1228,6 +1284,17 @@ export declare function executeStepWithHealing(params: {
1228
1284
  * so an advance step never fires a save-and-exit / cancel / restart control.
1229
1285
  */
1230
1286
  wizardExitButtonLabels: string[];
1287
+ /**
1288
+ * Live accessor for the running count of suppressed Stagehand AISDK
1289
+ * elementId-regex errors this session (see
1290
+ * `BrowserSession.getSuppressedAisdkElementIdErrorCount`). Corroborating
1291
+ * evidence only when a phantom click is detected — logged alongside the
1292
+ * escalation, never a trigger by itself (a nonzero count alone is too weak
1293
+ * a signal across a whole run). Omitted or absent on providers that don't
1294
+ * expose it (e.g. Steel); the phantom-click detection is unaffected either
1295
+ * way since it is keyed on the pre/post snapshot delta.
1296
+ */
1297
+ getSuppressedAisdkElementIdErrorCount?: () => number;
1231
1298
  /**
1232
1299
  * Optional accumulator the cascade pushes onto when this step verifies.
1233
1300
  * Lets the main loop maintain a short cross-step trajectory of `verifiedBy`