@enricai/barnacle 1.0.0

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 (448) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +958 -0
  3. package/dist/api/errors.d.ts +88 -0
  4. package/dist/api/errors.d.ts.map +1 -0
  5. package/dist/api/errors.js +164 -0
  6. package/dist/api/errors.js.map +1 -0
  7. package/dist/api/helpers/envelope.d.ts +13 -0
  8. package/dist/api/helpers/envelope.d.ts.map +1 -0
  9. package/dist/api/helpers/envelope.js +20 -0
  10. package/dist/api/helpers/envelope.js.map +1 -0
  11. package/dist/api/helpers/reply.d.ts +9 -0
  12. package/dist/api/helpers/reply.d.ts.map +1 -0
  13. package/dist/api/helpers/reply.js +14 -0
  14. package/dist/api/helpers/reply.js.map +1 -0
  15. package/dist/api/plugins/auth.d.ts +27 -0
  16. package/dist/api/plugins/auth.d.ts.map +1 -0
  17. package/dist/api/plugins/auth.js +111 -0
  18. package/dist/api/plugins/auth.js.map +1 -0
  19. package/dist/api/plugins/error-handler.d.ts +12 -0
  20. package/dist/api/plugins/error-handler.d.ts.map +1 -0
  21. package/dist/api/plugins/error-handler.js +72 -0
  22. package/dist/api/plugins/error-handler.js.map +1 -0
  23. package/dist/api/plugins/request-context.d.ts +17 -0
  24. package/dist/api/plugins/request-context.d.ts.map +1 -0
  25. package/dist/api/plugins/request-context.js +61 -0
  26. package/dist/api/plugins/request-context.js.map +1 -0
  27. package/dist/api/routes/health.d.ts +55 -0
  28. package/dist/api/routes/health.d.ts.map +1 -0
  29. package/dist/api/routes/health.js +137 -0
  30. package/dist/api/routes/health.js.map +1 -0
  31. package/dist/api/routes/plugins-introspection.d.ts +17 -0
  32. package/dist/api/routes/plugins-introspection.d.ts.map +1 -0
  33. package/dist/api/routes/plugins-introspection.js +13 -0
  34. package/dist/api/routes/plugins-introspection.js.map +1 -0
  35. package/dist/api/schemas/common.d.ts +114 -0
  36. package/dist/api/schemas/common.d.ts.map +1 -0
  37. package/dist/api/schemas/common.js +109 -0
  38. package/dist/api/schemas/common.js.map +1 -0
  39. package/dist/api/schemas/telemetry.d.ts +66 -0
  40. package/dist/api/schemas/telemetry.d.ts.map +1 -0
  41. package/dist/api/schemas/telemetry.js +52 -0
  42. package/dist/api/schemas/telemetry.js.map +1 -0
  43. package/dist/cache/keyed-ttl-cache.d.ts +45 -0
  44. package/dist/cache/keyed-ttl-cache.d.ts.map +1 -0
  45. package/dist/cache/keyed-ttl-cache.js +69 -0
  46. package/dist/cache/keyed-ttl-cache.js.map +1 -0
  47. package/dist/cache/response-cache.d.ts +37 -0
  48. package/dist/cache/response-cache.d.ts.map +1 -0
  49. package/dist/cache/response-cache.js +114 -0
  50. package/dist/cache/response-cache.js.map +1 -0
  51. package/dist/config.d.ts +258 -0
  52. package/dist/config.d.ts.map +1 -0
  53. package/dist/config.js +138 -0
  54. package/dist/config.js.map +1 -0
  55. package/dist/lib/applicant-payload.d.ts +28 -0
  56. package/dist/lib/applicant-payload.d.ts.map +1 -0
  57. package/dist/lib/applicant-payload.js +20 -0
  58. package/dist/lib/applicant-payload.js.map +1 -0
  59. package/dist/lib/application-address.d.ts +16 -0
  60. package/dist/lib/application-address.d.ts.map +1 -0
  61. package/dist/lib/application-address.js +20 -0
  62. package/dist/lib/application-address.js.map +1 -0
  63. package/dist/lib/application-answers.d.ts +151 -0
  64. package/dist/lib/application-answers.d.ts.map +1 -0
  65. package/dist/lib/application-answers.js +95 -0
  66. package/dist/lib/application-answers.js.map +1 -0
  67. package/dist/lib/application-identity.d.ts +18 -0
  68. package/dist/lib/application-identity.d.ts.map +1 -0
  69. package/dist/lib/application-identity.js +20 -0
  70. package/dist/lib/application-identity.js.map +1 -0
  71. package/dist/lib/application-resume.d.ts +21 -0
  72. package/dist/lib/application-resume.d.ts.map +1 -0
  73. package/dist/lib/application-resume.js +23 -0
  74. package/dist/lib/application-resume.js.map +1 -0
  75. package/dist/lib/bedrock.d.ts +22 -0
  76. package/dist/lib/bedrock.d.ts.map +1 -0
  77. package/dist/lib/bedrock.js +35 -0
  78. package/dist/lib/bedrock.js.map +1 -0
  79. package/dist/lib/case-insensitive-headers.d.ts +15 -0
  80. package/dist/lib/case-insensitive-headers.d.ts.map +1 -0
  81. package/dist/lib/case-insensitive-headers.js +29 -0
  82. package/dist/lib/case-insensitive-headers.js.map +1 -0
  83. package/dist/lib/chromium-client-hints.d.ts +11 -0
  84. package/dist/lib/chromium-client-hints.d.ts.map +1 -0
  85. package/dist/lib/chromium-client-hints.js +17 -0
  86. package/dist/lib/chromium-client-hints.js.map +1 -0
  87. package/dist/lib/cookie-value.d.ts +8 -0
  88. package/dist/lib/cookie-value.d.ts.map +1 -0
  89. package/dist/lib/cookie-value.js +14 -0
  90. package/dist/lib/cookie-value.js.map +1 -0
  91. package/dist/lib/datadog.d.ts +8 -0
  92. package/dist/lib/datadog.d.ts.map +1 -0
  93. package/dist/lib/datadog.js +27 -0
  94. package/dist/lib/datadog.js.map +1 -0
  95. package/dist/lib/dd-metrics.d.ts +28 -0
  96. package/dist/lib/dd-metrics.d.ts.map +1 -0
  97. package/dist/lib/dd-metrics.js +62 -0
  98. package/dist/lib/dd-metrics.js.map +1 -0
  99. package/dist/lib/dispatch-metrics.d.ts +19 -0
  100. package/dist/lib/dispatch-metrics.d.ts.map +1 -0
  101. package/dist/lib/dispatch-metrics.js +65 -0
  102. package/dist/lib/dispatch-metrics.js.map +1 -0
  103. package/dist/lib/env.d.ts +37 -0
  104. package/dist/lib/env.d.ts.map +1 -0
  105. package/dist/lib/env.js +70 -0
  106. package/dist/lib/env.js.map +1 -0
  107. package/dist/lib/errors.d.ts +15 -0
  108. package/dist/lib/errors.d.ts.map +1 -0
  109. package/dist/lib/errors.js +20 -0
  110. package/dist/lib/errors.js.map +1 -0
  111. package/dist/lib/http.d.ts +7 -0
  112. package/dist/lib/http.d.ts.map +1 -0
  113. package/dist/lib/http.js +14 -0
  114. package/dist/lib/http.js.map +1 -0
  115. package/dist/lib/job-tracking.d.ts +17 -0
  116. package/dist/lib/job-tracking.d.ts.map +1 -0
  117. package/dist/lib/job-tracking.js +19 -0
  118. package/dist/lib/job-tracking.js.map +1 -0
  119. package/dist/lib/llm/anthropic-client.d.ts +12 -0
  120. package/dist/lib/llm/anthropic-client.d.ts.map +1 -0
  121. package/dist/lib/llm/anthropic-client.js +23 -0
  122. package/dist/lib/llm/anthropic-client.js.map +1 -0
  123. package/dist/lib/llm/judge.d.ts +57 -0
  124. package/dist/lib/llm/judge.d.ts.map +1 -0
  125. package/dist/lib/llm/judge.js +123 -0
  126. package/dist/lib/llm/judge.js.map +1 -0
  127. package/dist/lib/llm/judges/error-messages.d.ts +38 -0
  128. package/dist/lib/llm/judges/error-messages.d.ts.map +1 -0
  129. package/dist/lib/llm/judges/error-messages.js +72 -0
  130. package/dist/lib/llm/judges/error-messages.js.map +1 -0
  131. package/dist/lib/llm/judges/invalid-fields.d.ts +43 -0
  132. package/dist/lib/llm/judges/invalid-fields.d.ts.map +1 -0
  133. package/dist/lib/llm/judges/invalid-fields.js +80 -0
  134. package/dist/lib/llm/judges/invalid-fields.js.map +1 -0
  135. package/dist/lib/llm/judges/modal-priority.d.ts +41 -0
  136. package/dist/lib/llm/judges/modal-priority.d.ts.map +1 -0
  137. package/dist/lib/llm/judges/modal-priority.js +74 -0
  138. package/dist/lib/llm/judges/modal-priority.js.map +1 -0
  139. package/dist/lib/llm/judges/select-option.d.ts +49 -0
  140. package/dist/lib/llm/judges/select-option.d.ts.map +1 -0
  141. package/dist/lib/llm/judges/select-option.js +90 -0
  142. package/dist/lib/llm/judges/select-option.js.map +1 -0
  143. package/dist/lib/llm/judges/verify-submit.d.ts +68 -0
  144. package/dist/lib/llm/judges/verify-submit.d.ts.map +1 -0
  145. package/dist/lib/llm/judges/verify-submit.js +89 -0
  146. package/dist/lib/llm/judges/verify-submit.js.map +1 -0
  147. package/dist/lib/llm/schemas.d.ts +220 -0
  148. package/dist/lib/llm/schemas.d.ts.map +1 -0
  149. package/dist/lib/llm/schemas.js +239 -0
  150. package/dist/lib/llm/schemas.js.map +1 -0
  151. package/dist/lib/logging.d.ts +39 -0
  152. package/dist/lib/logging.d.ts.map +1 -0
  153. package/dist/lib/logging.js +219 -0
  154. package/dist/lib/logging.js.map +1 -0
  155. package/dist/lib/multipart.d.ts +34 -0
  156. package/dist/lib/multipart.d.ts.map +1 -0
  157. package/dist/lib/multipart.js +55 -0
  158. package/dist/lib/multipart.js.map +1 -0
  159. package/dist/lib/option-matcher.d.ts +15 -0
  160. package/dist/lib/option-matcher.d.ts.map +1 -0
  161. package/dist/lib/option-matcher.js +69 -0
  162. package/dist/lib/option-matcher.js.map +1 -0
  163. package/dist/lib/phone.d.ts +11 -0
  164. package/dist/lib/phone.d.ts.map +1 -0
  165. package/dist/lib/phone.js +17 -0
  166. package/dist/lib/phone.js.map +1 -0
  167. package/dist/lib/random.d.ts +22 -0
  168. package/dist/lib/random.d.ts.map +1 -0
  169. package/dist/lib/random.js +33 -0
  170. package/dist/lib/random.js.map +1 -0
  171. package/dist/lib/statsd.d.ts +11 -0
  172. package/dist/lib/statsd.d.ts.map +1 -0
  173. package/dist/lib/statsd.js +68 -0
  174. package/dist/lib/statsd.js.map +1 -0
  175. package/dist/lib/telemetry/call-capture.d.ts +89 -0
  176. package/dist/lib/telemetry/call-capture.d.ts.map +1 -0
  177. package/dist/lib/telemetry/call-capture.js +190 -0
  178. package/dist/lib/telemetry/call-capture.js.map +1 -0
  179. package/dist/lib/telemetry/call-types.d.ts +65 -0
  180. package/dist/lib/telemetry/call-types.d.ts.map +1 -0
  181. package/dist/lib/telemetry/call-types.js +68 -0
  182. package/dist/lib/telemetry/call-types.js.map +1 -0
  183. package/dist/lib/telemetry/run-state.d.ts +15 -0
  184. package/dist/lib/telemetry/run-state.d.ts.map +1 -0
  185. package/dist/lib/telemetry/run-state.js +23 -0
  186. package/dist/lib/telemetry/run-state.js.map +1 -0
  187. package/dist/lib/telemetry/s3-sink.d.ts +48 -0
  188. package/dist/lib/telemetry/s3-sink.d.ts.map +1 -0
  189. package/dist/lib/telemetry/s3-sink.js +229 -0
  190. package/dist/lib/telemetry/s3-sink.js.map +1 -0
  191. package/dist/lib/telemetry/submission-capture.d.ts +45 -0
  192. package/dist/lib/telemetry/submission-capture.d.ts.map +1 -0
  193. package/dist/lib/telemetry/submission-capture.js +57 -0
  194. package/dist/lib/telemetry/submission-capture.js.map +1 -0
  195. package/dist/lib/telemetry/telemetry-paths.d.ts +59 -0
  196. package/dist/lib/telemetry/telemetry-paths.d.ts.map +1 -0
  197. package/dist/lib/telemetry/telemetry-paths.js +103 -0
  198. package/dist/lib/telemetry/telemetry-paths.js.map +1 -0
  199. package/dist/lib/tracking-click.d.ts +20 -0
  200. package/dist/lib/tracking-click.d.ts.map +1 -0
  201. package/dist/lib/tracking-click.js +75 -0
  202. package/dist/lib/tracking-click.js.map +1 -0
  203. package/dist/lib/url-capture.d.ts +8 -0
  204. package/dist/lib/url-capture.d.ts.map +1 -0
  205. package/dist/lib/url-capture.js +17 -0
  206. package/dist/lib/url-capture.js.map +1 -0
  207. package/dist/lib/us-states.d.ts +20 -0
  208. package/dist/lib/us-states.d.ts.map +1 -0
  209. package/dist/lib/us-states.js +86 -0
  210. package/dist/lib/us-states.js.map +1 -0
  211. package/dist/lib/zod-multipart.d.ts +18 -0
  212. package/dist/lib/zod-multipart.d.ts.map +1 -0
  213. package/dist/lib/zod-multipart.js +35 -0
  214. package/dist/lib/zod-multipart.js.map +1 -0
  215. package/dist/plugins/config-plugin.d.ts +77 -0
  216. package/dist/plugins/config-plugin.d.ts.map +1 -0
  217. package/dist/plugins/config-plugin.js +216 -0
  218. package/dist/plugins/config-plugin.js.map +1 -0
  219. package/dist/plugins/discover.d.ts +72 -0
  220. package/dist/plugins/discover.d.ts.map +1 -0
  221. package/dist/plugins/discover.js +265 -0
  222. package/dist/plugins/discover.js.map +1 -0
  223. package/dist/plugins/json-schema-to-zod.d.ts +47 -0
  224. package/dist/plugins/json-schema-to-zod.d.ts.map +1 -0
  225. package/dist/plugins/json-schema-to-zod.js +85 -0
  226. package/dist/plugins/json-schema-to-zod.js.map +1 -0
  227. package/dist/plugins/loader.d.ts +34 -0
  228. package/dist/plugins/loader.d.ts.map +1 -0
  229. package/dist/plugins/loader.js +348 -0
  230. package/dist/plugins/loader.js.map +1 -0
  231. package/dist/plugins/plugin-api-version.d.ts +8 -0
  232. package/dist/plugins/plugin-api-version.d.ts.map +1 -0
  233. package/dist/plugins/plugin-api-version.js +11 -0
  234. package/dist/plugins/plugin-api-version.js.map +1 -0
  235. package/dist/plugins/plugin-manifest-envelope.d.ts +16 -0
  236. package/dist/plugins/plugin-manifest-envelope.d.ts.map +1 -0
  237. package/dist/plugins/plugin-manifest-envelope.js +19 -0
  238. package/dist/plugins/plugin-manifest-envelope.js.map +1 -0
  239. package/dist/scraper/behavioral-signals.d.ts +16 -0
  240. package/dist/scraper/behavioral-signals.d.ts.map +1 -0
  241. package/dist/scraper/behavioral-signals.js +30 -0
  242. package/dist/scraper/behavioral-signals.js.map +1 -0
  243. package/dist/scraper/errors.d.ts +193 -0
  244. package/dist/scraper/errors.d.ts.map +1 -0
  245. package/dist/scraper/errors.js +226 -0
  246. package/dist/scraper/errors.js.map +1 -0
  247. package/dist/scraper/fixtures.d.ts +23 -0
  248. package/dist/scraper/fixtures.d.ts.map +1 -0
  249. package/dist/scraper/fixtures.js +48 -0
  250. package/dist/scraper/fixtures.js.map +1 -0
  251. package/dist/scraper/flow-runner.d.ts +1319 -0
  252. package/dist/scraper/flow-runner.d.ts.map +1 -0
  253. package/dist/scraper/flow-runner.js +5637 -0
  254. package/dist/scraper/flow-runner.js.map +1 -0
  255. package/dist/scraper/graphql-client.d.ts +30 -0
  256. package/dist/scraper/graphql-client.d.ts.map +1 -0
  257. package/dist/scraper/graphql-client.js +14 -0
  258. package/dist/scraper/graphql-client.js.map +1 -0
  259. package/dist/scraper/http-client.d.ts +75 -0
  260. package/dist/scraper/http-client.d.ts.map +1 -0
  261. package/dist/scraper/http-client.js +189 -0
  262. package/dist/scraper/http-client.js.map +1 -0
  263. package/dist/scraper/http-status-classifier.d.ts +12 -0
  264. package/dist/scraper/http-status-classifier.d.ts.map +1 -0
  265. package/dist/scraper/http-status-classifier.js +29 -0
  266. package/dist/scraper/http-status-classifier.js.map +1 -0
  267. package/dist/scraper/metrics.d.ts +50 -0
  268. package/dist/scraper/metrics.d.ts.map +1 -0
  269. package/dist/scraper/metrics.js +85 -0
  270. package/dist/scraper/metrics.js.map +1 -0
  271. package/dist/scraper/navigate.d.ts +20 -0
  272. package/dist/scraper/navigate.d.ts.map +1 -0
  273. package/dist/scraper/navigate.js +30 -0
  274. package/dist/scraper/navigate.js.map +1 -0
  275. package/dist/scraper/oracle-sentinels.d.ts +22 -0
  276. package/dist/scraper/oracle-sentinels.d.ts.map +1 -0
  277. package/dist/scraper/oracle-sentinels.js +40 -0
  278. package/dist/scraper/oracle-sentinels.js.map +1 -0
  279. package/dist/scraper/parse-json-response.d.ts +16 -0
  280. package/dist/scraper/parse-json-response.d.ts.map +1 -0
  281. package/dist/scraper/parse-json-response.js +37 -0
  282. package/dist/scraper/parse-json-response.js.map +1 -0
  283. package/dist/scraper/pool.d.ts +37 -0
  284. package/dist/scraper/pool.d.ts.map +1 -0
  285. package/dist/scraper/pool.js +105 -0
  286. package/dist/scraper/pool.js.map +1 -0
  287. package/dist/scraper/rate-limited-json-client.d.ts +35 -0
  288. package/dist/scraper/rate-limited-json-client.d.ts.map +1 -0
  289. package/dist/scraper/rate-limited-json-client.js +29 -0
  290. package/dist/scraper/rate-limited-json-client.js.map +1 -0
  291. package/dist/scraper/raw-fetch.d.ts +49 -0
  292. package/dist/scraper/raw-fetch.d.ts.map +1 -0
  293. package/dist/scraper/raw-fetch.js +40 -0
  294. package/dist/scraper/raw-fetch.js.map +1 -0
  295. package/dist/scraper/require-response-field.d.ts +23 -0
  296. package/dist/scraper/require-response-field.d.ts.map +1 -0
  297. package/dist/scraper/require-response-field.js +42 -0
  298. package/dist/scraper/require-response-field.js.map +1 -0
  299. package/dist/scraper/retry.d.ts +33 -0
  300. package/dist/scraper/retry.d.ts.map +1 -0
  301. package/dist/scraper/retry.js +122 -0
  302. package/dist/scraper/retry.js.map +1 -0
  303. package/dist/scraper/session-browserbase.d.ts +35 -0
  304. package/dist/scraper/session-browserbase.d.ts.map +1 -0
  305. package/dist/scraper/session-browserbase.js +198 -0
  306. package/dist/scraper/session-browserbase.js.map +1 -0
  307. package/dist/scraper/session-shared.d.ts +29 -0
  308. package/dist/scraper/session-shared.d.ts.map +1 -0
  309. package/dist/scraper/session-shared.js +35 -0
  310. package/dist/scraper/session-shared.js.map +1 -0
  311. package/dist/scraper/session-steel.d.ts +31 -0
  312. package/dist/scraper/session-steel.d.ts.map +1 -0
  313. package/dist/scraper/session-steel.js +140 -0
  314. package/dist/scraper/session-steel.js.map +1 -0
  315. package/dist/scraper/session-warmup.d.ts +31 -0
  316. package/dist/scraper/session-warmup.d.ts.map +1 -0
  317. package/dist/scraper/session-warmup.js +46 -0
  318. package/dist/scraper/session-warmup.js.map +1 -0
  319. package/dist/scraper/session.d.ts +21 -0
  320. package/dist/scraper/session.d.ts.map +1 -0
  321. package/dist/scraper/session.js +28 -0
  322. package/dist/scraper/session.js.map +1 -0
  323. package/dist/scraper/stagehand-guard.d.ts +128 -0
  324. package/dist/scraper/stagehand-guard.d.ts.map +1 -0
  325. package/dist/scraper/stagehand-guard.js +337 -0
  326. package/dist/scraper/stagehand-guard.js.map +1 -0
  327. package/dist/scraper/throttle.d.ts +21 -0
  328. package/dist/scraper/throttle.d.ts.map +1 -0
  329. package/dist/scraper/throttle.js +45 -0
  330. package/dist/scraper/throttle.js.map +1 -0
  331. package/dist/scripts/hca-inbox-poll.d.ts +19 -0
  332. package/dist/scripts/hca-inbox-poll.d.ts.map +1 -0
  333. package/dist/scripts/hca-inbox-poll.js +151 -0
  334. package/dist/scripts/hca-inbox-poll.js.map +1 -0
  335. package/dist/scripts/judge-llm-batch.d.ts +98 -0
  336. package/dist/scripts/judge-llm-batch.d.ts.map +1 -0
  337. package/dist/scripts/judge-llm-batch.js +350 -0
  338. package/dist/scripts/judge-llm-batch.js.map +1 -0
  339. package/dist/scripts/llm-heal.d.ts +165 -0
  340. package/dist/scripts/llm-heal.d.ts.map +1 -0
  341. package/dist/scripts/llm-heal.js +595 -0
  342. package/dist/scripts/llm-heal.js.map +1 -0
  343. package/dist/scripts/migrate-telemetry-dir-names.d.ts +57 -0
  344. package/dist/scripts/migrate-telemetry-dir-names.d.ts.map +1 -0
  345. package/dist/scripts/migrate-telemetry-dir-names.js +156 -0
  346. package/dist/scripts/migrate-telemetry-dir-names.js.map +1 -0
  347. package/dist/scripts/probe-setinputfiles.d.ts +28 -0
  348. package/dist/scripts/probe-setinputfiles.d.ts.map +1 -0
  349. package/dist/scripts/probe-setinputfiles.js +95 -0
  350. package/dist/scripts/probe-setinputfiles.js.map +1 -0
  351. package/dist/scripts/recon-browser.d.ts +304 -0
  352. package/dist/scripts/recon-browser.d.ts.map +1 -0
  353. package/dist/scripts/recon-browser.js +1833 -0
  354. package/dist/scripts/recon-browser.js.map +1 -0
  355. package/dist/scripts/recon-generate.d.ts +182 -0
  356. package/dist/scripts/recon-generate.d.ts.map +1 -0
  357. package/dist/scripts/recon-generate.js +2572 -0
  358. package/dist/scripts/recon-generate.js.map +1 -0
  359. package/dist/scripts/recon-heal.d.ts +176 -0
  360. package/dist/scripts/recon-heal.d.ts.map +1 -0
  361. package/dist/scripts/recon-heal.js +584 -0
  362. package/dist/scripts/recon-heal.js.map +1 -0
  363. package/dist/scripts/recon-http.d.ts +19 -0
  364. package/dist/scripts/recon-http.d.ts.map +1 -0
  365. package/dist/scripts/recon-http.js +377 -0
  366. package/dist/scripts/recon-http.js.map +1 -0
  367. package/dist/scripts/recon-replay-jobs.d.ts +21 -0
  368. package/dist/scripts/recon-replay-jobs.d.ts.map +1 -0
  369. package/dist/scripts/recon-replay-jobs.js +221 -0
  370. package/dist/scripts/recon-replay-jobs.js.map +1 -0
  371. package/dist/scripts/recon-shared.d.ts +52 -0
  372. package/dist/scripts/recon-shared.d.ts.map +1 -0
  373. package/dist/scripts/recon-shared.js +68 -0
  374. package/dist/scripts/recon-shared.js.map +1 -0
  375. package/dist/scripts/recon-summarize.d.ts +15 -0
  376. package/dist/scripts/recon-summarize.d.ts.map +1 -0
  377. package/dist/scripts/recon-summarize.js +233 -0
  378. package/dist/scripts/recon-summarize.js.map +1 -0
  379. package/dist/scripts/smoke-test.d.ts +22 -0
  380. package/dist/scripts/smoke-test.d.ts.map +1 -0
  381. package/dist/scripts/smoke-test.js +236 -0
  382. package/dist/scripts/smoke-test.js.map +1 -0
  383. package/dist/server.d.ts +10 -0
  384. package/dist/server.d.ts.map +1 -0
  385. package/dist/server.js +189 -0
  386. package/dist/server.js.map +1 -0
  387. package/dist/site-plugin.d.ts +226 -0
  388. package/dist/site-plugin.d.ts.map +1 -0
  389. package/dist/site-plugin.js +13 -0
  390. package/dist/site-plugin.js.map +1 -0
  391. package/dist/testing/answers-fixture.d.ts +13 -0
  392. package/dist/testing/answers-fixture.d.ts.map +1 -0
  393. package/dist/testing/answers-fixture.js +37 -0
  394. package/dist/testing/answers-fixture.js.map +1 -0
  395. package/dist/testing/batch-email-confirmation.d.ts +58 -0
  396. package/dist/testing/batch-email-confirmation.d.ts.map +1 -0
  397. package/dist/testing/batch-email-confirmation.js +62 -0
  398. package/dist/testing/batch-email-confirmation.js.map +1 -0
  399. package/dist/testing/batch-report.d.ts +33 -0
  400. package/dist/testing/batch-report.d.ts.map +1 -0
  401. package/dist/testing/batch-report.js +30 -0
  402. package/dist/testing/batch-report.js.map +1 -0
  403. package/dist/testing/contract-parity-suite.d.ts +55 -0
  404. package/dist/testing/contract-parity-suite.d.ts.map +1 -0
  405. package/dist/testing/contract-parity-suite.js +39 -0
  406. package/dist/testing/contract-parity-suite.js.map +1 -0
  407. package/dist/testing/coverage-guard-suite.d.ts +46 -0
  408. package/dist/testing/coverage-guard-suite.d.ts.map +1 -0
  409. package/dist/testing/coverage-guard-suite.js +39 -0
  410. package/dist/testing/coverage-guard-suite.js.map +1 -0
  411. package/dist/testing/fixtures/resume.pdf +54 -0
  412. package/dist/testing/integration-runner.d.ts +62 -0
  413. package/dist/testing/integration-runner.d.ts.map +1 -0
  414. package/dist/testing/integration-runner.js +46 -0
  415. package/dist/testing/integration-runner.js.map +1 -0
  416. package/dist/testing/mock-fetch-response.d.ts +7 -0
  417. package/dist/testing/mock-fetch-response.d.ts.map +1 -0
  418. package/dist/testing/mock-fetch-response.js +16 -0
  419. package/dist/testing/mock-fetch-response.js.map +1 -0
  420. package/dist/testing/persona-fixture.d.ts +77 -0
  421. package/dist/testing/persona-fixture.d.ts.map +1 -0
  422. package/dist/testing/persona-fixture.js +83 -0
  423. package/dist/testing/persona-fixture.js.map +1 -0
  424. package/dist/testing/replay-integration-suite.d.ts +50 -0
  425. package/dist/testing/replay-integration-suite.d.ts.map +1 -0
  426. package/dist/testing/replay-integration-suite.js +45 -0
  427. package/dist/testing/replay-integration-suite.js.map +1 -0
  428. package/dist/testing/resume-fixture.d.ts +39 -0
  429. package/dist/testing/resume-fixture.d.ts.map +1 -0
  430. package/dist/testing/resume-fixture.js +38 -0
  431. package/dist/testing/resume-fixture.js.map +1 -0
  432. package/dist/testmail/client.d.ts +61 -0
  433. package/dist/testmail/client.d.ts.map +1 -0
  434. package/dist/testmail/client.js +190 -0
  435. package/dist/testmail/client.js.map +1 -0
  436. package/dist/testmail/errors.d.ts +18 -0
  437. package/dist/testmail/errors.d.ts.map +1 -0
  438. package/dist/testmail/errors.js +29 -0
  439. package/dist/testmail/errors.js.map +1 -0
  440. package/dist/types/dispatch-metrics.d.ts +46 -0
  441. package/dist/types/dispatch-metrics.d.ts.map +1 -0
  442. package/dist/types/dispatch-metrics.js +25 -0
  443. package/dist/types/dispatch-metrics.js.map +1 -0
  444. package/dist/types/logging.d.ts +11 -0
  445. package/dist/types/logging.d.ts.map +1 -0
  446. package/dist/types/logging.js +3 -0
  447. package/dist/types/logging.js.map +1 -0
  448. package/package.json +283 -0
