@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.
- package/LICENSE +21 -0
- package/README.md +958 -0
- package/dist/api/errors.d.ts +88 -0
- package/dist/api/errors.d.ts.map +1 -0
- package/dist/api/errors.js +164 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/helpers/envelope.d.ts +13 -0
- package/dist/api/helpers/envelope.d.ts.map +1 -0
- package/dist/api/helpers/envelope.js +20 -0
- package/dist/api/helpers/envelope.js.map +1 -0
- package/dist/api/helpers/reply.d.ts +9 -0
- package/dist/api/helpers/reply.d.ts.map +1 -0
- package/dist/api/helpers/reply.js +14 -0
- package/dist/api/helpers/reply.js.map +1 -0
- package/dist/api/plugins/auth.d.ts +27 -0
- package/dist/api/plugins/auth.d.ts.map +1 -0
- package/dist/api/plugins/auth.js +111 -0
- package/dist/api/plugins/auth.js.map +1 -0
- package/dist/api/plugins/error-handler.d.ts +12 -0
- package/dist/api/plugins/error-handler.d.ts.map +1 -0
- package/dist/api/plugins/error-handler.js +72 -0
- package/dist/api/plugins/error-handler.js.map +1 -0
- package/dist/api/plugins/request-context.d.ts +17 -0
- package/dist/api/plugins/request-context.d.ts.map +1 -0
- package/dist/api/plugins/request-context.js +61 -0
- package/dist/api/plugins/request-context.js.map +1 -0
- package/dist/api/routes/health.d.ts +55 -0
- package/dist/api/routes/health.d.ts.map +1 -0
- package/dist/api/routes/health.js +137 -0
- package/dist/api/routes/health.js.map +1 -0
- package/dist/api/routes/plugins-introspection.d.ts +17 -0
- package/dist/api/routes/plugins-introspection.d.ts.map +1 -0
- package/dist/api/routes/plugins-introspection.js +13 -0
- package/dist/api/routes/plugins-introspection.js.map +1 -0
- package/dist/api/schemas/common.d.ts +114 -0
- package/dist/api/schemas/common.d.ts.map +1 -0
- package/dist/api/schemas/common.js +109 -0
- package/dist/api/schemas/common.js.map +1 -0
- package/dist/api/schemas/telemetry.d.ts +66 -0
- package/dist/api/schemas/telemetry.d.ts.map +1 -0
- package/dist/api/schemas/telemetry.js +52 -0
- package/dist/api/schemas/telemetry.js.map +1 -0
- package/dist/cache/keyed-ttl-cache.d.ts +45 -0
- package/dist/cache/keyed-ttl-cache.d.ts.map +1 -0
- package/dist/cache/keyed-ttl-cache.js +69 -0
- package/dist/cache/keyed-ttl-cache.js.map +1 -0
- package/dist/cache/response-cache.d.ts +37 -0
- package/dist/cache/response-cache.d.ts.map +1 -0
- package/dist/cache/response-cache.js +114 -0
- package/dist/cache/response-cache.js.map +1 -0
- package/dist/config.d.ts +258 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +138 -0
- package/dist/config.js.map +1 -0
- package/dist/lib/applicant-payload.d.ts +28 -0
- package/dist/lib/applicant-payload.d.ts.map +1 -0
- package/dist/lib/applicant-payload.js +20 -0
- package/dist/lib/applicant-payload.js.map +1 -0
- package/dist/lib/application-address.d.ts +16 -0
- package/dist/lib/application-address.d.ts.map +1 -0
- package/dist/lib/application-address.js +20 -0
- package/dist/lib/application-address.js.map +1 -0
- package/dist/lib/application-answers.d.ts +151 -0
- package/dist/lib/application-answers.d.ts.map +1 -0
- package/dist/lib/application-answers.js +95 -0
- package/dist/lib/application-answers.js.map +1 -0
- package/dist/lib/application-identity.d.ts +18 -0
- package/dist/lib/application-identity.d.ts.map +1 -0
- package/dist/lib/application-identity.js +20 -0
- package/dist/lib/application-identity.js.map +1 -0
- package/dist/lib/application-resume.d.ts +21 -0
- package/dist/lib/application-resume.d.ts.map +1 -0
- package/dist/lib/application-resume.js +23 -0
- package/dist/lib/application-resume.js.map +1 -0
- package/dist/lib/bedrock.d.ts +22 -0
- package/dist/lib/bedrock.d.ts.map +1 -0
- package/dist/lib/bedrock.js +35 -0
- package/dist/lib/bedrock.js.map +1 -0
- package/dist/lib/case-insensitive-headers.d.ts +15 -0
- package/dist/lib/case-insensitive-headers.d.ts.map +1 -0
- package/dist/lib/case-insensitive-headers.js +29 -0
- package/dist/lib/case-insensitive-headers.js.map +1 -0
- package/dist/lib/chromium-client-hints.d.ts +11 -0
- package/dist/lib/chromium-client-hints.d.ts.map +1 -0
- package/dist/lib/chromium-client-hints.js +17 -0
- package/dist/lib/chromium-client-hints.js.map +1 -0
- package/dist/lib/cookie-value.d.ts +8 -0
- package/dist/lib/cookie-value.d.ts.map +1 -0
- package/dist/lib/cookie-value.js +14 -0
- package/dist/lib/cookie-value.js.map +1 -0
- package/dist/lib/datadog.d.ts +8 -0
- package/dist/lib/datadog.d.ts.map +1 -0
- package/dist/lib/datadog.js +27 -0
- package/dist/lib/datadog.js.map +1 -0
- package/dist/lib/dd-metrics.d.ts +28 -0
- package/dist/lib/dd-metrics.d.ts.map +1 -0
- package/dist/lib/dd-metrics.js +62 -0
- package/dist/lib/dd-metrics.js.map +1 -0
- package/dist/lib/dispatch-metrics.d.ts +19 -0
- package/dist/lib/dispatch-metrics.d.ts.map +1 -0
- package/dist/lib/dispatch-metrics.js +65 -0
- package/dist/lib/dispatch-metrics.js.map +1 -0
- package/dist/lib/env.d.ts +37 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +70 -0
- package/dist/lib/env.js.map +1 -0
- package/dist/lib/errors.d.ts +15 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +20 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/http.d.ts +7 -0
- package/dist/lib/http.d.ts.map +1 -0
- package/dist/lib/http.js +14 -0
- package/dist/lib/http.js.map +1 -0
- package/dist/lib/job-tracking.d.ts +17 -0
- package/dist/lib/job-tracking.d.ts.map +1 -0
- package/dist/lib/job-tracking.js +19 -0
- package/dist/lib/job-tracking.js.map +1 -0
- package/dist/lib/llm/anthropic-client.d.ts +12 -0
- package/dist/lib/llm/anthropic-client.d.ts.map +1 -0
- package/dist/lib/llm/anthropic-client.js +23 -0
- package/dist/lib/llm/anthropic-client.js.map +1 -0
- package/dist/lib/llm/judge.d.ts +57 -0
- package/dist/lib/llm/judge.d.ts.map +1 -0
- package/dist/lib/llm/judge.js +123 -0
- package/dist/lib/llm/judge.js.map +1 -0
- package/dist/lib/llm/judges/error-messages.d.ts +38 -0
- package/dist/lib/llm/judges/error-messages.d.ts.map +1 -0
- package/dist/lib/llm/judges/error-messages.js +72 -0
- package/dist/lib/llm/judges/error-messages.js.map +1 -0
- package/dist/lib/llm/judges/invalid-fields.d.ts +43 -0
- package/dist/lib/llm/judges/invalid-fields.d.ts.map +1 -0
- package/dist/lib/llm/judges/invalid-fields.js +80 -0
- package/dist/lib/llm/judges/invalid-fields.js.map +1 -0
- package/dist/lib/llm/judges/modal-priority.d.ts +41 -0
- package/dist/lib/llm/judges/modal-priority.d.ts.map +1 -0
- package/dist/lib/llm/judges/modal-priority.js +74 -0
- package/dist/lib/llm/judges/modal-priority.js.map +1 -0
- package/dist/lib/llm/judges/select-option.d.ts +49 -0
- package/dist/lib/llm/judges/select-option.d.ts.map +1 -0
- package/dist/lib/llm/judges/select-option.js +90 -0
- package/dist/lib/llm/judges/select-option.js.map +1 -0
- package/dist/lib/llm/judges/verify-submit.d.ts +68 -0
- package/dist/lib/llm/judges/verify-submit.d.ts.map +1 -0
- package/dist/lib/llm/judges/verify-submit.js +89 -0
- package/dist/lib/llm/judges/verify-submit.js.map +1 -0
- package/dist/lib/llm/schemas.d.ts +220 -0
- package/dist/lib/llm/schemas.d.ts.map +1 -0
- package/dist/lib/llm/schemas.js +239 -0
- package/dist/lib/llm/schemas.js.map +1 -0
- package/dist/lib/logging.d.ts +39 -0
- package/dist/lib/logging.d.ts.map +1 -0
- package/dist/lib/logging.js +219 -0
- package/dist/lib/logging.js.map +1 -0
- package/dist/lib/multipart.d.ts +34 -0
- package/dist/lib/multipart.d.ts.map +1 -0
- package/dist/lib/multipart.js +55 -0
- package/dist/lib/multipart.js.map +1 -0
- package/dist/lib/option-matcher.d.ts +15 -0
- package/dist/lib/option-matcher.d.ts.map +1 -0
- package/dist/lib/option-matcher.js +69 -0
- package/dist/lib/option-matcher.js.map +1 -0
- package/dist/lib/phone.d.ts +11 -0
- package/dist/lib/phone.d.ts.map +1 -0
- package/dist/lib/phone.js +17 -0
- package/dist/lib/phone.js.map +1 -0
- package/dist/lib/random.d.ts +22 -0
- package/dist/lib/random.d.ts.map +1 -0
- package/dist/lib/random.js +33 -0
- package/dist/lib/random.js.map +1 -0
- package/dist/lib/statsd.d.ts +11 -0
- package/dist/lib/statsd.d.ts.map +1 -0
- package/dist/lib/statsd.js +68 -0
- package/dist/lib/statsd.js.map +1 -0
- package/dist/lib/telemetry/call-capture.d.ts +89 -0
- package/dist/lib/telemetry/call-capture.d.ts.map +1 -0
- package/dist/lib/telemetry/call-capture.js +190 -0
- package/dist/lib/telemetry/call-capture.js.map +1 -0
- package/dist/lib/telemetry/call-types.d.ts +65 -0
- package/dist/lib/telemetry/call-types.d.ts.map +1 -0
- package/dist/lib/telemetry/call-types.js +68 -0
- package/dist/lib/telemetry/call-types.js.map +1 -0
- package/dist/lib/telemetry/run-state.d.ts +15 -0
- package/dist/lib/telemetry/run-state.d.ts.map +1 -0
- package/dist/lib/telemetry/run-state.js +23 -0
- package/dist/lib/telemetry/run-state.js.map +1 -0
- package/dist/lib/telemetry/s3-sink.d.ts +48 -0
- package/dist/lib/telemetry/s3-sink.d.ts.map +1 -0
- package/dist/lib/telemetry/s3-sink.js +229 -0
- package/dist/lib/telemetry/s3-sink.js.map +1 -0
- package/dist/lib/telemetry/submission-capture.d.ts +45 -0
- package/dist/lib/telemetry/submission-capture.d.ts.map +1 -0
- package/dist/lib/telemetry/submission-capture.js +57 -0
- package/dist/lib/telemetry/submission-capture.js.map +1 -0
- package/dist/lib/telemetry/telemetry-paths.d.ts +59 -0
- package/dist/lib/telemetry/telemetry-paths.d.ts.map +1 -0
- package/dist/lib/telemetry/telemetry-paths.js +103 -0
- package/dist/lib/telemetry/telemetry-paths.js.map +1 -0
- package/dist/lib/tracking-click.d.ts +20 -0
- package/dist/lib/tracking-click.d.ts.map +1 -0
- package/dist/lib/tracking-click.js +75 -0
- package/dist/lib/tracking-click.js.map +1 -0
- package/dist/lib/url-capture.d.ts +8 -0
- package/dist/lib/url-capture.d.ts.map +1 -0
- package/dist/lib/url-capture.js +17 -0
- package/dist/lib/url-capture.js.map +1 -0
- package/dist/lib/us-states.d.ts +20 -0
- package/dist/lib/us-states.d.ts.map +1 -0
- package/dist/lib/us-states.js +86 -0
- package/dist/lib/us-states.js.map +1 -0
- package/dist/lib/zod-multipart.d.ts +18 -0
- package/dist/lib/zod-multipart.d.ts.map +1 -0
- package/dist/lib/zod-multipart.js +35 -0
- package/dist/lib/zod-multipart.js.map +1 -0
- package/dist/plugins/config-plugin.d.ts +77 -0
- package/dist/plugins/config-plugin.d.ts.map +1 -0
- package/dist/plugins/config-plugin.js +216 -0
- package/dist/plugins/config-plugin.js.map +1 -0
- package/dist/plugins/discover.d.ts +72 -0
- package/dist/plugins/discover.d.ts.map +1 -0
- package/dist/plugins/discover.js +265 -0
- package/dist/plugins/discover.js.map +1 -0
- package/dist/plugins/json-schema-to-zod.d.ts +47 -0
- package/dist/plugins/json-schema-to-zod.d.ts.map +1 -0
- package/dist/plugins/json-schema-to-zod.js +85 -0
- package/dist/plugins/json-schema-to-zod.js.map +1 -0
- package/dist/plugins/loader.d.ts +34 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +348 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/plugin-api-version.d.ts +8 -0
- package/dist/plugins/plugin-api-version.d.ts.map +1 -0
- package/dist/plugins/plugin-api-version.js +11 -0
- package/dist/plugins/plugin-api-version.js.map +1 -0
- package/dist/plugins/plugin-manifest-envelope.d.ts +16 -0
- package/dist/plugins/plugin-manifest-envelope.d.ts.map +1 -0
- package/dist/plugins/plugin-manifest-envelope.js +19 -0
- package/dist/plugins/plugin-manifest-envelope.js.map +1 -0
- package/dist/scraper/behavioral-signals.d.ts +16 -0
- package/dist/scraper/behavioral-signals.d.ts.map +1 -0
- package/dist/scraper/behavioral-signals.js +30 -0
- package/dist/scraper/behavioral-signals.js.map +1 -0
- package/dist/scraper/errors.d.ts +193 -0
- package/dist/scraper/errors.d.ts.map +1 -0
- package/dist/scraper/errors.js +226 -0
- package/dist/scraper/errors.js.map +1 -0
- package/dist/scraper/fixtures.d.ts +23 -0
- package/dist/scraper/fixtures.d.ts.map +1 -0
- package/dist/scraper/fixtures.js +48 -0
- package/dist/scraper/fixtures.js.map +1 -0
- package/dist/scraper/flow-runner.d.ts +1319 -0
- package/dist/scraper/flow-runner.d.ts.map +1 -0
- package/dist/scraper/flow-runner.js +5637 -0
- package/dist/scraper/flow-runner.js.map +1 -0
- package/dist/scraper/graphql-client.d.ts +30 -0
- package/dist/scraper/graphql-client.d.ts.map +1 -0
- package/dist/scraper/graphql-client.js +14 -0
- package/dist/scraper/graphql-client.js.map +1 -0
- package/dist/scraper/http-client.d.ts +75 -0
- package/dist/scraper/http-client.d.ts.map +1 -0
- package/dist/scraper/http-client.js +189 -0
- package/dist/scraper/http-client.js.map +1 -0
- package/dist/scraper/http-status-classifier.d.ts +12 -0
- package/dist/scraper/http-status-classifier.d.ts.map +1 -0
- package/dist/scraper/http-status-classifier.js +29 -0
- package/dist/scraper/http-status-classifier.js.map +1 -0
- package/dist/scraper/metrics.d.ts +50 -0
- package/dist/scraper/metrics.d.ts.map +1 -0
- package/dist/scraper/metrics.js +85 -0
- package/dist/scraper/metrics.js.map +1 -0
- package/dist/scraper/navigate.d.ts +20 -0
- package/dist/scraper/navigate.d.ts.map +1 -0
- package/dist/scraper/navigate.js +30 -0
- package/dist/scraper/navigate.js.map +1 -0
- package/dist/scraper/oracle-sentinels.d.ts +22 -0
- package/dist/scraper/oracle-sentinels.d.ts.map +1 -0
- package/dist/scraper/oracle-sentinels.js +40 -0
- package/dist/scraper/oracle-sentinels.js.map +1 -0
- package/dist/scraper/parse-json-response.d.ts +16 -0
- package/dist/scraper/parse-json-response.d.ts.map +1 -0
- package/dist/scraper/parse-json-response.js +37 -0
- package/dist/scraper/parse-json-response.js.map +1 -0
- package/dist/scraper/pool.d.ts +37 -0
- package/dist/scraper/pool.d.ts.map +1 -0
- package/dist/scraper/pool.js +105 -0
- package/dist/scraper/pool.js.map +1 -0
- package/dist/scraper/rate-limited-json-client.d.ts +35 -0
- package/dist/scraper/rate-limited-json-client.d.ts.map +1 -0
- package/dist/scraper/rate-limited-json-client.js +29 -0
- package/dist/scraper/rate-limited-json-client.js.map +1 -0
- package/dist/scraper/raw-fetch.d.ts +49 -0
- package/dist/scraper/raw-fetch.d.ts.map +1 -0
- package/dist/scraper/raw-fetch.js +40 -0
- package/dist/scraper/raw-fetch.js.map +1 -0
- package/dist/scraper/require-response-field.d.ts +23 -0
- package/dist/scraper/require-response-field.d.ts.map +1 -0
- package/dist/scraper/require-response-field.js +42 -0
- package/dist/scraper/require-response-field.js.map +1 -0
- package/dist/scraper/retry.d.ts +33 -0
- package/dist/scraper/retry.d.ts.map +1 -0
- package/dist/scraper/retry.js +122 -0
- package/dist/scraper/retry.js.map +1 -0
- package/dist/scraper/session-browserbase.d.ts +35 -0
- package/dist/scraper/session-browserbase.d.ts.map +1 -0
- package/dist/scraper/session-browserbase.js +198 -0
- package/dist/scraper/session-browserbase.js.map +1 -0
- package/dist/scraper/session-shared.d.ts +29 -0
- package/dist/scraper/session-shared.d.ts.map +1 -0
- package/dist/scraper/session-shared.js +35 -0
- package/dist/scraper/session-shared.js.map +1 -0
- package/dist/scraper/session-steel.d.ts +31 -0
- package/dist/scraper/session-steel.d.ts.map +1 -0
- package/dist/scraper/session-steel.js +140 -0
- package/dist/scraper/session-steel.js.map +1 -0
- package/dist/scraper/session-warmup.d.ts +31 -0
- package/dist/scraper/session-warmup.d.ts.map +1 -0
- package/dist/scraper/session-warmup.js +46 -0
- package/dist/scraper/session-warmup.js.map +1 -0
- package/dist/scraper/session.d.ts +21 -0
- package/dist/scraper/session.d.ts.map +1 -0
- package/dist/scraper/session.js +28 -0
- package/dist/scraper/session.js.map +1 -0
- package/dist/scraper/stagehand-guard.d.ts +128 -0
- package/dist/scraper/stagehand-guard.d.ts.map +1 -0
- package/dist/scraper/stagehand-guard.js +337 -0
- package/dist/scraper/stagehand-guard.js.map +1 -0
- package/dist/scraper/throttle.d.ts +21 -0
- package/dist/scraper/throttle.d.ts.map +1 -0
- package/dist/scraper/throttle.js +45 -0
- package/dist/scraper/throttle.js.map +1 -0
- package/dist/scripts/hca-inbox-poll.d.ts +19 -0
- package/dist/scripts/hca-inbox-poll.d.ts.map +1 -0
- package/dist/scripts/hca-inbox-poll.js +151 -0
- package/dist/scripts/hca-inbox-poll.js.map +1 -0
- package/dist/scripts/judge-llm-batch.d.ts +98 -0
- package/dist/scripts/judge-llm-batch.d.ts.map +1 -0
- package/dist/scripts/judge-llm-batch.js +350 -0
- package/dist/scripts/judge-llm-batch.js.map +1 -0
- package/dist/scripts/llm-heal.d.ts +165 -0
- package/dist/scripts/llm-heal.d.ts.map +1 -0
- package/dist/scripts/llm-heal.js +595 -0
- package/dist/scripts/llm-heal.js.map +1 -0
- package/dist/scripts/migrate-telemetry-dir-names.d.ts +57 -0
- package/dist/scripts/migrate-telemetry-dir-names.d.ts.map +1 -0
- package/dist/scripts/migrate-telemetry-dir-names.js +156 -0
- package/dist/scripts/migrate-telemetry-dir-names.js.map +1 -0
- package/dist/scripts/probe-setinputfiles.d.ts +28 -0
- package/dist/scripts/probe-setinputfiles.d.ts.map +1 -0
- package/dist/scripts/probe-setinputfiles.js +95 -0
- package/dist/scripts/probe-setinputfiles.js.map +1 -0
- package/dist/scripts/recon-browser.d.ts +304 -0
- package/dist/scripts/recon-browser.d.ts.map +1 -0
- package/dist/scripts/recon-browser.js +1833 -0
- package/dist/scripts/recon-browser.js.map +1 -0
- package/dist/scripts/recon-generate.d.ts +182 -0
- package/dist/scripts/recon-generate.d.ts.map +1 -0
- package/dist/scripts/recon-generate.js +2572 -0
- package/dist/scripts/recon-generate.js.map +1 -0
- package/dist/scripts/recon-heal.d.ts +176 -0
- package/dist/scripts/recon-heal.d.ts.map +1 -0
- package/dist/scripts/recon-heal.js +584 -0
- package/dist/scripts/recon-heal.js.map +1 -0
- package/dist/scripts/recon-http.d.ts +19 -0
- package/dist/scripts/recon-http.d.ts.map +1 -0
- package/dist/scripts/recon-http.js +377 -0
- package/dist/scripts/recon-http.js.map +1 -0
- package/dist/scripts/recon-replay-jobs.d.ts +21 -0
- package/dist/scripts/recon-replay-jobs.d.ts.map +1 -0
- package/dist/scripts/recon-replay-jobs.js +221 -0
- package/dist/scripts/recon-replay-jobs.js.map +1 -0
- package/dist/scripts/recon-shared.d.ts +52 -0
- package/dist/scripts/recon-shared.d.ts.map +1 -0
- package/dist/scripts/recon-shared.js +68 -0
- package/dist/scripts/recon-shared.js.map +1 -0
- package/dist/scripts/recon-summarize.d.ts +15 -0
- package/dist/scripts/recon-summarize.d.ts.map +1 -0
- package/dist/scripts/recon-summarize.js +233 -0
- package/dist/scripts/recon-summarize.js.map +1 -0
- package/dist/scripts/smoke-test.d.ts +22 -0
- package/dist/scripts/smoke-test.d.ts.map +1 -0
- package/dist/scripts/smoke-test.js +236 -0
- package/dist/scripts/smoke-test.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +189 -0
- package/dist/server.js.map +1 -0
- package/dist/site-plugin.d.ts +226 -0
- package/dist/site-plugin.d.ts.map +1 -0
- package/dist/site-plugin.js +13 -0
- package/dist/site-plugin.js.map +1 -0
- package/dist/testing/answers-fixture.d.ts +13 -0
- package/dist/testing/answers-fixture.d.ts.map +1 -0
- package/dist/testing/answers-fixture.js +37 -0
- package/dist/testing/answers-fixture.js.map +1 -0
- package/dist/testing/batch-email-confirmation.d.ts +58 -0
- package/dist/testing/batch-email-confirmation.d.ts.map +1 -0
- package/dist/testing/batch-email-confirmation.js +62 -0
- package/dist/testing/batch-email-confirmation.js.map +1 -0
- package/dist/testing/batch-report.d.ts +33 -0
- package/dist/testing/batch-report.d.ts.map +1 -0
- package/dist/testing/batch-report.js +30 -0
- package/dist/testing/batch-report.js.map +1 -0
- package/dist/testing/contract-parity-suite.d.ts +55 -0
- package/dist/testing/contract-parity-suite.d.ts.map +1 -0
- package/dist/testing/contract-parity-suite.js +39 -0
- package/dist/testing/contract-parity-suite.js.map +1 -0
- package/dist/testing/coverage-guard-suite.d.ts +46 -0
- package/dist/testing/coverage-guard-suite.d.ts.map +1 -0
- package/dist/testing/coverage-guard-suite.js +39 -0
- package/dist/testing/coverage-guard-suite.js.map +1 -0
- package/dist/testing/fixtures/resume.pdf +54 -0
- package/dist/testing/integration-runner.d.ts +62 -0
- package/dist/testing/integration-runner.d.ts.map +1 -0
- package/dist/testing/integration-runner.js +46 -0
- package/dist/testing/integration-runner.js.map +1 -0
- package/dist/testing/mock-fetch-response.d.ts +7 -0
- package/dist/testing/mock-fetch-response.d.ts.map +1 -0
- package/dist/testing/mock-fetch-response.js +16 -0
- package/dist/testing/mock-fetch-response.js.map +1 -0
- package/dist/testing/persona-fixture.d.ts +77 -0
- package/dist/testing/persona-fixture.d.ts.map +1 -0
- package/dist/testing/persona-fixture.js +83 -0
- package/dist/testing/persona-fixture.js.map +1 -0
- package/dist/testing/replay-integration-suite.d.ts +50 -0
- package/dist/testing/replay-integration-suite.d.ts.map +1 -0
- package/dist/testing/replay-integration-suite.js +45 -0
- package/dist/testing/replay-integration-suite.js.map +1 -0
- package/dist/testing/resume-fixture.d.ts +39 -0
- package/dist/testing/resume-fixture.d.ts.map +1 -0
- package/dist/testing/resume-fixture.js +38 -0
- package/dist/testing/resume-fixture.js.map +1 -0
- package/dist/testmail/client.d.ts +61 -0
- package/dist/testmail/client.d.ts.map +1 -0
- package/dist/testmail/client.js +190 -0
- package/dist/testmail/client.js.map +1 -0
- package/dist/testmail/errors.d.ts +18 -0
- package/dist/testmail/errors.d.ts.map +1 -0
- package/dist/testmail/errors.js +29 -0
- package/dist/testmail/errors.js.map +1 -0
- package/dist/types/dispatch-metrics.d.ts +46 -0
- package/dist/types/dispatch-metrics.d.ts.map +1 -0
- package/dist/types/dispatch-metrics.js +25 -0
- package/dist/types/dispatch-metrics.js.map +1 -0
- package/dist/types/logging.d.ts +11 -0
- package/dist/types/logging.d.ts.map +1 -0
- package/dist/types/logging.js +3 -0
- package/dist/types/logging.js.map +1 -0
- 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
|