NEW in v0.13.0 - Built-in CAPTCHA detection with configurable handling strategies for browser automation agents.
Sentience is designed as infrastructure for token-efficient browser automation, not as a full-service automation provider. We deliberately limit our scope to detection and verification for two reasons:
As an infrastructure layer, Sentience focuses on providing reliable, efficient browser automation primitives. CAPTCHA resolution involves domain-specific policies, third-party integrations, and compliance considerations that vary significantly across organizations and use cases.
Different organizations have distinct security policies, legal requirements, and acceptable use guidelines governing CAPTCHA handling. By delegating resolution to customer-controlled systems, Sentience avoids imposing a one-size-fits-all approach and enables customers to implement solutions that align with their specific compliance obligations.
Sentience detects CAPTCHAs, pauses execution, and verifies clearance. You decide how to resolve them using your own workflows or external systems that comply with your organization's security policies.
When the Sentience SDK detects a CAPTCHA during browser automation, the following flow occurs:
snapshot.diagnostics.captcha with a confidence score.captcha.detected == true and confidence >= minConfidence, the runtime invokes your configured policy.wait_until_cleared, the runtime continuously re-snapshots until captcha.detected == false or timeout is reached.Policies determine what happens when a CAPTCHA is detected:
| Policy | Behavior |
|---|---|
abort | Stop execution immediately when CAPTCHA is detected. Safest default for workflows where CAPTCHA indicates an unexpected state. |
callback | Invoke your custom handler function once per CAPTCHA incident, allowing you to decide the appropriate action dynamically. |
Actions are the runtime behaviors your handler can request:
| Action | Behavior | Details |
|---|---|---|
abort | Terminate the run | Sets reason_code to captcha_policy_abort |
retry_new_session | Reset and retry | Closes current browser session, opens fresh one, retries from beginning. Bounded by maxRetriesNewSession (default: 3) |
wait_until_cleared | Pause and poll | Suspends execution, periodically re-snapshots until CAPTCHA is no longer detected or timeoutMs expires |
| Option | Type | Default | Description |
|---|---|---|---|
minConfidence | number | 0.7 | Minimum confidence threshold (0-1) to trigger CAPTCHA handling |
timeoutMs | number | 120000 | Maximum time (ms) to wait for CAPTCHA clearance |
pollMs | number | 1000 | Interval (ms) between re-snapshot attempts during wait_until_cleared |
maxRetriesNewSession | number | 3 | Maximum retry_new_session attempts before aborting |
Sentience provides three built-in strategy helpers. These are not solvers; they configure the runtime's response to CAPTCHA detection and rely on external systems or humans for actual resolution.
| Strategy | Purpose | Use Case |
|---|---|---|
HumanHandoffSolver | Pause execution and signal a human operator | Live sessions, monitoring dashboards, manual intervention workflows |
VisionSolver | Use vision to confirm clearance (no clicking/typing) | Automated verification after external resolution |
ExternalSolver | Call your webhook/service, then wait for clearance | Integration with third-party CAPTCHA services or custom internal systems |
Immediately stop when CAPTCHA is detected. No resolution attempted.
from sentience import AgentRuntime, CaptchaOptions
runtime.set_captcha_options(
CaptchaOptions(
policy="abort",
min_confidence=0.7,
)
)When CAPTCHA is detected, the runtime pauses and waits for a human to solve it in the live browser session.
from sentience import CaptchaOptions, HumanHandoffSolver
runtime.set_captcha_options(
CaptchaOptions(
policy="callback",
handler=HumanHandoffSolver(),
min_confidence=0.7,
timeout_ms=120_000, # Wait up to 2 minutes
poll_ms=1_000, # Re-check every second
)
)Uses vision to confirm the CAPTCHA has cleared. Does not click or type. Useful after an external system has already solved the CAPTCHA.
from sentience import CaptchaOptions, VisionSolver
runtime.set_captcha_options(
CaptchaOptions(policy="callback", handler=VisionSolver())
)Calls your webhook/service when CAPTCHA is detected, then waits for clearance. Your external system performs the actual resolution.
from sentience import CaptchaOptions, ExternalSolver
async def notify_webhook(ctx) -> None:
"""
Example hook: send context to your external system.
Replace with your own client / queue / webhook call.
Sentience does NOT implement solver logic.
"""
print(f"[captcha] external resolver notified: url={ctx.url} run_id={ctx.run_id}")
# Example: await your_http_client.post(webhook_url, json={...})
runtime.set_captcha_options(
CaptchaOptions(
policy="callback",
handler=ExternalSolver(lambda ctx: notify_webhook(ctx)),
timeout_ms=180_000, # 3 minutes for external service
)
)import asyncio
import os
from sentience import (
AgentRuntime,
AsyncSentienceBrowser,
CaptchaOptions,
ExternalSolver,
HumanHandoffSolver,
VisionSolver,
)
from sentience.tracing import JsonlTraceSink, Tracer
async def notify_webhook(ctx) -> None:
"""
Example hook: send context to your external system.
Replace with your own client / queue / webhook call.
Sentience does NOT implement solver logic.
"""
print(f"[captcha] external resolver notified: url={ctx.url} run_id={ctx.run_id}")
# Example: await your_http_client.post(webhook_url, json={...})
async def main() -> None:
tracer = Tracer(run_id="captcha-demo", sink=JsonlTraceSink("trace.jsonl"))
async with AsyncSentienceBrowser() as browser:
page = await browser.new_page()
runtime = await AgentRuntime.from_sentience_browser(
browser=browser,
page=page,
tracer=tracer,
)
# ---------------------------------------------------------------------
# Option 1: Human-in-loop (recommended for live sessions)
# ---------------------------------------------------------------------
# When CAPTCHA is detected, the runtime pauses and waits for a human
# to solve it in the live browser session.
runtime.set_captcha_options(
CaptchaOptions(
policy="callback",
handler=HumanHandoffSolver(),
min_confidence=0.7,
timeout_ms=120_000, # Wait up to 2 minutes
poll_ms=1_000, # Re-check every second
)
)
# ---------------------------------------------------------------------
# Option 2: Vision-only verification (no DOM actions)
# ---------------------------------------------------------------------
# Uses vision to confirm clearance. Does not click or type.
runtime.set_captcha_options(
CaptchaOptions(policy="callback", handler=VisionSolver())
)
# ---------------------------------------------------------------------
# Option 3: External resolver orchestration
# ---------------------------------------------------------------------
# Calls your webhook/service, then waits for clearance.
runtime.set_captcha_options(
CaptchaOptions(
policy="callback",
handler=ExternalSolver(lambda ctx: notify_webhook(ctx)),
timeout_ms=180_000, # 3 minutes for external service
)
)
# ---------------------------------------------------------------------
# Option 4: Abort policy (safest for production)
# ---------------------------------------------------------------------
# Immediately stop when CAPTCHA is detected. No resolution attempted.
runtime.set_captcha_options(
CaptchaOptions(policy="abort", min_confidence=0.7)
)
# Navigate and take snapshot (CAPTCHA handling triggers automatically)
await page.goto(os.environ.get("CAPTCHA_TEST_URL", "https://example.com"))
runtime.begin_step("Captcha-aware snapshot")
await runtime.snapshot()
if __name__ == "__main__":
asyncio.run(main())Sentience detects the following CAPTCHA providers with high confidence:
| Provider | provider_hint | Detection Signals |
|---|---|---|
| Google reCAPTCHA | recaptcha | iframe src, .g-recaptcha, [data-sitekey] |
| hCaptcha | hcaptcha | iframe src, .h-captcha |
| Cloudflare Turnstile | turnstile | iframe src, .cf-turnstile, [data-cf-turnstile-sitekey] |
| Arkose Labs (FunCaptcha) | arkose | iframe src, #FunCaptcha, [data-arkose-public-key] |
| AWS WAF CAPTCHA | awswaf | iframe src, [data-awswaf-captcha], script src |
| Generic/Unknown | unknown | Text keywords: "verify you are human", "unusual traffic", "security check" |
The snapshot.diagnostics.captcha object contains:
type CaptchaDiagnostics = {
detected: boolean;
provider_hint?: "recaptcha" | "hcaptcha" | "turnstile" | "arkose" | "awswaf" | "unknown";
confidence: number; // 0..1
evidence: {
text_hits: string[]; // Matched text keywords
selector_hits: string[]; // Matched CSS selectors
iframe_src_hits: string[]; // Matched iframe sources
url_hits: string[]; // Matched URL patterns
};
};
The detection uses a multi-signal scoring system:
A CAPTCHA is considered detected when confidence >= 0.7 (configurable via minConfidence).
If you integrate an external provider (e.g., 2captcha, Anti-Captcha) or your own internal system:
The external system performs the actual resolution. Sentience monitors for clearance.
Your handler receives a context object with:
| Field (Python) | Field (TypeScript) | Description |
|---|---|---|
run_id | runId | Current run identifier |
step_index | stepIndex | Current step number |
url | url | Page URL where CAPTCHA was detected |
captcha | captcha | CAPTCHA diagnostics (provider_hint, confidence, evidence) |
Keep audit logs and ensure your resolution approach complies with your organization's policies (consent, allowed domains, rate limits).
Your handler should return or allow the default wait_until_cleared action. The runtime then confirms clearance before resuming.
Set appropriate timeouts based on your external service's SLA. External services may take 30-180 seconds to resolve.
CAPTCHA events are automatically emitted as verification events to the tracer:
{
"type": "verification",
"data": {
"kind": "captcha",
"label": "captcha_detected",
"passed": false,
"reason": "CAPTCHA detected: recaptcha (confidence: 0.85)",
"details": {
"provider_hint": "recaptcha",
"confidence": 0.85,
"evidence": {
"selector_hits": [".g-recaptcha"],
"iframe_src_hits": ["https://www.google.com/recaptcha/..."]
}
}
},
"step_id": "abc-123"
}
policy: "abort" in production until you understand your CAPTCHA patterns