<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christian Bromann</title>
    <description>The latest articles on DEV Community by Christian Bromann (@christian_bromann).</description>
    <link>https://dev.to/christian_bromann</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F799374%2F9892cff5-d13c-4bd9-af53-1125008130af.jpeg</url>
      <title>DEV Community: Christian Bromann</title>
      <link>https://dev.to/christian_bromann</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/christian_bromann"/>
    <language>en</language>
    <item>
      <title>Build Better Agent UX: Streaming Progress, Status, and File Ops with LangChain</title>
      <dc:creator>Christian Bromann</dc:creator>
      <pubDate>Thu, 15 Jan 2026 18:38:38 +0000</pubDate>
      <link>https://dev.to/christian_bromann/build-better-agent-ux-streaming-progress-status-and-file-ops-with-langchain-3dg3</link>
      <guid>https://dev.to/christian_bromann/build-better-agent-ux-streaming-progress-status-and-file-ops-with-langchain-3dg3</guid>
      <description>&lt;p&gt;If you’ve built an agent UI before, you know the uncomfortable truth: most “progress” indicators are vibes.&lt;/p&gt;

&lt;p&gt;A spinner means &lt;em&gt;something&lt;/em&gt; is happening… probably.&lt;br&gt;&lt;br&gt;
But users don’t need theater — they need &lt;strong&gt;truth&lt;/strong&gt;: what’s running &lt;em&gt;right now&lt;/em&gt;, how far along it is, and what the agent is doing to their filesystem.&lt;/p&gt;

&lt;p&gt;In this short video, I show how to stream &lt;strong&gt;custom, TypeScript-safe events&lt;/strong&gt; from a LangGraph tool call directly into a React UI.&lt;/p&gt;

&lt;p&gt;🎥 Video: &lt;code&gt;https://youtu.be/3daSUNpWErQ&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  What you’ll build
&lt;/h3&gt;

&lt;p&gt;A simple pattern:&lt;/p&gt;

&lt;p&gt;1) &lt;strong&gt;Your tool emits events&lt;/strong&gt; (progress/status/file ops) while it runs&lt;br&gt;&lt;br&gt;
2) &lt;strong&gt;The frontend subscribes&lt;/strong&gt; and renders those events immediately&lt;br&gt;&lt;br&gt;
3) &lt;strong&gt;Type guards&lt;/strong&gt; keep the UI logic safe and predictable&lt;/p&gt;

&lt;p&gt;No polling loops. No guessing. No “thinking…” placeholders.&lt;/p&gt;
&lt;h3&gt;
  
  
  1) Emit typed custom events from a tool call
&lt;/h3&gt;