@@ -0,0 +1,1319 @@
1
+ /**
2
+ * Self-heal step-execution engine for Phase 1 recon. Extracted verbatim from
3
+ * `recon-browser.ts` so the step runner, its five DOM primitives (upload,
4
+ * select, checkbox, radio, required-selects), the pure verify/signal helpers,
5
+ * and the in-memory network-capture wiring can be reused and unit-tested apart
6
+ * from the recon CLI entry-point. This module is a leaf: it never imports the
7
+ * recon CLI module, so the CLI can depend on it without a cycle.
8
+ *
9
+ * Two seams let the CLI inject its disk-facing behavior without this module
10
+ * knowing about the filesystem layout: {@link wireSignalCapture} takes an
11
+ * `onCapture` callback for persisting each capture, and
12
+ * {@link executeStepWithHealing} takes an `onStepFailure` callback for writing
13
+ * the terminal failure dump.
14
+ */
15
+ import type Anthropic from "@anthropic-ai/sdk";
16
+ import type { Action, Page, Stagehand } from "@browserbasehq/stagehand";
17
+ import { type LlmCallInput } from "../lib/telemetry/call-capture";
18
+ import { type Capture } from "../scripts/recon-shared";
19
+ import type { Logger } from "../types/logging";
20
+ /**
21
+ * Wires CDP Network event listeners onto the page's main session and returns
22
+ * a cleanup function. Stagehand V3 already enables the Network domain
23
+ * internally, so we only need to attach our own listeners.
24
+ *
25
+ * Uses `requestId` to correlate requestWillBeSent/responseReceived/loadingFinished
26
+ * so we can fetch the response body only after it's fully received.
27
+ *
28
+ * Owns only the in-memory half of capture handling: builds each {@link Capture},
29
+ * records it in the rolling `recentCaptures`/`recentCaptureMeta` windows, and
30
+ * emits the analytics early-warnings. Persistence is delegated to the optional
31
+ * `onCapture` callback so the recon CLI can own the disk layout without this
32
+ * leaf module depending on it.
33
+ */
34
+ export declare function wireSignalCapture(page: Page, params: {
35
+ counter: {
36
+ n: number;
37
+ };
38
+ signalCounter: {
39
+ n: number;
40
+ };
41
+ recentCaptures: string[];
42
+ recentCaptureMeta: {
43
+ method: string;
44
+ status: number;
45
+ url: string;
46
+ }[];
47
+ getCurrentPhase: () => string;
48
+ getCurrentPageOrigin: () => string;
49
+ onCapture?: (capture: Capture, filename: string) => void;
50
+ }): () => void;
51
+ /** Navigation timeout for page.goto — raise for slow tunnels or proxied targets. */
52
+ export declare const GOTO_TIMEOUT_MS = 120000;
53
+ /**
54
+ * Per-step watchdog passed as Stagehand's native `timeout` on every
55
+ * `act()`/`observe()`. Stagehand's `ensureTimeRemaining()` checks the
56
+ * deadline between work units and throws `ActTimeoutError` cleanly on
57
+ * expiry — the healing cascade catches it as just another attempt failure.
58
+ * Matches `GOTO_TIMEOUT_MS` precedent so a hung Stagehand call can't pin
59
+ * the recon for hours.
60
+ */
61
+ export declare const STEP_WATCHDOG_MS = 120000;
62
+ /**
63
+ * How many steps from the end of the flow are considered "trailing" for the
64
+ * Tier 1 grace path. A verification failure on an optional step within this
65
+ * window is treated as a benign no-op exit when a recent non-GET capture also
66
+ * returned 2xx — the flow's real work landed and the trailing tail is
67
+ * redundant. Two covers the common pattern: an upload step followed by a
68
+ * final Continue/Submit click.
69
+ */
70
+ export declare const TRAILING_GRACE_WINDOW = 2;
71
+ /** Cheap snapshot of side effects we use to decide whether a step "worked". */
72
+ interface StepSnapshot {
73
+ networkCount: number;
74
+ url: string;
75
+ /**
76
+ * `document.body.outerHTML.length`. Measurement-only — pre/post delta
77
+ * exposes whether a click triggered a client-side state change that
78
+ * doesn't show up in network or URL (e.g. React view swap inside an SPA).
79
+ * Not consumed by the verifier yet; gathered for threshold tuning.
80
+ */
81
+ bodyHtmlLength: number;
82
+ /**
83
+ * `document.body.innerText.length + ":" + first 200 chars`. A cheap,
84
+ * deterministic proxy for "did the visible text change" that filters
85
+ * React-internal attribute churn (which moves `bodyHtmlLength` without
86
+ * moving anything the user perceives). Measurement-only.
87
+ */
88
+ visibleTextSignature: string;
89
+ }
90
+ /** One attempt's audit trail — included verbatim in the failure dump. */
91
+ export interface AttemptRecord {
92
+ attempt: number;
93
+ technique: "act-string" | "observe-act" | "structured-click" | "observe-act-exclude" | "llm-rephrase";
94
+ instruction: string | null;
95
+ triedSelectors: string[];
96
+ actResultSuccess: boolean | null;
97
+ actResultDescription: string | null;
98
+ errorMessage: string | null;
99
+ pre: StepSnapshot;
100
+ post: StepSnapshot;
101
+ /**
102
+ * Stagehand-resolved action method (`fill`, `click`, etc.) that the verifier
103
+ * used to decide signal type. Null when no action was resolved (observe
104
+ * returned no candidates) or when Stagehand returned an empty actions[].
105
+ */
106
+ resolvedMethod: string | null;
107
+ /** Args Stagehand passed to the resolved action — what we expect to see post-fill. */
108
+ resolvedArguments: string[] | null;
109
+ /**
110
+ * Which verification signal carried the attempt:
111
+ * - `network`: same-origin POST counter advanced in the attempt window.
112
+ * - `url`: page navigated to a new URL.
113
+ * - `dom`: verifyDomEffect confirmed a structural change (radio/checkbox state).
114
+ * - `submitted-state-dom`: final-step DOM fallback fired — a flow-declared
115
+ * `submittedStateSelectors` entry matched live DOM, indicating the SPA
116
+ * reached its submitted state even though the network capture missed
117
+ * the submit POST within the attempt window.
118
+ * - `null`: failure path.
119
+ */
120
+ verifiedBy: "network" | "url" | "dom" | "submitted-state-dom" | null;
121
+ }
122
+ export declare function snapshotPage(page: Page, signalCounter: {
123
+ n: number;
124
+ }): Promise<StepSnapshot>;
125
+ /**
126
+ * Detect whether a 2xx response body indicates the server REJECTED the
127
+ * application despite returning a 2xx HTTP status. Many ATSs use a "200 OK
128
+ * with rejection envelope" pattern instead of a 4xx: AppCast returns
129
+ * `{not_qualified: true, error: "Not qualified reason: <field>"}`,
130
+ * Greenhouse uses `{rejected: true, reason: "..."}`, Lever uses
131
+ * `{qualified: false, reason: "..."}`, Workday uses
132
+ * `{status: "rejected"}`. Empirically verified on AppCast: 4/6 historical
133
+ * /integrated_apply 200s on this codebase had `not_qualified: true` and
134
+ * we treated them as wins because the audit only checked HTTP status.
135
+ *
136
+ * Site-agnostic: the helper checks for the union of common rejection
137
+ * envelope shapes. New ATSs can be added by extending the recognized
138
+ * keys; the existing ones cover the four most common patterns.
139
+ */
140
+ /**
141
+ * Detect whether the supplied capture-meta window contains a backend
142
+ * 5xx response that matches the configured submit endpoint pattern.
143
+ * The cascade can't heal a backend crash by retrying clicks or
144
+ * rephrasing instructions; surfacing this signal lets the caller
145
+ * fail-fast instead of burning replan budget on an unrecoverable
146
+ * server state.
147
+ *
148
+ * Conservative: requires both `submitEndpointPattern` to match (5xx
149
+ * from analytics/tracking URLs is noise, not a real backend failure)
150
+ * AND a 5xx status. Returns the matched URL when found; null otherwise.
151
+ */
152
+ export declare function findRecentBackendError(params: {
153
+ recentCaptureMeta: readonly {
154
+ method: string;
155
+ status: number;
156
+ url: string;
157
+ }[];
158
+ preMetaLength: number;
159
+ /**
160
+ * Hostnames considered "the site's own backend." Replaces the prior
161
+ * `submitEndpointPattern` regex — we no longer pattern-match URL paths,
162
+ * just check hostname against the site-supplied whitelist (deterministic
163
+ * string equality on a URL component, not a fuzzy regex). A 5xx from any
164
+ * URL whose hostname is on this list is treated as a backend error;
165
+ * 5xx from third-party hosts (analytics, CDNs) is ignored. Empty array
166
+ * disables the check entirely.
167
+ */
168
+ ownBackendHostnames: readonly string[];
169
+ }): string | null;
170
+ /**
171
+ * Detect whether the page legitimately transitioned within the supplied
172
+ * capture-meta window — a 3xx redirect or a same-origin non-GET capture
173
+ * that returned 2xx and looks like a flow-progression URL (not a tracking
174
+ * beacon). `preMetaLength` is the caller's chosen window start (typically
175
+ * captured at step entry, so the scan covers everything that landed since
176
+ * this step began processing). When a transition is detected, a probe-
177
+ * absent escalation to replan is unnecessary noise — the form auto-
178
+ * advanced and the next probe naturally sees zero candidates for the OLD
179
+ * step.
180
+ *
181
+ * Conservative: returns null when the signal is ambiguous so the cascade
182
+ * keeps its existing replan path. Returns the matched URL string when a
183
+ * transition is detected, which the caller logs as the reason for the
184
+ * clean skip.
185
+ */
186
+ export declare function findRecentPageTransition(params: {
187
+ recentCaptureMeta: readonly {
188
+ method: string;
189
+ status: number;
190
+ url: string;
191
+ }[];
192
+ preMetaLength: number;
193
+ }): string | null;
194
+ /**
195
+ * True when a resolved action's description names a wizard-exit control (a
196
+ * save-and-exit / cancel / restart button) rather than a forward action.
197
+ * Matched case-insensitively as a substring against the built-in list plus any
198
+ * site-supplied `extraLabels`. Bare "cancel"/"back" are intentionally NOT in the
199
+ * default list (too many false positives, e.g. "cancel changes to this field",
200
+ * "background"); a site can add them via `wizardExitButtonLabels` if its wizard
201
+ * uses them destructively.
202
+ */
203
+ export declare function isWizardExitAction(description: string | null | undefined, extraLabels?: readonly string[]): boolean;
204
+ /**
205
+ * Is this flow step an advance/"Next" click (move to the next wizard page),
206
+ * rather than a field-answer? Used to veto a DOM-only "success" (a field toggle)
207
+ * from counting as an advance: toggling a control never moves the wizard, so an
208
+ * advance step must be verified by a real transition (network/URL), not a DOM
209
+ * state change. Keyed on the ORIGINAL step instruction so a rephrase that
210
+ * resolves "Next" to a radio click can't launder a field toggle into a passed
211
+ * advance. Pure; unit-testable seam mirroring `isWizardExitAction`.
212
+ */
213
+ export declare function isAdvanceStep(instruction: string | null | undefined): boolean;
214
+ /**
215
+ * Parse the monotonic capture index from a capture filename. Every capture is
216
+ * written as `<idx>-<phase>-<unix-ms>-<hash>.json` where `<idx>` is a
217
+ * zero-padded, never-reused counter (`counter.n++`). Returns the numeric prefix,
218
+ * or null when the name has no leading integer. Pure; the seam the window
219
+ * helpers use to scope a step's captures by INDEX rather than by array position.
220
+ */
221
+ export declare function parseCaptureIndex(filename: string): number | null;
222
+ /**
223
+ * The highest capture index currently known, read from the LIVE `recentCaptures`
224
+ * array's last entry (captures are appended in index order, so the tail always
225
+ * carries the high-water mark even after the array is front-evicted to
226
+ * `RECENT_CAPTURES_WINDOW`). Snapshotting THIS before a step, then asking the
227
+ * window helpers for captures with a greater index, scopes the scan to the
228
+ * step's own captures WITHOUT depending on the capped array holding them — the
229
+ * eviction-proof replacement for the old `recentCaptures.length` slice index.
230
+ * Returns -1 when nothing has been captured yet (so index 0 is in-window).
231
+ */
232
+ export declare function latestCaptureIndex(recentCaptures: readonly string[]): number;
233
+ /**
234
+ * Filenames of the raw captures written AFTER `preIdx` (this step's window),
235
+ * read straight from `capturesDir` — NOT from the in-memory `recentCaptures`
236
+ * array, which is front-evicted to `RECENT_CAPTURES_WINDOW` and therefore drops
237
+ * a step's transition when >20 captures flood during the step (measured 43
238
+ * across one HCA cascade). Scanning disk by filename index is eviction-proof:
239
+ * the transition file is always on disk regardless of array churn. `.decoded.json`
240
+ * sidecars are excluded (the raw file carries `requestPostData`). Sorted by index
241
+ * so callers scan in capture order. Returns [] when the dir is unreadable.
242
+ */
243
+ export declare function capturesAfterIndex(preIdx: number, capturesDir: string): string[];
244
+ /**
245
+ * Detects a multi-page-wizard RESTART / backward navigation by scanning the
246
+ * captures written during this step for a configured restart-signal pattern
247
+ * (e.g. `init-apply`, `application_canceled=true`). The restart signal is often
248
+ * a plain GET (Talemetry's `GET .../init-apply?...&application_canceled=true`),
249
+ * so it scans the raw capture files' `url` field (GETs are written to disk even
250
+ * though they're dropped from `recentCaptureMeta`). Window scoped by
251
+ * `preIdx` via {@link capturesAfterIndex} (eviction-proof). Returns the matching
252
+ * URL (for the diagnostic) or null. No-op when no `restartSignalUrlPatterns`.
253
+ */
254
+ /**
255
+ * Does any same-window network capture's REQUEST BODY match the configured
256
+ * transition pattern? Proves an interior "advance"/"Next" step really moved the
257
+ * wizard forward when advance and non-advance mutations share one endpoint URL
258
+ * (e.g. Talemetry `/gq`: a real advance is a `TransitionWorklet` mutation, a
259
+ * field edit is `EditQuestionItem` — same URL, only the body differs, so a
260
+ * URL/meta-based check can't tell them apart). Window scoped by `preIdx` via
261
+ * {@link capturesAfterIndex} (disk-scan by filename index — eviction-proof, so a
262
+ * transition isn't lost when >20 captures flood during the step). Returns false
263
+ * when no pattern is configured (opt-in) or nothing in the window matches.
264
+ */
265
+ export declare function windowHasTransitionBody(params: {
266
+ preIdx: number;
267
+ advanceTransitionBodyPattern: string | null;
268
+ capturesDir?: string;
269
+ }): boolean;
270
+ /**
271
+ * Stricter sibling of {@link windowHasTransitionBody}: a same-window capture
272
+ * whose request body matches the transition pattern AND whose parsed
273
+ * `variables.input.type === "next"`. The mutation NAME alone is a weak
274
+ * distinguisher — on Talemetry a `back` bounce is ALSO a `TransitionWorklet`
275
+ * mutation (its body contains the pattern too) and would wrongly count as an
276
+ * advance; and the fast `WorkletPayload` autosave that precedes the real
277
+ * transition is a different mutation with no `input.type`. Requiring the parsed
278
+ * `type==="next"` isolates a genuine FORWARD advance. Deliberately does NOT use
279
+ * `input.is_next` — it is inverted/unreliable on this ATS (a real `next` carries
280
+ * `is_next:false`, a `back` carries `is_next:true`). Window scoped by `preIdx`
281
+ * via {@link capturesAfterIndex} (eviction-proof disk-scan). Opt-in /
282
+ * site-agnostic: returns false when no pattern is configured.
283
+ */
284
+ export declare function windowHasAdvanceTransition(params: {
285
+ preIdx: number;
286
+ advanceTransitionBodyPattern: string | null;
287
+ capturesDir?: string;
288
+ }): boolean;
289
+ /**
290
+ * Decide whether the n+16 `el.click()` fallback's "advance" signals should be
291
+ * VETOED — i.e. NOT counted as verifying a wizard transition. Pure + exported so
292
+ * the RC2 gate is unit-testable.
293
+ *
294
+ * An interior "Next" on an SPA where an advance and a mere field-edit share one
295
+ * endpoint (Talemetry `/gq`: TransitionWorklet vs EditQuestionItem — same URL,
296
+ * different body) can fire a network POST that is NOT a real advance; the
297
+ * fallback's htmlDelta/textChanged/checked-radio signals are then validation
298
+ * re-renders / field toggles that don't move the wizard. So for an opted-in
299
+ * (`hasPattern`) non-final/non-submit ADVANCE step, a real advance requires a URL
300
+ * change OR a same-window capture body matching the transition pattern
301
+ * (`retryNetworkIsRealAdvance`); anything less is vetoed. Non-advance/field-answer
302
+ * steps and sites without the pattern are never vetoed here.
303
+ */
304
+ export declare function shouldVetoFallbackAdvance(params: {
305
+ hasPattern: boolean;
306
+ isFinalOrSubmit: boolean;
307
+ isAdvance: boolean;
308
+ retryUrlChanged: boolean;
309
+ retryNetworkIsRealAdvance: boolean;
310
+ }): boolean;
311
+ /**
312
+ * Whether a DOM-verified signal should count as verifying an interior ADVANCE
313
+ * step (the PRIMARY verifier's counterpart to {@link shouldVetoFallbackAdvance}).
314
+ * Pure + exported so the RC2-on-the-DOM-branch gate is unit-testable.
315
+ *
316
+ * An advance/"Next" on a pattern-configured SPA is only real when a genuine
317
+ * transition fired — a URL change OR a real `type=next` (`networkIsRealAdvance`).
318
+ * A DOM change alone is a validation re-render / field toggle that never moves
319
+ * the wizard. Crucially this must veto even when a NON-advancing network POST
320
+ * fired (Talemetry's `WorkletPayload` autosave): keying the veto on "no network
321
+ * at all" let a rephrase that triggered an autosave + DOM reflow false-verify an
322
+ * advance, desyncing the flow from the wizard. Returns whether the DOM signal is
323
+ * ALLOWED to verify: false = veto it. Non-advance/field steps, sites without the
324
+ * pattern, and final/submit steps are never vetoed here (DOM stands).
325
+ */
326
+ export declare function isDomOnlyAdvanceVerified(params: {
327
+ hasPattern: boolean;
328
+ isFinalOrSubmit: boolean;
329
+ isAdvance: boolean;
330
+ domVerified: boolean;
331
+ networkIsRealAdvance: boolean;
332
+ urlChanged: boolean;
333
+ }): boolean;
334
+ /**
335
+ * Read-only count of ng-invalid form controls on the page. Side-effect-free
336
+ * counterpart to `probeFormValidityBeforeSubmit` (which also auto-fills
337
+ * unselected radio groups via element.click()). Used by the cascade's
338
+ * early-exit predicate to detect "the Submit click revealed new required
339
+ * questions" — when this count grows from 0 (pre-submit) to ≥1 (post-attempt-1),
340
+ * attempts 2-5 cannot succeed and the cascade should route to replan
341
+ * immediately instead of burning Stagehand calls.
342
+ */
343
+ export declare function countNgInvalidContainers(page: Page): Promise<number>;
344
+ /**
345
+ * Translate the pre/post snapshot delta + same-window captures into a short
346
+ * diagnostic phrase that goes into `failureReasons[]`. Surfaces patterns the
347
+ * verifier itself discards: e.g. "DOM grew but no submit-shaped network
348
+ * request" (client-side validation blocked the form), or "analytics beacon
349
+ * fired but no same-origin submit" (third-party tracking, not real signal).
350
+ * The rephrase + replan LLMs read these strings to choose between "retry the
351
+ * click" and "fill an unanswered required field".
352
+ */
353
+ export declare function describeAttemptEffectSignals(pre: StepSnapshot, post: StepSnapshot, recentCaptureMeta: readonly {
354
+ method: string;
355
+ status: number;
356
+ url: string;
357
+ }[], preMetaLength: number): string;
358
+ /**
359
+ * Was the failed step *structurally* unresolvable — i.e. no cascade attempt ever
360
+ * resolved a selector/control for it — as opposed to a control that WAS found but
361
+ * failed to verify? True only when every attempt resolved nothing
362
+ * (`triedSelectors` empty) and none carried a verification signal. The replan
363
+ * prompt uses this to stop merely rewording a step whose target the engine has no
364
+ * driver for (e.g. a custom widget), and instead propose a different path or mark
365
+ * impossible. Conservative: any attempt that resolved a selector or verified makes
366
+ * this false (that step is resolvable; rewording may still help).
367
+ */
368
+ /**
369
+ * Decide whether a cascade technique's preconditions cannot be met by the
370
+ * prior attempts' state, so running it would burn the attempt slot without
371
+ * exercising new behaviour. Conservative: returns true ONLY when the
372
+ * predicate can prove the technique is mathematically unable to succeed;
373
+ * anything ambiguous falls through to "run the attempt" so the cascade
374
+ * keeps healing opportunistically.
375
+ */
376
+ export declare function shouldSkipTechnique(params: {
377
+ technique: "act-string" | "observe-act" | "structured-click" | "observe-act-exclude" | "llm-rephrase";
378
+ priorAttempts: readonly {
379
+ technique: string;
380
+ triedSelectors: readonly string[];
381
+ errorMessage: string | null;
382
+ }[];
383
+ /**
384
+ * True when the step is a wizard ADVANCE ("Next") whose attempt-1 produced no
385
+ * real forward transition — either a non-advancing POST or no observable
386
+ * effect at all. Optional so existing callers are unchanged.
387
+ */
388
+ advanceUnmovedAfterAttempt1?: boolean;
389
+ }): {
390
+ skip: boolean;
391
+ reason: string;
392
+ };
393
+ /**
394
+ * Bucket the recent `recon-replan` LLM calls by `failureKind` and produce a
395
+ * single diagnostic phrase the runner can surface when the cascade aborts
396
+ * its replan budget. Lets the operator distinguish transient
397
+ * (anthropic-rate-limit) from permanent (schema-validation-failed,
398
+ * response-empty) failure modes without having to grep calls.ndjson.
399
+ *
400
+ * Reads the most recent N entries from the configured calls path, filters
401
+ * to the matching callType, and tallies their failureKind. Empty string
402
+ * when no relevant failures are present.
403
+ */
404
+ /**
405
+ * Decide whether attempt 1 on a final-Submit click revealed new required
406
+ * questions that mathematically can't be cleared by retrying the same
407
+ * click. When true, the cascade should break out of its attempt loop and
408
+ * route directly to global replan, which already reads the failure dump's
409
+ * ng-invalid + interactive-target lists and produces follow-up steps.
410
+ *
411
+ * Strict 5-condition predicate: the click must be on the final flow step
412
+ * with a configured submit endpoint, the resolved action must be a click,
413
+ * the pre/post snapshot delta must produce the dom-grew-without-network
414
+ * signal, AND the ng-invalid container count must have grown. When any
415
+ * condition fails, the full cascade runs as usual — keeping the predicate
416
+ * from false-positiving on ordinary state changes (network-fired
417
+ * navigations, partial DOM reflows, etc.).
418
+ */
419
+ export declare function isSubmitRevealedInvalid(params: {
420
+ isFinalStep: boolean;
421
+ requireSubmitEndpoint: boolean;
422
+ resolvedMethod: string | null;
423
+ effectSignals: string;
424
+ preSubmitInvalidCount: number;
425
+ postAttemptInvalidCount: number;
426
+ }): boolean;
427
+ /**
428
+ * Decide whether attempt 1 on an interior wizard-ADVANCE step already proved
429
+ * that clicking cannot move the wizard, so attempts 2-N are dead work. When the
430
+ * step is an advance step on a site with the transition-body gate configured,
431
+ * the click DID fire (Stagehand resolved and clicked a button — `clickFired`)
432
+ * and network traffic happened, yet no real `TransitionWorklet(type="next")`
433
+ * landed within the poll window (`networkIsRealAdvance` false), the button works
434
+ * but the wizard is refusing to advance (a precondition isn't met, e.g. a
435
+ * required field the flow answers on a LATER step). Re-clicking the same button
436
+ * only re-fires the autosave / a `back` bounce — measured across HCA runs as the
437
+ * next→back oscillation. Break to replan instead, which can reorder a later step
438
+ * forward. Conservative: any condition unmet → run the full cascade as before.
439
+ * Never fires on final/submit steps (they own `isSubmitRevealedInvalid`) or on
440
+ * sites without the pattern.
441
+ */
442
+ export declare function isAdvanceStalled(params: {
443
+ isAdvance: boolean;
444
+ isFinalOrSubmit: boolean;
445
+ hasPattern: boolean;
446
+ clickFired: boolean;
447
+ networkFired: boolean;
448
+ networkIsRealAdvance: boolean;
449
+ urlChanged: boolean;
450
+ }): boolean;
451
+ /**
452
+ * Lazy Anthropic client for attempt 4's rephrase. Returns null when the
453
+ * deployment is Bedrock-only (no ANTHROPIC_API_KEY) — attempt 4 then becomes
454
+ * a no-op and the executor escalates straight to the failure dump. The other
455
+ * three attempts already cover the lion's share of recovery.
456
+ */
457
+ /**
458
+ * Strip the `anthropic/` prefix Stagehand expects on `STAGEHAND_MODEL` so the
459
+ * Anthropic SDK sees the bare model id (e.g. `claude-sonnet-4-6`).
460
+ */
461
+ export declare function anthropicModelName(): string;
462
+ /** Injectable capture function — matches `captureLlmCall`'s signature. */
463
+ export type CaptureFn = (input: LlmCallInput) => Promise<void>;
464
+ /**
465
+ * Attempt-4 of the step-healing cascade: when three mechanical retry variations
466
+ * all fail, this is the last resort before the step is declared terminal. Exported
467
+ * so tests can inject a fake capture sink without touching the browser session.
468
+ */
469
+ export declare function rephraseWithLLM(client: Anthropic, originalStep: string, triedSelectors: string[], observeCandidates: Action[], failureReasons: string[], captureFn?: CaptureFn,
470
+ /**
471
+ * Optional live-page form evidence extracted by the caller (typically by
472
+ * fetching document.body.outerHTML and running the Haiku invalid-fields
473
+ * and error-messages judges on it). When non-empty, surfaces structurally
474
+ * invalid form fields and visible error messages so the rephrase LLM can
475
+ * propose corrective fills instead of
476
+ * just "click harder" — the previous limitation observed in the
477
+ * telemetry of the appcast Encompass run (every rephrase converged on
478
+ * "Click Submit Application using JavaScript" because the prompt had no
479
+ * signal that the form was invalid).
480
+ */
481
+ pageEvidence?: {
482
+ invalidFieldList: string;
483
+ errorTextList: string;
484
+ interactiveTargetsList: string;
485
+ },
486
+ /**
487
+ * Optional unfocused observe list. The default `observeCandidates` is
488
+ * Stagehand's observe filtered by the FAILED STEP'S INSTRUCTION — when
489
+ * that step is "click submit," candidates collapse to "Submit button"
490
+ * and any modals/dialogs blocking submit stay invisible to the LLM.
491
+ * Pass an unfocused observe (stagehand.observe() with no instruction)
492
+ * here so the rephrase prompt can see ambient UI like modal Save/Close
493
+ * buttons that the failed step's instruction filter hid. Modal entries
494
+ * are prioritized to the top by `renderUnfocusedObserve`.
495
+ */
496
+ unfocusedObserve?: Action[],
497
+ /**
498
+ * Optional list of structured server-side validation errors harvested
499
+ * from any failed submit-endpoint captures in the recent window. Lets
500
+ * the rephrase LLM see "the form did POST and got back '{field: email,
501
+ * message: Invalid format}' from the server" — a signal the verifier
502
+ * alone hides behind "no observable effect".
503
+ */
504
+ submitFailureList?: string,
505
+ /**
506
+ * Optional verbatim instructions sent by prior cascade attempts. The
507
+ * SELECTORS ALREADY TRIED section tells the LLM which xpaths failed,
508
+ * but not which natural-language strategies it (or earlier observe
509
+ * branches) already proposed. Surfacing the prior instruction text
510
+ * prevents the rephrase LLM from cycling on near-synonyms of a
511
+ * wording the cascade has already proven ineffective.
512
+ */
513
+ priorAttempts?: readonly {
514
+ technique: string;
515
+ instruction: string | null;
516
+ verdict: string | null;
517
+ }[],
518
+ /**
519
+ * Optional rendered Google Analytics Measurement Protocol event list
520
+ * (output of `extractGaEventEvidence`). When non-empty, surfaces SPA-
521
+ * internal page transitions (view_secondPage), success signals
522
+ * (view_thankYouPage), and the site's own validator counts
523
+ * (epn.validationErrorsCount) so the rephrase LLM can pivot off
524
+ * "click harder" into "fill the actually-missing field." Telemetry
525
+ * the engine already captures but never showed the LLM until now.
526
+ */
527
+ gaEventEvidence?: string): Promise<string | null>;
528
+ /**
529
+ * Getter for the module-private billing-exhausted flag. Exported so the
530
+ * cascade's attempt-5 guard can read it without coupling to module-level
531
+ * state directly; also lets tests reset / observe the flag without
532
+ * mutating shared globals.
533
+ */
534
+ export declare function hasBillingErrorBeenLogged(): boolean;
535
+ /**
536
+ * Test-only reset. Tests that exercise the billing-exhausted path need to
537
+ * clear the per-process flag between cases; production code never resets it.
538
+ */
539
+ export declare function resetBillingErrorFlagForTests(): void;
540
+ export declare function logBillingErrorIfPresent(err: unknown): boolean;
541
+ /**
542
+ * Render an unfocused-observe array into a numbered string the prompt can
543
+ * consume, prioritizing any modal/dialog/overlay/popup entries to the top
544
+ * regardless of their index in the raw list. Without this prefix, modals
545
+ * that Stagehand observes at index 70+ (verified against the prior run's
546
+ * dump — 11 modal entries lived at positions 64-79 of 80) get truncated
547
+ * away by the cap and the LLM-replan can't propose to save/close them.
548
+ */
549
+ export declare function renderUnfocusedObserve(observations: Action[], options?: {
550
+ cap?: number;
551
+ client?: Anthropic | null;
552
+ captureFn?: CaptureFn;
553
+ }): Promise<string>;
554
+ /**
555
+ * Live-page sibling to `readFailureDumpEvidence` — fetches the current
556
+ * page body and runs the same framework-agnostic class scans. Used by the
557
+ * cascade's per-attempt rephrase path, which fires BEFORE the failure dump
558
+ * is written (the dump only happens after the whole cascade exhausts) and
559
+ * therefore can't read from disk.
560
+ *
561
+ * Returns empty strings on any failure (page.evaluate threw, body missing,
562
+ * etc.) — evidence is advisory, never load-bearing.
563
+ */
564
+ /**
565
+ * One paired "we filled this field and the validator visibly rejected it"
566
+ * tuple. Surfaced to the LLM replan prompt as a structured failureReason
567
+ * line so it can pivot the value or return outcome=impossible instead of
568
+ * burning replan budget on the same proposal.
569
+ */
570
+ export interface ValidationRejectionPair {
571
+ fieldLabel: string;
572
+ errorText: string;
573
+ }
574
+ /**
575
+ * Pair invalid-marked field entries from invalidFieldList with their
576
+ * positionally-adjacent error text from errorTextList. Strategy is
577
+ * positional (not cross-product) — entry N pairs with entry N — because
578
+ * the Haiku judges return fields and messages in DOM document order, so
579
+ * the Nth invalid container's sibling error message tends to be the Nth
580
+ * error entry.
581
+ *
582
+ * Returns an empty array when no entries match — pure additive signal,
583
+ * silent no-op on sites whose forms don't follow this DOM convention.
584
+ *
585
+ * Why "touched+dirty" instead of just "invalid": empirical survey of
586
+ * 30 production step-failure dumps showed 22 of 22 Continue/Submit
587
+ * failures had the touched+dirty + visible error text pattern. The
588
+ * remaining failure shapes (pristine empty required, fill-step
589
+ * errors, pre-form failures) need different diagnostics.
590
+ */
591
+ export declare function pairInvalidWithErrors(invalidFieldList: string, errorTextList: string): ValidationRejectionPair[];
592
+ /**
593
+ * Format a {@link ValidationRejectionPair} as a single-line failureReason
594
+ * string the LLM reads from the replan prompt's WHY VERIFICATION FAILED
595
+ * block. Style matches existing reason formats (`submit-revealed-invalid`,
596
+ * `submit-endpoint-not-matched`): leading category tag, then the facts,
597
+ * then a brief imperative for what the LLM should do.
598
+ */
599
+ export declare function formatValidationRejectedReason(pair: ValidationRejectionPair): string;
600
+ /**
601
+ * Pick a window of `body` that's likely to contain the form's structural
602
+ * evidence (ng-invalid markers, error messages, question labels) that the
603
+ * downstream Haiku judges need to populate the replan/rephrase prompt's
604
+ * FORM FIELDS section.
605
+ *
606
+ * Default: first 8KB. That window held for tenants whose form was at the
607
+ * top of the page (early sweeps in 2026-06).
608
+ *
609
+ * For pages where the form HTML lives below 8KB (verified on AppCast's
610
+ * applyboard SPA: ng-invalid first appears at byte ~15,500 after a header
611
+ * of Angular hydration JS + chrome), the 8KB cap silently produced
612
+ * "FORM FIELDS CURRENTLY MARKED INVALID: (none)" in the replan prompt,
613
+ * leaving the LLM with no evidence and causing it to hallucinate steps
614
+ * from the existing flow's tenant content. The smart path: when an
615
+ * `ng-invalid` or `<form` token appears beyond the default cap, return a
616
+ * window of FORM_WINDOW bytes centered on the marker so the judge sees
617
+ * the actual form structure.
618
+ *
619
+ * Site-agnostic: the markers we look for (ng-invalid, mat-form-field-
620
+ * invalid, is-invalid, <form) are framework-level CSS-class conventions
621
+ * used across countless SPAs, not AppCast-specific.
622
+ */
623
+ export declare function selectBodyExcerpt(body: string): string;
624
+ /**
625
+ * For each container marked invalid by the framework's validity classes,
626
+ * walk the DOM tree under it and surface clickable descendants (radio
627
+ * labels, dropdown options, text inputs) with an xpath the rephrase LLM
628
+ * can hand directly to Stagehand's act(). Closes the gap where the
629
+ * rephrase prompt carries "field X is invalid" but no selector for the
630
+ * radio/option that would clear it — so the LLM proposes "click Submit
631
+ * harder" instead of "answer field X with Yes/No".
632
+ */
633
+ /**
634
+ * Structured leaf-invalid-container record emitted by `probeLeafInvalidContainers`.
635
+ * Replaces the LLM-judge's `{ containerXpath, label, framework, markerKind }` shape
636
+ * with a deterministic-only record carrying everything the prompt needs to surface
637
+ * a specific actionable target ("the Address field, an Angular smart-address
638
+ * autocomplete component, error text 'This field is required'").
639
+ */
640
+ export interface LeafInvalidField {
641
+ /** Best-effort xpath of the leaf invalid container itself. */
642
+ xpath: string;
643
+ /** Nearest discoverable label text walking up from the container, null if not findable. */
644
+ label: string | null;
645
+ /** Which framework convention triggered the leaf match. */
646
+ framework: "angular" | "material" | "bootstrap" | "aria" | "other";
647
+ /** The actual class signature on the container that matched (debug aid). */
648
+ markerClass: string;
649
+ /** Any visible error/required-message text in an adjacent error container. */
650
+ visibleErrorText: string | null;
651
+ /** Tag of the input element inside the container (input, app-input, etc.). */
652
+ inputTag: string;
653
+ }
654
+ /**
655
+ * Deterministic DOM probe for LEAF invalid form containers, replacing the
656
+ * LLM-judge's stochastic "prefer the deepest container" heuristic. Uses the
657
+ * native CSS `:has()` selector — universally supported as of 2023 (Chrome 105+,
658
+ * we run Chrome 149) — to query only containers whose `ng-invalid` /
659
+ * `mat-form-field-invalid` / `is-invalid` / `aria-invalid` marker is NOT
660
+ * shadowed by a same-marker descendant. That's the exact definition of "leaf"
661
+ * in Angular's invalidity-bubbling model: a `<ol class="ng-invalid">` parent
662
+ * matches `ng-invalid` because of bubbling, but its child `<app-input
663
+ * class="ng-invalid">` ALSO matches; `:not(:has(...))` filters out the parent.
664
+ *
665
+ * Today's Encompass-Fitchburg smoke (run 1781478440322) showed E1's prompt
666
+ * instruction ("prefer the leaf, not the bubbled parent") only got Haiku from
667
+ * 5 wrong fields → 1 wrong field — still surfaced `(unlabeled) <ol>` instead
668
+ * of `<app-input autocomplete="zip-code">` at byte 95,033. All 3 replans + 4
669
+ * rephrases then converged on "Click Continue" because the FORM FIELDS section
670
+ * never named a specific fillable target. Switching to deterministic extraction
671
+ * is industry-standard: Anthropic's prompt-engineering guidance says "use the
672
+ * LLM for fuzzy judgment, deterministic extraction for structurally-derivable
673
+ * signals" — DOM tree walking is the latter.
674
+ *
675
+ * Returns up to 12 leaf records. Empty array on `page.evaluate` failure (safe
676
+ * fallback to the existing Haiku judge upstream). The `inputTag` and
677
+ * `visibleErrorText` fields let the prompt distinguish a smart-address
678
+ * autocomplete (where typing-only fails and the cascade needs dropdown
679
+ * selection) from a plain text input.
680
+ */
681
+ export declare function probeLeafInvalidContainers(page: Page): Promise<LeafInvalidField[]>;
682
+ /**
683
+ * Render a list of structured leaf-invalid records as the numbered evidence
684
+ * lines the prompt's FORM FIELDS section expects. Surfaces label + framework
685
+ * + input tag + visible error text in one line so the LLM can disambiguate
686
+ * a smart-address autocomplete from a plain text input (separate widget
687
+ * interaction patterns) and target the field by its real name instead of
688
+ * the `(unlabeled) <ol>` bubble parent the LLM-judge surfaced today.
689
+ */
690
+ export declare function renderLeafInvalidFields(fields: readonly LeafInvalidField[]): string;
691
+ /**
692
+ * Scan recent capture files for failed submit-endpoint requests and pull
693
+ * out structured field-level errors from the response body. The cascade
694
+ * already saves every captured request to `CAPTURES_DIR` with its parsed
695
+ * `responseBody`; this helper reads those files back, filters to captures
696
+ * matching the configured submit pattern with status >= 400, and walks
697
+ * common error-shape conventions (`{ errors: [{ field, message }] }`,
698
+ * `{ validation/fieldErrors: { … } }`, `{ message }`).
699
+ *
700
+ * Returns a short bullet list ready to drop into a prompt; empty string
701
+ * when no failed submit was found. Advisory — never load-bearing.
702
+ */
703
+ export declare function extractSubmitFailureEvidence(recentCaptureFilenames: readonly string[],
704
+ /**
705
+ * Hostnames considered "the site's own backend." In strict mode, only 4xx
706
+ * responses from one of these hostnames count as submit failures (we
707
+ * don't surface third-party CDN/analytics 4xx as form-rejection
708
+ * evidence). Replaces the prior `submitEndpointPattern` regex with
709
+ * deterministic hostname equality. Empty list / "any-4xx" mode disables
710
+ * the host filter and returns any 4xx in the window.
711
+ */
712
+ ownBackendHostnames: readonly string[], capturesDir?: string, mode?: "strict" | "any-4xx"): string;
713
+ /**
714
+ * Surface Google Analytics Measurement Protocol events captured during a
715
+ * step's attempt window. AppCast (and most GA4-instrumented SPAs) emit
716
+ * `view_secondPage`, `view_thankYouPage`, `form_submit` and similar events
717
+ * via `https://www.google-analytics.com/g/collect` — the engine already
718
+ * stores these in `recentCaptures[]` but no code reads them. Without
719
+ * surfacing them the LLM cannot tell that a Submit click actually advanced
720
+ * the SPA to page 2 (because `pre.url === post.url` under SPA routing), nor
721
+ * that the site's own validator (`epn.validationErrorsCount`) reports N
722
+ * unfilled required fields, nor that the application reached the
723
+ * thank-you page (the canonical SUCCESS signal when `/integrated_apply`
724
+ * POSTs are debounced or missed).
725
+ *
726
+ * Returns a numbered evidence list. Empty string when no GA collect
727
+ * captures are present. Advisory — never load-bearing.
728
+ */
729
+ export declare function extractGaEventEvidence(recentCaptureFilenames: readonly string[], capturesDir?: string): string;
730
+ /**
731
+ * Render a long step list as a small head + tail window with an elision
732
+ * marker. Replan prompts grew to ~114KB on the AppCast 331-step flow
733
+ * (verified Fitchburg 2026-06-14 run), causing Sonnet 4.6 to TTFT-stall
734
+ * out at 187s with `APIConnectionTimeoutError: Request timed out.` —
735
+ * the ONLY non-API-quota replan failure across ~30+ historical calls.
736
+ *
737
+ * Empirically, replans at ≤65K chars complete in 3-13s; the 114K case
738
+ * was 15x slower than the worst clean run. Trimming the THREE step
739
+ * blocks (THE ORIGINAL FLOW + STEPS ALREADY SUCCESSFULLY COMPLETED +
740
+ * REMAINING UNEXECUTED STEPS) eliminates ~80% of the bloat without
741
+ * losing replan-relevant context: the LLM's job is to bridge from the
742
+ * failed step back into the remaining tail, not enumerate every step.
743
+ *
744
+ * Configurable head/tail caps so callers can keep relevant boundaries
745
+ * (e.g. completed tail = the last few steps that just succeeded;
746
+ * remaining head = what to bridge into).
747
+ */
748
+ /**
749
+ * Outcome of attempting to fill an HTML5 date/time input via the
750
+ * native-setter + dispatch-events workaround. `null` when the target
751
+ * isn't a date/time input (caller falls back to the normal cascade).
752
+ */
753
+ export interface Html5DateFillResult {
754
+ /** Whether the value actually landed in the DOM after dispatch. */
755
+ filled: boolean;
756
+ /** What the input's value is now (for verifier signal). */
757
+ postValue: string;
758
+ /** The input's type attribute, for the verifier and prompt context. */
759
+ inputType: string;
760
+ }
761
+ /**
762
+ * Deterministic fill for HTML5 `<input type="date|time|datetime-local|month|week">`
763
+ * elements. Bypasses Stagehand bug #1249 (locator.fill() and act({method: 'fill'})
764
+ * resolve without error but the value reads back as empty string — confirmed
765
+ * OPEN as of 2026-06-14 in browserbase/stagehand). The fix follows the
766
+ * industry-standard React/Angular controlled-component pattern, also
767
+ * documented as the verified workaround in the Stagehand issue itself.
768
+ *
769
+ * Mechanism:
770
+ * 1. Walk the input value setter on `HTMLInputElement.prototype` to bypass
771
+ * framework value-setter interception (React, Angular Forms, Vue v-model
772
+ * all override the setter at instance level — calling the prototype
773
+ * descriptor's setter restores the native behavior).
774
+ * 2. Dispatch synthesized `input` and `change` events with `bubbles: true`
775
+ * so the framework's reactivity hooks fire and the form-control state
776
+ * updates (mark dirty / mark touched / clear ng-pristine).
777
+ *
778
+ * Returns `null` when the xpath doesn't resolve OR the resolved element
779
+ * isn't a date/time input — caller falls back to the normal cascade path.
780
+ *
781
+ * Site-agnostic: the bug + workaround are universal across any tenant
782
+ * using HTML5 date/time inputs.
783
+ */
784
+ /**
785
+ * Normalize a date/time string to the format the HTML5 spec requires for
786
+ * the given input type. The HTML5 spec REJECTS programmatic .value writes
787
+ * that don't match the canonical format, regardless of how the browser
788
+ * DISPLAYS the date (locale only affects display formatting).
789
+ *
790
+ * - type="date": YYYY-MM-DD
791
+ * - type="time": HH:MM (or HH:MM:SS)
792
+ * - type="month": YYYY-MM
793
+ * - type="week": YYYY-Www
794
+ * - type="datetime-local": YYYY-MM-DDTHH:MM
795
+ *
796
+ * Today's smoke surfaced this gap: flow text passed "06-14-2026" (MM-DD-YYYY)
797
+ * to a `<input type="date">`. Even if Stagehand had fired the fill correctly,
798
+ * the value would have been silently rejected by the input's setter. K'2's
799
+ * dispatchEvent and Fix I's verifyFillReadback both catch the consequence,
800
+ * but normalizing the value here lets the cascade WORK on first try.
801
+ *
802
+ * Returns null when the input format is unrecognized — caller knows to
803
+ * either pass-through (the value might be correct as-is) or skip.
804
+ */
805
+ export declare function normalizeDateValue(raw: string, inputType: string): string | null;
806
+ export declare function fillHtml5DateTimeInput(page: Page, xpath: string, value: string): Promise<Html5DateFillResult | null>;
807
+ /**
808
+ * Outcome of verifying that a fill action's value actually landed in the
809
+ * target element. Used by the cascade verifier to catch silent-value-rejection
810
+ * cases (HTML5 type validation, framework-controlled-component rejection,
811
+ * masked-input library reformatting).
812
+ */
813
+ export interface VerifyFillReadbackResult {
814
+ /** "matched" = element.value === expectedValue; "rejected" = element.value === "" after a non-empty fill; "differs" = element value is non-empty but different (masked / reformatted) */
815
+ outcome: "matched" | "rejected" | "differs";
816
+ /** Actual value read back from the element after the fill. */
817
+ postValue: string;
818
+ /** Tag of the target element (input/textarea/contenteditable). */
819
+ tag: string;
820
+ }
821
+ /**
822
+ * Read back an element's value after a fill action and compare to the
823
+ * expected value. Catches silent-value-rejection cases that the verifier's
824
+ * existing signals (network/url/dom/htmlDelta/textChanged) miss:
825
+ * - HTML5 type validation rejecting bad format (date with MM-DD-YYYY,
826
+ * number with letters, email without @, url without protocol, etc.)
827
+ * - Framework-controlled-component (Angular [(ngModel)], React useState)
828
+ * silently rejecting values that don't pass internal validation
829
+ * - Masked-input libraries (phone, currency, date formatters) reformatting
830
+ * the value as it's typed
831
+ *
832
+ * Returns null for non-fillable elements (clicks, selects, etc.) — caller
833
+ * knows to skip the check.
834
+ *
835
+ * Site-agnostic: works on any <input>, <textarea>, or [contenteditable]
836
+ * element regardless of framework wrapping. Industry-standard pattern
837
+ * (react-testing-library's `getByDisplayValue` does the same readback).
838
+ */
839
+ export declare function verifyFillReadback(page: Page, xpath: string, expectedValue: string): Promise<VerifyFillReadbackResult | null>;
840
+ /**
841
+ * True when a control's text/aria-label denotes a resume-upload affordance
842
+ * (the button that surfaces a hidden `<input type=file>` or opens a chooser).
843
+ * Pure + exported for unit tests; the vocabulary is intentionally generic so it
844
+ * benefits any MUI/React/dropzone ATS, not just Talemetry. Rejects negative
845
+ * lookalikes ("upload later", "no file", a bare "submit") so the click-to-
846
+ * surface path never fires a skip/decline/submit control.
847
+ */
848
+ export declare function isUploadAffordanceLabel(label: string): boolean;
849
+ /**
850
+ * Materialize the in-memory resume fixture to a temp file so CDP
851
+ * `DOM.setFileInputFiles` (which requires a filesystem path, unlike
852
+ * Playwright's `locator.setInputFiles`) can reference it. Only used as a
853
+ * fallback when the on-disk fixture path is unavailable. Process-scoped — the
854
+ * recon run is ephemeral, so no explicit cleanup. Returns the absolute path.
855
+ */
856
+ export declare function writeFixtureToTempFile(fixture: {
857
+ buffer: Buffer;
858
+ name: string;
859
+ }): string;
860
+ /**
861
+ * Parse a select/dropdown flow step into the option to choose and (when
862
+ * present) the question label that scopes which dropdown it targets.
863
+ *
864
+ * Why: HCA/Talemetry render dropdowns as `MuiNativeSelect` native `<select>`
865
+ * with `tabindex="-1"` — removed from the accessibility tree, so Stagehand
866
+ * observe returns `[]` and the cascade can never select an option. The select
867
+ * primitive answers these directly from the DOM, but needs the target option
868
+ * text (and, to disambiguate multiple dropdowns on one page, the question
869
+ * label) extracted from the human-readable step.
870
+ *
871
+ * Recognizes the flow's conventional phrasings, all quoted:
872
+ * "select 'Yes'", "select or check 'BLS'",
873
+ * "for 'What is your highest level…?' select 'BSN completed'",
874
+ * "select 'Texas' in the State/Region dropdown".
875
+ * Returns null when the step is not a single-dropdown select (e.g. generic
876
+ * "for any remaining question…" catch-alls, or radio/checkbox-only steps) so
877
+ * the caller falls through to the normal cascade.
878
+ */
879
+ export declare function parseSelectStep(instruction: string): {
880
+ option: string;
881
+ questionLabel: string | null;
882
+ } | null;
883
+ /**
884
+ * Parse a single-choice RADIO flow step into the option to click and (when
885
+ * present) the question label that scopes which radio group it targets.
886
+ *
887
+ * Why this exists (sibling of `parseSelectStep`): `parseSelectStep`
888
+ * deliberately excludes bare radio steps ("a bare 'click the Yes answer' is a
889
+ * radio"), leaving radios with no DOM-direct primitive — they fall to the
890
+ * observe cascade, which on HCA/Talemetry's MUI radio markup resolves the step
891
+ * to a wrapper `<div>`/`<span>` (not the `<input type=radio>`) and commits via
892
+ * a bare `el.click()` that never triggers React's controlled-state `onChange`.
893
+ * The field stays `Mui-error` "required", Next no-ops, and the wizard walls at
894
+ * Step 2 of 10. `tryRadioPrimitive` needs the option/question text extracted
895
+ * from the human-readable step to answer the radio group directly.
896
+ *
897
+ * Recognizes the flow's conventional radio phrasings, all quoted:
898
+ * "Click the 'Yes' answer for the question 'Are you at least 18 years…?'",
899
+ * "Click the 'No' answer for the question about requiring visa sponsorship…",
900
+ * "Click the 'Yes' radio button for the 'Are you currently licensed…' question".
901
+ * Returns null for select/checkbox steps (`select`/`check` verbs — those route
902
+ * to the select/checkbox primitives) and for the "for any remaining…" catch-all.
903
+ */
904
+ export declare function parseRadioStep(instruction: string): {
905
+ option: string;
906
+ questionLabel: string | null;
907
+ } | null;
908
+ /**
909
+ * Run a primitive's read-only DOM enumerate with a bounded settle-retry. SPA
910
+ * wizards (Talemetry) frequently render the target widget a beat AFTER the flow
911
+ * step fires — the first evaluate sees an empty page, so the primitive would
912
+ * give up even though the widget appears moments later. Re-run the enumerate up
913
+ * to `PRIMITIVE_ENUMERATE_ATTEMPTS` times, waiting `PRIMITIVE_ENUMERATE_RETRY_MS`
914
+ * between tries, returning as soon as `isPresent(result)` is true; otherwise
915
+ * return the last (absent) result so the caller falls through to the cascade
916
+ * unchanged. Only wraps the ENUMERATE (which is read-only when nothing matches);
917
+ * the apply/mutate paths are untouched. Site-agnostic render-lag mitigation.
918
+ *
919
+ * `opts` overrides the attempt count / interval for callers that need a longer
920
+ * window (the resume-upload widget can take 5s+ to mount); omitting it keeps the
921
+ * default ~3s window so every existing caller is unchanged.
922
+ */
923
+ export declare function pollEnumerate<T>(page: Page, expr: string, isPresent: (result: T) => boolean, opts?: {
924
+ attempts?: number;
925
+ intervalMs?: number;
926
+ }): Promise<T>;
927
+ /**
928
+ * Bounded poll for the real advance-transition POST to appear in this step's
929
+ * capture window. The verifiers snapshot once after `STEP_PAUSE_MS`, but the
930
+ * genuine `TransitionWorklet(type="next")` POST can land hundreds of ms to 2s+
931
+ * AFTER that snapshot (HCA fires a fast `WorkletPayload` autosave first). A
932
+ * one-shot check false-negatives the advance, retries the click, and the stale
933
+ * retry fires a `back` — a next→back oscillation that never leaves the page.
934
+ * Re-check {@link windowHasAdvanceTransition} every `intervalMs` until it matches
935
+ * or `timeoutMs` elapses; returns true the moment a real advance lands.
936
+ *
937
+ * Each poll iteration re-scans `capturesDir` for files indexed after `preIdx`
938
+ * (via {@link capturesAfterIndex}), so a POST that lands on disk AFTER the first
939
+ * check enters the scanned window on the next iteration. `preIdx` scopes the
940
+ * window to THIS step, so a later step's transition can't satisfy it.
941
+ */
942
+ export declare function waitForTransitionBody(params: {
943
+ page: Page;
944
+ preIdx: number;
945
+ advanceTransitionBodyPattern: string | null;
946
+ timeoutMs: number;
947
+ intervalMs: number;
948
+ capturesDir?: string;
949
+ }): Promise<boolean>;
950
+ /**
951
+ * Pick an option to satisfy a REQUIRED select on a catch-all step, from the
952
+ * select's option TEXTS (placeholder already excluded upstream). Policy: take
953
+ * the first non-decline option (a plausible substantive answer — the operator
954
+ * accepts LLM-plausible answers reaching HCA prod); fall back to the first
955
+ * option only if every option is a decline/placeholder. Returns null when there
956
+ * is nothing selectable. Pure + exported so the policy is unit-testable; the LLM
957
+ * path ({@link judgeSelectOptionWithLLM}) is preferred when a client is present,
958
+ * this is the deterministic fallback.
959
+ */
960
+ export declare function chooseRequiredSelectOption(options: readonly string[]): string | null;
961
+ /** One radio group enumerated from the DOM for `selectRadioGroupOption`. */
962
+ export type RadioGroupCandidate = {
963
+ /** Index into the DOM's radio-group list (stable across the enumerate/apply pair). */
964
+ gi: number;
965
+ /** The group's legend/label text; empty string when the group is unlabeled. */
966
+ label: string;
967
+ /**
968
+ * The group's radio options. `ri` is the raw radio DOM index within the group;
969
+ * `id`/`xpath` are stable locator hints threaded to the trusted-click commit
970
+ * (`applyRadioSelection`) — `id` preferred, `xpath` the no-id fallback.
971
+ */
972
+ options: {
973
+ ri: number;
974
+ text: string;
975
+ id: string;
976
+ xpath: string;
977
+ }[];
978
+ /** True when the group already has a checked radio (answered by an earlier step). */
979
+ alreadyChecked: boolean;
980
+ };
981
+ /**
982
+ * Build an XPath predicate that matches an `<input>` by its `id`, safe for any
983
+ * id value. MUI/Talemetry radio ids are base64-ish (no double-quote), so a plain
984
+ * quoted literal suffices — but if an id ever contains a `"`, fall back to
985
+ * `concat(...)` so the XPath stays valid. Pure + exported for unit tests.
986
+ */
987
+ export declare function buildRadioIdXPath(id: string): string;
988
+ /**
989
+ * Choose which radio group + option answers a flow step, deterministically and
990
+ * positionally. Pure (no DOM/LLM) so it is unit-testable — the crux of the
991
+ * unlabeled-radio disambiguation.
992
+ *
993
+ * Why this exists: HCA/Talemetry Basic Info has multiple UNLABELED yes/no groups
994
+ * (visa-sponsorship, common-domicile), answered by consecutive flow steps. The
995
+ * old in-browser matcher treated an unlabeled group (`label===""`) as matching
996
+ * ANY question (`"".includes(q)`/`q.includes("")===0`), so two unlabeled "No"
997
+ * groups both matched → ambiguous → an LLM guess that could answer one group
998
+ * twice and leave the other required-blank. This picks the k-th unanswered
999
+ * unlabeled group for the k-th unlabeled step instead.
1000
+ *
1001
+ * Resolution order (already-answered groups — `alreadyChecked` — are excluded
1002
+ * throughout, since a prior step leaves its group's radio checked):
1003
+ * 1. exactly one unanswered group whose NON-empty label genuinely matches the
1004
+ * question (substring either way) AND offers the wanted option → pick it;
1005
+ * 2. more than one such labeled match → `"ambiguous"` (caller uses the LLM);
1006
+ * 3. else (no labeled match — the question is unlabeled-in-DOM) → the FIRST
1007
+ * unanswered group in DOM order that offers the wanted option → pick it
1008
+ * (positional: k-th unlabeled step → k-th unlabeled group);
1009
+ * 4. nothing offers the wanted option → `null` (caller falls through).
1010
+ */
1011
+ export declare function selectRadioGroupOption(params: {
1012
+ groups: readonly RadioGroupCandidate[];
1013
+ wantOption: string;
1014
+ questionLabel: string | null;
1015
+ }): {
1016
+ gi: number;
1017
+ ri: number;
1018
+ } | null | "ambiguous";
1019
+ /**
1020
+ * Runs one flow step through the self-healing cascade. Returns when any
1021
+ * attempt produces an observable effect (network call or URL change). Throws
1022
+ * StepVerificationError after all attempts have been exhausted; the
1023
+ * diagnostic bundle on disk has everything the human needs to fix the flow.
1024
+ */
1025
+ /**
1026
+ * Cheap page-state check run BEFORE the self-healing cascade. Asks Stagehand
1027
+ * to observe the page filtered by the step's instruction; returns "absent"
1028
+ * when zero candidates come back. Treat any thrown error (incl. timeout) as
1029
+ * "present" — we don't want a flaky observe call to short-circuit into a
1030
+ * replan when the cascade might still succeed; the cascade has its own
1031
+ * timeouts and dump path.
1032
+ *
1033
+ * The cascade does this same call as attempt 2 today (line ~1278). Running
1034
+ * it ahead of attempt 1 catches the failure mode confirmed by step-failures
1035
+ * dumps 008 + 086: page state had drifted (e.g. flow expected the form page
1036
+ * but the SPA was still on the resume-upload screen), so attempt 1's
1037
+ * `stagehand.act(step)` chewed up an LLM call producing nothing useful. The
1038
+ * probe lets us fail fast and feed the replanner a clean "no candidates"
1039
+ * signal instead of "all 4 attempts failed."
1040
+ */
1041
+ /** One entry per ng-invalid form control found by the pre-submit probe. */
1042
+ export interface InvalidFormControl {
1043
+ /** Human-readable label associated with the field — element's nearest
1044
+ * `<label>` text, or the value of `aria-label` / `data-id` / `name`. */
1045
+ label: string;
1046
+ /** Compact class signature naming the framework-specific marker that
1047
+ * matched, e.g. `ng-invalid ng-touched`. Helps the LLM correlate
1048
+ * "user-interacted + still invalid" patterns. */
1049
+ classSignature: string;
1050
+ /** True when the underlying control is *empty* (text input value is "",
1051
+ * radio/checkbox is unchecked, select has no chosen option). False
1052
+ * means the field is non-empty but still marked invalid (e.g. wrong
1053
+ * format). The cascade's pre-submit warning surfaces the empty ones
1054
+ * loudly because they are almost always Stagehand re-render victims. */
1055
+ emptyOrUnchecked: boolean;
1056
+ /** Set by the probe when it auto-picked a value to clear the
1057
+ * ng-invalid state. Identifies WHAT action was taken so the cascade
1058
+ * can surface a self-heal hint ("the probe auto-picked X for you;
1059
+ * consider adding an explicit step to the flow file"). `null` when no
1060
+ * auto-pick fired (either the control was already valid, or the probe
1061
+ * couldn't find a sensible default). */
1062
+ autoFilled: {
1063
+ action: "selected-radio" | "checked-checkbox" | "filled-text" | "selected-option";
1064
+ value: string;
1065
+ } | null;
1066
+ }
1067
+ /**
1068
+ * Runs ONLY on the cascade's final step when a submitEndpointPattern is
1069
+ * declared. Surfaces ng-invalid form controls (and whether each is empty)
1070
+ * BEFORE the first click attempt, so the cascade's first failure reason
1071
+ * names the real blocker instead of "no observable effect." Empty + invalid
1072
+ * is the signature of "Stagehand filled this earlier but a downstream
1073
+ * Angular/React re-render wiped it" — the issue the LLM-replan cannot
1074
+ * diagnose from observe-list alone.
1075
+ *
1076
+ * Returns the list (potentially empty) so callers can both log it AND
1077
+ * inject structured warnings into the cascade's failureReasons array.
1078
+ * Pure read — no side effects on the page.
1079
+ */
1080
+ /**
1081
+ * Type-narrow a raw page.evaluate payload entry into a typed
1082
+ * `InvalidFormControl`. Exported for unit testing — the browser-context
1083
+ * expression is hard to unit-test directly, but the narrowing happens on
1084
+ * the Node side and is the source of any bugs that would silently coerce
1085
+ * a malformed entry into a valid record.
1086
+ *
1087
+ * Returns null when the entry is missing required fields or has the wrong
1088
+ * shape. Defensive about `autoFilled` (allowed to be null OR a typed
1089
+ * action+value object; anything else becomes null).
1090
+ */
1091
+ export declare function narrowInvalidFormControl(entry: unknown): InvalidFormControl | null;
1092
+ /**
1093
+ * Cheap pre-cascade reachability gate. Runs before the 5-attempt healing cascade
1094
+ * (and any global replan) so a step aimed at the wrong page state fails fast
1095
+ * instead of burning attempts and replan budget. A focused observe can
1096
+ * under-return on controlled-component (React/MUI) forms — returning zero
1097
+ * candidates for a declarative "Fill in X" step even when the field is present —
1098
+ * so a 0-candidate focused result falls back to an unfocused observe before the
1099
+ * step is declared "absent". Exported for tests.
1100
+ */
1101
+ export declare function probeStepBeforeAttempts(params: {
1102
+ stagehand: Stagehand;
1103
+ step: string;
1104
+ stepIndex: number;
1105
+ logger: Logger;
1106
+ captureFn?: CaptureFn;
1107
+ }): Promise<"present" | "absent">;
1108
+ export declare function executeStepWithHealing(params: {
1109
+ stagehand: Stagehand;
1110
+ page: Page;
1111
+ step: string;
1112
+ /**
1113
+ * When true and Stagehand's act+observe finds no candidates, the cascade
1114
+ * skips the step cleanly instead of burning through attempts 3-4 and the
1115
+ * replan budget. Required steps (default) keep the full 4-attempt healing.
1116
+ */
1117
+ optional: boolean;
1118
+ /**
1119
+ * When true, dispatches to the upload primitive (which sets the fixture
1120
+ * on the page's <input type=file>) instead of running the cascade. Required
1121
+ * for resume-upload widgets that hide the real file input behind styled
1122
+ * buttons Stagehand can't click. Set from the flow file's `upload: true`.
1123
+ */
1124
+ upload: boolean;
1125
+ /**
1126
+ * When true, treat this step as the canonical submit click for the
1127
+ * `submitEndpointPattern` verifier even if it is NOT the last step in
1128
+ * the flow. Set from the flow file's `submitStep: true`. AppCast's flow
1129
+ * has its Submit click at index 55/328 — without this flag, the pre-
1130
+ * submit DOM probe (gated on isFinalStep alone) never fires on the real
1131
+ * Submit, so unfilled required fields produce silent submit failures.
1132
+ * Site-agnostic: any flow whose canonical submit is mid-list can opt in.
1133
+ */
1134
+ submitStep: boolean;
1135
+ stepIndex: number;
1136
+ phase: string;
1137
+ signalCounter: {
1138
+ n: number;
1139
+ };
1140
+ recentCaptures: string[];
1141
+ recentCaptureMeta: {
1142
+ method: string;
1143
+ status: number;
1144
+ url: string;
1145
+ }[];
1146
+ anthropic: Anthropic | null;
1147
+ logger: Logger;
1148
+ captureFn?: CaptureFn;
1149
+ resumeFixture: {
1150
+ buffer: Buffer;
1151
+ name: string;
1152
+ mimeType: string;
1153
+ } | null;
1154
+ /**
1155
+ * Final-step gate: when both are set, the verifier additionally requires at
1156
+ * least one capture in `recentCaptureMeta` whose URL matches the pattern. Lets
1157
+ * sites declare "the click that ends the flow must produce a request to
1158
+ * /api/.../submit" so the cascade can detect tracking-pixel-only clicks as
1159
+ * verification failures and engage the rephrase/replan recovery path
1160
+ * instead of declaring victory.
1161
+ */
1162
+ isFinalStep: boolean;
1163
+ submitEndpointPattern: string | null;
1164
+ /**
1165
+ * DOM-level fallback for the final-step submit gate. If the network
1166
+ * capture didn't match `submitEndpointPattern` within the attempt
1167
+ * window, the verifier also probes the live DOM for any of these
1168
+ * selectors — a match indicates the SPA has reached its submitted /
1169
+ * thank-you state even though the underlying POST landed outside the
1170
+ * verifier's capture window (debounced submits, batched requests,
1171
+ * SPAs that swap the form before the network event records).
1172
+ */
1173
+ submittedStateSelectors: string[];
1174
+ /**
1175
+ * When true, the final-step verifier accepts ONLY a `submitEndpointPattern`
1176
+ * network capture as proof of submission — DOM-state matches become a
1177
+ * tiebreaker, not a standalone fallback. See `RECON_FLOW_FILE_SCHEMA` for
1178
+ * the rationale + the SPA failure mode this guards against.
1179
+ */
1180
+ requireSubmitEndpointMatch: boolean;
1181
+ /**
1182
+ * Opt-in regex matched against same-window capture request BODIES to trust an
1183
+ * interior advance/"Next" step's network signal (see `RECON_FLOW_FILE_SCHEMA`
1184
+ * `advanceTransitionBodyPattern`). Null/empty = today's behavior (any
1185
+ * network/url/dom signal verifies an advance).
1186
+ */
1187
+ advanceTransitionBodyPattern: string | null;
1188
+ /**
1189
+ * URL path fragments that indicate a successful submit transition.
1190
+ * Surfaced to the Haiku verifySubmit judge as one of the strong
1191
+ * corroborating signals (DOM/URL/title) required for verified=true.
1192
+ * Site-supplied via the flow file. Empty array = no URL hints.
1193
+ */
1194
+ successUrlFragments: string[];
1195
+ /**
1196
+ * Page-title substrings that indicate a successful submit. Same role as
1197
+ * successUrlFragments. Site-supplied via the flow file. Empty = no hints.
1198
+ */
1199
+ successPageTitleHints: string[];
1200
+ /**
1201
+ * Hostnames considered "the site's own backend." A 2xx POST/PUT/DELETE
1202
+ * to one of these hostnames within the attempt window is a corroborating
1203
+ * network signal for the Haiku verifySubmit judge. Site-supplied via the
1204
+ * flow file. Empty array means the judge ignores network signals.
1205
+ */
1206
+ ownBackendHostnames: string[];
1207
+ /**
1208
+ * Optional site-specific class-name prefixes that wrap form/error
1209
+ * state. Surfaced to the Haiku invalid-fields judge as additional
1210
+ * structural evidence. Empty array = framework-conventional only.
1211
+ */
1212
+ knownErrorClassPrefixes: string[];
1213
+ /**
1214
+ * Site + engine wizard-exit labels. When a "click / advance" step's resolved
1215
+ * action names one of these (via isWizardExitAction), the cascade rejects it
1216
+ * so an advance step never fires a save-and-exit / cancel / restart control.
1217
+ */
1218
+ wizardExitButtonLabels: string[];
1219
+ /**
1220
+ * Optional accumulator the cascade pushes onto when this step verifies.
1221
+ * Lets the main loop maintain a short cross-step trajectory of `verifiedBy`
1222
+ * signals (network / url / dom / submitted-state-dom) which is then
1223
+ * surfaced to the replan prompt as "PRIOR STEP TRAJECTORY" so the LLM
1224
+ * can tell whether the page has been visibly transitioning vs. staying
1225
+ * static. When omitted, the cascade behaves identically — purely
1226
+ * additive instrumentation.
1227
+ */
1228
+ trajectory?: {
1229
+ stepIndex: number;
1230
+ verifiedBy: AttemptRecord["verifiedBy"];
1231
+ }[];
1232
+ /**
1233
+ * Persistence seam for the terminal failure dump. When the probe finds no
1234
+ * candidates or the cascade exhausts every attempt, the engine hands the
1235
+ * dump payload to this callback and threads its returned path into the
1236
+ * thrown {@link StepVerificationError} message. Keeps this leaf module free
1237
+ * of the recon CLI's on-disk `step-failures/` layout; the CLI passes its own
1238
+ * `dumpStepFailure`. When omitted, the dump is skipped and the path is null.
1239
+ */
1240
+ onStepFailure?: (params: {
1241
+ stepIndex: number;
1242
+ phase: string;
1243
+ originalStep: string;
1244
+ attempts: AttemptRecord[];
1245
+ finalObserve: Action[];
1246
+ pageUrl: string;
1247
+ pageTitle: string;
1248
+ recentCaptures: string[];
1249
+ bodyOuterHtml: string | null;
1250
+ unfocusedObserve: Action[];
1251
+ }) => string | null;
1252
+ }): Promise<"completed" | "skipped">;
1253
+ /** Default resume fixture path; overridable via --resume-fixture or RESUME_FIXTURE_PATH. */
1254
+ /**
1255
+ * One flow step in the shape a generated plugin hands to {@link runHealingFlow}.
1256
+ * Mirrors the recon CLI's `NormalizedStep` minus its replan-origin bookkeeping:
1257
+ * a plugin only needs the four fields the self-heal cascade reads per step.
1258
+ */
1259
+ export interface HealingFlowStep {
1260
+ instruction: string;
1261
+ optional: boolean;
1262
+ upload: boolean;
1263
+ submitStep: boolean;
1264
+ }
1265
+ /**
1266
+ * Dependency bundle for {@link runHealingFlow}. The verifier-config fields are
1267
+ * all optional and default to the SAME values the recon CLI's `main()` passes to
1268
+ * {@link executeStepWithHealing}, so a plugin that supplies none gets identical
1269
+ * cascade behavior to a recon run with no site-specific verifier hints.
1270
+ */
1271
+ export interface RunHealingFlowDeps {
1272
+ stagehand: Stagehand;
1273
+ page: Page;
1274
+ steps: HealingFlowStep[];
1275
+ logger: Logger;
1276
+ anthropic: Anthropic | null;
1277
+ resumeFixture: {
1278
+ buffer: Buffer;
1279
+ name: string;
1280
+ mimeType: string;
1281
+ } | null;
1282
+ submitEndpointPattern?: string | null;
1283
+ submittedStateSelectors?: string[];
1284
+ requireSubmitEndpointMatch?: boolean;
1285
+ advanceTransitionBodyPattern?: string | null;
1286
+ successUrlFragments?: string[];
1287
+ successPageTitleHints?: string[];
1288
+ ownBackendHostnames?: string[];
1289
+ knownErrorClassPrefixes?: string[];
1290
+ wizardExitButtonLabels?: string[];
1291
+ }
1292
+ /**
1293
+ * Block until a just-navigated SPA has actually hydrated, so the flow does not
1294
+ * begin stepping against a shell page. `page.goto(..., "networkidle")` on a
1295
+ * Cloudflare-fronted single-page app resolves during the challenge/redirect —
1296
+ * before the client framework renders the real DOM — so the first steps would
1297
+ * otherwise probe an empty page, find no candidates, and (being optional) skip
1298
+ * the entire flow. The recon CLI has this gate inline; generated plugins call it
1299
+ * here so they inherit the same behavior. Polls `document.body.outerHTML.length`
1300
+ * up to a threshold, then proceeds regardless (best-effort, never throws).
1301
+ */
1302
+ export declare function waitForSpaReady(page: Page, logger: Logger, opts?: {
1303
+ timeoutMs?: number;
1304
+ pollMs?: number;
1305
+ minBodyLength?: number;
1306
+ }): Promise<void>;
1307
+ /**
1308
+ * Plugin-facing wrapper that drives a recon flow's steps through the SAME
1309
+ * self-heal cascade the recon CLI uses, WITHOUT the CLI's disk-dump/replan
1310
+ * layer. Exists so a generated site plugin can reuse the battle-tested step
1311
+ * runner (its five DOM primitives + multi-signal submit verifier) as a browser
1312
+ * fallback, instead of re-implementing a bare `guardedAct` loop that has none of
1313
+ * the healing. Passing no `onStepFailure`/`captureFn`/`trajectory` means a
1314
+ * terminal step failure propagates as {@link StepVerificationError} for the
1315
+ * plugin's `execute()` to handle — there is no on-disk dump and no LLM replan.
1316
+ */
1317
+ export declare function runHealingFlow(deps: RunHealingFlowDeps): Promise<void>;
1318
+ export {};
1319
+ //# sourceMappingURL=flow-runner.d.ts.map