{"slug": "build-an-ai-website-monitor-with-typescript-and-agent-browser", "title": "Build an AI Website Monitor with TypeScript and Agent Browser", "summary": "A developer at Telnyx published Watchdog, an open-source TypeScript example that uses a Telnyx Edge Stateful Actor and the beta Agent Browser to monitor websites for experience-level changes rather than just HTTP 200 responses. The system captures a healthy baseline, runs a real Chromium session on a daily schedule to observe pages and interact with forms, then sends an SMS via Telnyx Messaging when a verdict flips between healthy and broken. Each watched hostname maps to its own durable actor with independent state, scheduling, and incident history.", "body_md": "An uptime check can tell you that a page returned HTTP `200`. It cannot tell you that the price changed unexpectedly, a form stopped submitting, or a client-side deployment removed an important field.\n\n[Watchdog](https://github.com/team-telnyx/telnyx-code-examples/tree/main/watchdog) is an open-source TypeScript example that checks the experience, not only the response code. It remembers a healthy baseline, wakes on a schedule, drives a real browser through the page, records evidence, and sends an SMS when the result changes.\n\nThe application combines a Telnyx Edge Stateful Actor, Agent Browser, SQL, KV, AI Inference, and Messaging. This tutorial explains how those pieces fit together and why its retry rules matter.\n\n**Beta notice:** Telnyx Agent Browser is currently in beta. Request shapes, actions, and error behavior may change. Check the [current Agent Browser documentation](https://developers.telnyx.com/docs/agent-browser) before using this pattern in production.\n\nOne actor owns one watched hostname. Its state includes:\n\nThe first scheduled run captures the baseline. Later runs:\n\n`finally` block.\nThe verdict is either `healthy` or `broken` with a reason. A broken verdict opens or updates an incident. A later healthy run closes that incident with before-and-after evidence.\n\nThe actor identity comes from the target hostname:\n\n```\nexport function watchId(targetUrl: string): string {\n  return new URL(targetUrl).hostname\n}\n\nconst actor = env.WATCHDOG.idFromName(watchId(targetUrl))\n```\n\nTwo URLs on the same hostname belong to the same watchdog. A different hostname gets a separate actor with independent state, scheduling, and incident history.\n\nThis makes the watched property a durable entity instead of a row that a stateless cron task must reconstruct on every run.\n\nWhen a watch is armed, the actor stores its configuration and schedules both the first check and the recurring daily check:\n\n``` js\nconst dailyId = await this.every(\n  86400,\n  \"runCheck\",\n  { reason: \"scheduled\" },\n  { id: `daily-${ownerId}` },\n)\n\nawait this.schedule(\n  5,\n  \"runCheck\",\n  { reason: \"first\", run_key: firstRunKey },\n  { id: `first-${ownerId}` },\n)\n```\n\nThe first run occurs shortly after setup. Subsequent checks run daily.\n\nThe actor calls the Agent Browser REST API to create a short-lived Chromium session. The request restricts navigation to the watched hostname and applies hard lifetime limits:\n\n```\n{\n  \"policy\": {\n    \"allowed_domains\": [\"shop.example.com\"]\n  },\n  \"limits\": {\n    \"lifetime_s\": 900,\n    \"idle_timeout_s\": 120\n  },\n  \"idempotency_key\": \"2026-11-06\"\n}\n```\n\nThe actor polls the session until it becomes ready. A session returned by an idempotent replay may already be closed, failed, or expired, so the code checks for terminal states and creates a fresh session when needed.\n\nAgent Browser provides isolated sessions plus structured actions for navigation, observation, interaction, and screenshots. The official [Agent Browser overview](https://developers.telnyx.com/docs/agent-browser) describes the browser as short-lived and isolated, with semantic observations designed for model tool use.\n\nAfter navigation, an `observe` action returns an `observation_id`, a text representation of the page, and element refs. The model works with those refs rather than pixel coordinates.\n\nThe watchdog asks the configured inference model to identify four elements:\n\nIt then performs `fill` and `click` actions using both the current observation ID and the selected ref. The [Agent Browser actions documentation](https://developers.telnyx.com/docs/agent-browser/actions) requires `observation_id` for ref-based interactions and documents the available action types.\n\nThe model does not decide whether the HTTP request succeeded. It helps map the structured observation to the page elements the deterministic workflow needs.\n\nThe first successful run stores the observed price and form labels as the baseline:\n\n```\ninterface Baseline {\n  price_text: string\n  form_labels: string[]\n}\n```\n\nLater runs compare the pre-submit observation with that baseline. The post-submit observation has a different purpose: it proves whether the interaction reached the expected success state.\n\nKeeping those observations separate avoids a subtle mistake. A form submission may navigate or rerender the page, so the final page is not necessarily the correct source for pricing comparison.\n\nThe Stateful Actor owns the current lifecycle, while `WATCHDOG_DB` stores an audit-friendly ledger.\n\nThe `runs` table records values including:\n\nThe `incidents` table records the first broken time, reason, evidence artifact IDs, and open or closed status.\n\nScheduled runs use the UTC date as their stable run key. Before starting, the actor checks whether that key already has a verdict:\n\n``` js\nconst prior = await this.rows<{ verdict: string | null }>(\n  \"SELECT verdict FROM runs WHERE run_key = ?\",\n  runKey,\n)\n\nif (prior[0]?.verdict && (reason === \"scheduled\" || reason === \"first\")) return\n```\n\nThat guard makes a restarted or replayed scheduled task a no-op instead of a duplicate browser run or duplicate incident.\n\nKV holds the small indexes needed to route incoming commands:\n\n``` php\nwatch/<hostname> -> owner phone\nowner/<phone>    -> watched hostnames\nincident/<id>    -> watched hostname\n```\n\nThe owner can control the watchdog by SMS:\n\n```\nwatch https://shop.example.com/pricing\nstatus\nrecheck now\nrecheck <incident_id>\n```\n\nIncoming messaging webhooks are Ed25519-verified in live mode. The sender is then checked against the watch owner before the actor can expose status or queue another browser run. Unknown senders cannot trigger paid sessions or read incident state.\n\nThe most reusable part of this sample is its error model. Browser automation is different from a read-only API request because retrying a mutation may repeat an effect.\n\nThe DOM may change after an observation. Agent Browser reports `stale_ref` or `stale_observation` as an action failure inside an HTTP `200` response.\n\nThe watchdog does not retry the old ref. It observes again, asks for current refs, and retries once. A second consecutive stale result is recorded as an incident note rather than guessed away.\n\nAn `execution_lost` result does not prove that a fill or click failed to take effect. Repeating the action immediately could submit a form twice.\n\nThe actor first observes the current page and checks for the expected effect. It repeats the mutation only when the effect did not land, using a fresh observation. If the browser session is terminal, the actor reconciles the original outcome before using a new session for remaining work.\n\nThis follows the official recovery guidance: an unknown outcome must be inspected before a mutation is repeated.\n\nA `429 quota_exceeded` response means the concurrent browser-session quota is full. It is not handled with a tight retry loop.\n\nThe actor closes any session it owns, schedules `runCheck` for 60 seconds later with the same run key, and returns. The stable run key keeps the delayed attempt replay-safe.\n\nClone the sample and install its dependencies:\n\n```\ngit clone https://github.com/team-telnyx/telnyx-code-examples.git\ncd telnyx-code-examples/watchdog\nnpm install\ncp .env.example .env\n```\n\nLeave this setting enabled initially:\n\n```\nDEMO_MODE=true\n```\n\nDemo mode replays recorded Agent Browser envelopes from `demo/browser-fixtures.json`. It exercises baseline capture, stale-ref recovery, a broken verdict, incident creation, and fix verification without opening a live browser session or sending an SMS.\n\nRun the smoke test:\n\n```\nnpm run smoke\n```\n\nThe test covers the end-to-end demo loop, restart behavior, incident deduplication, owner authorization, live-mode webhook refusal without a valid signature, and fixture consistency.\n\nThe sample's `telnyx.toml` declares:\n\n`WATCHDOG` actor namespace`WATCHDOG_DB` SQL storage`WATCHDOG_INDEX` KV storage`TELNYX` binding\nReplace the placeholder resource IDs and configure the required secrets before deployment. Use placeholders rather than committing credentials or real phone numbers.\n\nGenerate binding types and deploy:\n\n```\nnpx telnyx-edge types\nnpm run deploy\n```\n\nAgent Browser runs in the cloud, so the target must be a public HTTPS URL. It cannot reach `localhost` or a private service. The sample includes a fixture page available from the deployed function at `/fixture`.\n\nArm a watch through the HTTP API:\n\n```\ncurl -X POST \"$PUBLIC_BASE_URL/watch\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"url\": \"https://your-public-host.example.com/fixture\",\n    \"owner\": \"<OWNER_PHONE_E164>\"\n  }'\n```\n\nFor live mode, configure `TELNYX_API_KEY`, `TELNYX_PUBLIC_KEY`, `WATCH_OWNER_PHONE`, `FROM_NUMBER`, and a supported `AI_MODEL`, then set `DEMO_MODE=false`. Point the Messaging Profile webhook to:\n\n```\nhttps://<YOUR_DEPLOYED_HOST>/webhooks/messaging\n```\n\nThe sample demonstrates the architecture, but production monitoring needs additional policy:\n\nWatchdog is a website-monitoring example, but its pattern applies to any stateful browser workflow:\n\nThe complete TypeScript implementation, recorded browser fixtures, API notes, and smoke test are available in the [Watchdog code example](https://github.com/team-telnyx/telnyx-code-examples/tree/main/watchdog).", "url": "https://wpnews.pro/news/build-an-ai-website-monitor-with-typescript-and-agent-browser", "canonical_source": "https://dev.to/sonam_gupta_dba00ec9473e5/build-an-ai-website-monitor-with-typescript-and-agent-browser-ip1", "published_at": "2026-11-06 23:43:06+00:00", "updated_at": "2026-11-06 23:53:23.995471+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-products"], "entities": ["Telnyx", "Watchdog", "Agent Browser", "GitHub", "Chromium"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/build-an-ai-website-monitor-with-typescript-and-agent-browser", "markdown": "https://wpnews.pro/news/build-an-ai-website-monitor-with-typescript-and-agent-browser.md", "text": "https://wpnews.pro/news/build-an-ai-website-monitor-with-typescript-and-agent-browser.txt", "jsonld": "https://wpnews.pro/news/build-an-ai-website-monitor-with-typescript-and-agent-browser.jsonld"}}