&lt;p&gt;Inside the tool call, write custom events as the work progresses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;?.({&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;analysisId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// stable id =&amp;gt; update in place&lt;/span&gt;
  &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;totalSteps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;currentStep&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;toolCall&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toolCall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;satisfies&lt;/span&gt; &lt;span class="nx"&gt;ProgressData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the key shift: &lt;strong&gt;tools aren’t just functions&lt;/strong&gt; — they’re event producers.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Receive those events in React
&lt;/h3&gt;

&lt;p&gt;In the UI, pass a handler into the stream hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;onCustomEvent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;handleCustomEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every event emitted by your tool arrives in the client &lt;em&gt;as it happens&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) Narrow event types and update UI state predictably
&lt;/h3&gt;

&lt;p&gt;Treat incoming events as &lt;code&gt;unknown&lt;/code&gt;, then narrow with type guards and update state maps keyed by &lt;code&gt;id&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isProgressData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* update progress */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isStatusData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* update status */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isFileStatusData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* update file ops */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps the frontend stable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;progress updates &lt;em&gt;in place&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;minimal re-renders&lt;/li&gt;
&lt;li&gt;no stringly-typed event spaghetti&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why it matters (beyond “nice UI”)
&lt;/h3&gt;

&lt;p&gt;When the UI reflects real execution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;users trust the agent more&lt;/li&gt;
&lt;li&gt;debugging becomes dramatically easier&lt;/li&gt;
&lt;li&gt;failures are understandable without digging through logs&lt;/li&gt;
&lt;li&gt;you can build better UX: step indicators, timelines, file operation feeds, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build something with this pattern, I’d love to see it — share a screenshot or link in the comments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>javascript</category>
      <category>agents</category>
    </item>
    <item>
      <title>Giving a Chat App Operational Access to My Cloudflare Account with MCP</title>
      <dc:creator>Christian Bromann</dc:creator>
      <pubDate>Thu, 18 Dec 2025 16:02:24 +0000</pubDate>
      <link>https://dev.to/christian_bromann/giving-a-chat-app-operational-access-to-my-cloudflare-account-with-mcp-2j50</link>
      <guid>https://dev.to/christian_bromann/giving-a-chat-app-operational-access-to-my-cloudflare-account-with-mcp-2j50</guid>
      <description>&lt;p&gt;I recently built a small demo that changed how I think about agent tooling and platform integrations.&lt;/p&gt;

&lt;p&gt;In short, I gave a chat-based application operational access to my entire Cloudflare account by connecting Cloudflare’s managed MCP servers to a LangChain agent powered by Claude.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No custom APIs.&lt;/li&gt;
&lt;li&gt;No giant tool schemas.&lt;/li&gt;
&lt;li&gt;No hand-written adapters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just MCP.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;Most agent setups today treat tools as static. You define them upfront, wire them into prompts, and hope the model uses them correctly.&lt;/p&gt;

&lt;p&gt;That works for small demos. It starts breaking down once you point an agent at a real platform like Cloudflare, which exposes hundreds of possible operations across many services.&lt;/p&gt;

&lt;p&gt;MCP changes that model.&lt;/p&gt;

&lt;p&gt;Instead of shipping a massive list of tools, the agent connects to MCP servers that describe available capabilities. The model can then discover tools dynamically at runtime and load only what it actually needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;In the demo, I connect a LangChainJS agent to multiple Cloudflare MCP servers, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the Browser MCP to fetch and convert live web pages&lt;/li&gt;
&lt;li&gt;the Docs MCP for up-to-date Cloudflare documentation&lt;/li&gt;
&lt;li&gt;Cloudflare’s GraphQL MCP for analytics queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using Anthropic’s native MCP toolset and tool search, Claude explores the available tools, decides what to use, and executes them on demand.&lt;/p&gt;

&lt;p&gt;From the outside, it just looks like a chat interface. Under the hood, that chat app has real operational access to my Cloudflare account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this felt different
&lt;/h2&gt;

&lt;p&gt;What stood out to me while building this is how little glue code was needed.&lt;/p&gt;

&lt;p&gt;I didn’t have to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;model every API endpoint as a tool&lt;/li&gt;
&lt;li&gt;maintain schemas as the platform evolves&lt;/li&gt;
&lt;li&gt;preload hundreds of tools into the prompt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, the agent discovers capabilities as it goes. Tools feel less like rigid contracts and more like something the agent can explore.&lt;/p&gt;

&lt;p&gt;That’s a big shift if you’re thinking about agents interacting with real infrastructure, not just answering questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why MCP matters
&lt;/h2&gt;

&lt;p&gt;MCP is interesting not because it’s another protocol, but because it enables a different mental model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;platforms expose capabilities, not static tools&lt;/li&gt;
&lt;li&gt;agents discover and load functionality dynamically&lt;/li&gt;
&lt;li&gt;providers like Cloudflare can safely expose large surfaces without overwhelming the model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This feels much closer to how we’ll want agents to interact with production systems going forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code and the video
&lt;/h2&gt;

&lt;p&gt;I walk through the full setup, including the LangChain agent, MCP configuration, and dynamic tool discovery, in this video:&lt;/p&gt;

&lt;p&gt;🎥 &lt;a href="https://www.youtube.com/watch?v=n-Hw_K_GsOg" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=n-Hw_K_GsOg&lt;/a&gt;&lt;br&gt;
👉 &lt;a href="https://github.com/christian-bromann/langchat" rel="noopener noreferrer"&gt;https://github.com/christian-bromann/langchat&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re experimenting with MCP or thinking about giving agents access to real platforms, I’d love to hear what you’re building.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>llm</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Automating a Browser with Anthropic’s Computer Use to Play Tic-Tac-Toe</title>
      <dc:creator>Christian Bromann</dc:creator>
      <pubDate>Tue, 16 Dec 2025 18:29:45 +0000</pubDate>
      <link>https://dev.to/christian_bromann/automating-a-browser-with-anthropics-computer-use-to-play-tic-tac-toe-3de2</link>
      <guid>https://dev.to/christian_bromann/automating-a-browser-with-anthropics-computer-use-to-play-tic-tac-toe-3de2</guid>
      <description>&lt;p&gt;For years, the “agent” story was mostly &lt;strong&gt;text → API calls → text&lt;/strong&gt;. That works when software exposes clean endpoints, but the real world is full of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Legacy UIs&lt;/strong&gt; with no API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SaaS products&lt;/strong&gt; where the API is incomplete or locked down&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflows that span apps&lt;/strong&gt; (browser + spreadsheet + admin UI)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tasks where the UI &lt;em&gt;is&lt;/em&gt; the source of truth&lt;/strong&gt; (what’s visible, what’s enabled, what error banners appear)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Provider-native &lt;strong&gt;computer use&lt;/strong&gt; tools are a response to that gap: they let a model operate software the same way a human does—by &lt;strong&gt;seeing the screen&lt;/strong&gt; and &lt;strong&gt;performing input actions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;OpenAI frames this as a “Computer-Using Agent” capability aimed at controlling real interfaces and measuring progress on benchmarks like OSWorld (a sign they’re treating UI control as a first-class modality, not a hack) (&lt;a href="https://openai.com/index/computer-using-agent/" rel="noopener noreferrer"&gt;OpenAI: Computer-Using Agent&lt;/a&gt;). Anthropic positions “computer use” as enabling Claude to interact with existing interfaces directly while highlighting operational safety concerns (e.g., isolate execution in a dedicated environment) (&lt;a href="https://docs.anthropic.com/en/docs/build-with-claude/computer-use" rel="noopener noreferrer"&gt;Anthropic computer use docs&lt;/a&gt;, &lt;a href="https://www.anthropic.com/news/3-5-models-and-computer-use" rel="noopener noreferrer"&gt;Anthropic announcement&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Under the hood, the important idea is &lt;strong&gt;standardization&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Providers define a &lt;strong&gt;tool schema&lt;/strong&gt; (action types, fields, image formats).&lt;/li&gt;
&lt;li&gt;They train (and safety-tune) models to &lt;strong&gt;reliably emit that schema&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;They enforce &lt;strong&gt;constraints&lt;/strong&gt; (environment type, context handling) that make the loop workable in production.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s why these tools matter: you’re not just “running Selenium with an LLM”—you’re using a model/tool pair designed together as a control system.&lt;/p&gt;




&lt;h3&gt;
  
  
  What “computer use” enables at a technical level
&lt;/h3&gt;

&lt;p&gt;Provider computer-use is basically a minimal OS/UI control API with three properties:&lt;/p&gt;

&lt;h4&gt;
  
  
  1) A perception channel grounded in pixels
&lt;/h4&gt;

&lt;p&gt;The model can request a &lt;strong&gt;screenshot&lt;/strong&gt; and interpret UI state: text, layout, icons, highlights, banners, disabled buttons, etc. This is the “state observation” step in a control loop.&lt;/p&gt;

&lt;h4&gt;
  
  
  2) A constrained action vocabulary
&lt;/h4&gt;

&lt;p&gt;Instead of arbitrary code execution, the model emits actions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;click / move / drag&lt;/li&gt;
&lt;li&gt;type / keypress&lt;/li&gt;
&lt;li&gt;scroll&lt;/li&gt;
&lt;li&gt;wait&lt;/li&gt;
&lt;li&gt;screenshot (again)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This constraint is &lt;em&gt;good&lt;/em&gt;: fewer degrees of freedom means fewer unsafe/irreversible actions and more predictable orchestration.&lt;/p&gt;

&lt;h4&gt;
  
  
  3) Closed-loop autonomy
&lt;/h4&gt;

&lt;p&gt;The model can iterate: &lt;strong&gt;observe → act → observe&lt;/strong&gt;, handling uncertainty and recovery:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Did my click land?”&lt;/li&gt;
&lt;li&gt;“Did the UI change?”&lt;/li&gt;
&lt;li&gt;“Do I need to wait for the next state?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is what makes “computer use” different from one-shot vision: it’s not just recognition; it’s interactive control.&lt;/p&gt;




&lt;h3&gt;
  
  
  How your Tic-Tac-Toe project leverages these tools (and what it demonstrates)
&lt;/h3&gt;

&lt;p&gt;This demo is valuable because it isolates the core computer-use loop without lots of app complexity—and still exposes the hard parts.&lt;/p&gt;

&lt;h4&gt;
  
  
  1) The UI becomes the “API surface”
&lt;/h4&gt;

&lt;p&gt;Your agent does &lt;strong&gt;not&lt;/strong&gt; get a structured board array. It must infer the board from screenshots and interact via clicks. That’s the entire point of computer-use: operate systems where the UI &lt;em&gt;is&lt;/em&gt; the interface.&lt;/p&gt;

&lt;p&gt;To make that reliable, the project adds an important “agent affordance”: &lt;strong&gt;cell labels&lt;/strong&gt; (&lt;code&gt;TOP-LEFT&lt;/code&gt;, &lt;code&gt;CENTER&lt;/code&gt;, …). This is a general pattern: if you want robust UI control, you design UI elements that are easy for vision models to anchor on (stable text, consistent placement, clear state cues).&lt;/p&gt;

&lt;h4&gt;
  
  
  2) You turn the model into a controller, not a narrator
&lt;/h4&gt;

&lt;p&gt;The implementation forces an explicit loop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Take screenshot&lt;/li&gt;
&lt;li&gt;Choose a move&lt;/li&gt;
&lt;li&gt;Click&lt;/li&gt;
&lt;li&gt;Take screenshot to verify&lt;/li&gt;
&lt;li&gt;Wait for opponent&lt;/li&gt;
&lt;li&gt;Repeat&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That “verify after action” step is the difference between a demo that “usually works” and one that can recover from inevitable UI mistakes.&lt;/p&gt;

&lt;h4&gt;
  
  
  3) You anchor termination to UI truth (critical for reliability)
&lt;/h4&gt;

&lt;p&gt;Both prompts insist the agent must only end the game when it &lt;strong&gt;sees&lt;/strong&gt; the on-screen banner (“Player X wins!”, “It’s a draw!”), not when it &lt;em&gt;believes&lt;/em&gt; it has three in a row.&lt;/p&gt;

&lt;p&gt;This is a broadly applicable safety/reliability pattern for computer-use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Never end (or submit, pay, delete, send) based on internal inference alone&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Require &lt;strong&gt;screen evidence&lt;/strong&gt; for critical transitions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It reduces hallucinated “success” and makes runs auditable.&lt;/p&gt;

&lt;h4&gt;
  
  
  4) You surface real provider constraints (OpenAI truncation, Anthropic context bloat)
&lt;/h4&gt;

&lt;p&gt;Provider-native tools come with operational requirements that show up immediately in multi-step UI loops:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OpenAI&lt;/strong&gt;: your agent sets &lt;code&gt;truncation: "auto"&lt;/code&gt; because OpenAI’s computer-use flow expects automatic truncation to keep long interactive sessions viable (&lt;a href="https://platform.openai.com/docs/guides/tools-computer-use" rel="noopener noreferrer"&gt;OpenAI computer use guide&lt;/a&gt;). This is a concrete example of “provider tool != generic LLM call”; there are mode-specific runtime contracts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Anthropic&lt;/strong&gt;: your agent uses middleware to clear old tool uses (screenshots). That’s essentially &lt;strong&gt;context garbage collection&lt;/strong&gt;—and it’s not optional in screenshot-heavy loops. Without pruning, you hit context limits or degrade performance as stale observations pile up.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one of the biggest “why computer-use is hard” lessons: the environment is unstructured, and the data (images) is heavy.&lt;/p&gt;

&lt;h4&gt;
  
  
  5) You demonstrate why providers add &lt;em&gt;more than&lt;/em&gt; computer control: persistent memory
&lt;/h4&gt;

&lt;p&gt;The Anthropic player adds a native memory tool and stores learnings as markdown (strategy, opponent patterns, mistakes). In practice, this turns a single-session controller into something that can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;review prior outcomes before starting&lt;/li&gt;
&lt;li&gt;encode opponent-specific openings&lt;/li&gt;
&lt;li&gt;avoid repeating mistakes across games&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The demo’s memory files show exactly the value proposition: the agent loses once due to a missed threat, then blocks the same pattern next game. That’s a minimal but real example of “agent improvement” that’s hard to get from prompts alone.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why this matters beyond Tic-Tac-Toe
&lt;/h3&gt;

&lt;p&gt;This project is a good representation of where computer-use shines &lt;strong&gt;and&lt;/strong&gt; where it bites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shines&lt;/strong&gt; when you need to automate UI-only workflows quickly, without building bespoke integrations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bites&lt;/strong&gt; because reliability depends on:

&lt;ul&gt;
&lt;li&gt;UI stability and “readability”&lt;/li&gt;
&lt;li&gt;verification loops&lt;/li&gt;
&lt;li&gt;context management&lt;/li&gt;
&lt;li&gt;isolation/sandboxing (providers explicitly recommend this for safety) (&lt;a href="https://docs.anthropic.com/en/docs/build-with-claude/computer-use" rel="noopener noreferrer"&gt;Anthropic computer use docs&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;In other words: computer-use is best understood as a &lt;strong&gt;systems discipline&lt;/strong&gt;—a control loop combining model behavior, tool constraints, UI design, and runtime safeguards.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>automation</category>
    </item>
    <item>
      <title>Keep Your Apps Accessible and Your e2e Tests Stable With WebdriverIOs New Accessibility Selector</title>
      <dc:creator>Christian Bromann</dc:creator>
      <pubDate>Mon, 05 Sep 2022 17:35:55 +0000</pubDate>
      <link>https://dev.to/christian_bromann/keep-your-apps-accessible-and-your-e2e-tests-stable-with-webdriverios-new-accessibility-selector-4fkf</link>
      <guid>https://dev.to/christian_bromann/keep-your-apps-accessible-and-your-e2e-tests-stable-with-webdriverios-new-accessibility-selector-4fkf</guid>
      <description>&lt;p&gt;Fetching elements within e2e tests can sometimes be very hard. Complex CSS paths or arbitrary test ids make them either less readable or prone to failures. The disappointment we experience when our test fail is by far not comparable to a the bad experience people have when they need to use assistent devices like screen readers on applications build without accessibility in mind.&lt;/p&gt;

&lt;p&gt;With the accessibility selector introduced in version &lt;code&gt;v7.24.0&lt;/code&gt; WebdriverIO now provides a powerful way to fetch various of elements containing a certain accessibility name. Rather than applying arbitrary &lt;code&gt;data-testId&lt;/code&gt; properties to elements which won't be recognised by assistent devices, developers or QA engineers can now either apply a correct accessibility name to the element themselves or ask the development team to improve the accessibility so that writing tests becomes easier.&lt;/p&gt;

&lt;p&gt;WebdriverIO internally uses a chain of xPath selector conditions to fetch the correct element. While the framework has no access to the accessibility tree of the browser, it can only guess the correct name here. As accessibility names are computed based on author supplied names and content names, WebdriverIO fetches an element based in a certain order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First we try to find an element that has an &lt;code&gt;aria-labelledBy&lt;/code&gt; or &lt;code&gt;aria-describedBy&lt;/code&gt; property pointing to an element containing a valid id, e.g.:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"social"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Social Media&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;aria-labelledBy=&lt;/span&gt;&lt;span class="s"&gt;"social"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we can fetch a certain link within our navigation via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria/Social Media&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a=API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Then we look for elements with a certain &lt;code&gt;aria-label&lt;/code&gt;, e.g.:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"close button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;X&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rather than using &lt;code&gt;X&lt;/code&gt; to fetch the element or applying a test id property we can just do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria/close button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Well defined HTML forms provide a label to every input element, e.g.:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Username&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting the value of the input can now be done via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria/Username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foobar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Less ideal but still working are &lt;code&gt;placeholder&lt;/code&gt; or &lt;code&gt;aria-placeholder&lt;/code&gt; properties:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Your Username"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which can now be used to fetch elements as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria/Your Username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foobar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Furthermore if an image tag provides a certain alternative text, this can be used to query that element as well, e.g.:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A warm sommer night"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Such an image can be now fetched via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria/A warm sommer night&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;getTagName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// outputs "img"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Lastly, if no proper accessibility name can be derived, it is computed by its accumulated text, e.g.:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Welcome!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Such a heading tag can be now fetched via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria/Welcome!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;getTagName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// outputs "h1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, there are a variety of ways to define the accessibility name of an element. Many of the browser debugging tools provide handy accessibility features that help you to find the proper name of the element:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lRZhJdAf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://webdriver.io/img/ally.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lRZhJdAf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://webdriver.io/img/ally.png" alt="Getting Accessibility Name in Chrome DevTools" width="880" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For more information check out the &lt;a href="https://developer.chrome.com/docs/devtools/accessibility/reference/#pane"&gt;Chrome DevTools&lt;/a&gt; or &lt;a href="https://firefox-source-docs.mozilla.org/devtools-user/accessibility_inspector/"&gt;Firefox Accessibility Inspector&lt;/a&gt; docs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Accessibility is not only a powerful tool to create an inclusive web, it can also help you write stable and readable tests. While you should &lt;strong&gt;not&lt;/strong&gt; go ahead and give every element an &lt;code&gt;aria-label&lt;/code&gt;, this new selector can help you build web applications with accessibility in mind so that writing e2e tests for it later on will become much easier.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>webdriverio</category>
      <category>selenium</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
