@@ -326,6 +326,40 @@ async def get_url(self) -> str:
326326 self ._cached_url = url
327327 return url
328328
329+ async def read_markdown (self , max_chars : int = 8000 ) -> str | None :
330+ """
331+ Read page content as markdown for semantic understanding.
332+
333+ This extracts the page HTML and converts it to markdown format,
334+ which is useful for LLM planning to understand page context
335+ (e.g., product listings, form fields, navigation structure).
336+
337+ Args:
338+ max_chars: Maximum characters to return (default 8000).
339+ Truncates from the end if content exceeds this limit.
340+
341+ Returns:
342+ Markdown string if successful, None if extraction fails.
343+ """
344+ try :
345+ page = getattr (self .backend , "page" , None )
346+ if page is None :
347+ return None
348+
349+ # Import here to avoid circular dependency
350+ from .read import _fallback_read_from_page_async
351+
352+ result = await _fallback_read_from_page_async (page , output_format = "markdown" )
353+ if result is None or result .status != "success" :
354+ return None
355+
356+ content = result .content
357+ if len (content ) > max_chars :
358+ content = content [:max_chars ]
359+ return content
360+ except Exception :
361+ return None
362+
329363 async def get_viewport_height (self ) -> int :
330364 """
331365 Get current viewport height in pixels.
@@ -398,19 +432,23 @@ async def click(self, element_id: int) -> None:
398432
399433 await self .record_action (f"CLICK({ element_id } )" )
400434
401- async def type (self , element_id : int , text : str ) -> None :
435+ async def type (self , element_id : int , text : str , * , delay_ms : float | None = None ) -> None :
402436 """
403437 Type text into an element.
404438
405439 Args:
406440 element_id: Element ID from snapshot
407441 text: Text to type
442+ delay_ms: Optional delay between keystrokes in milliseconds
408443 """
409444 # First click to focus
410445 await self .click (element_id )
411446
412447 # Then type
413- await self .backend .type_text (text )
448+ if delay_ms is None :
449+ await self .backend .type_text (text )
450+ else :
451+ await self .backend .type_text (text , delay_ms = delay_ms )
414452 await self .record_action (f"TYPE({ element_id } , '{ text [:20 ]} ...')" if len (text ) > 20 else f"TYPE({ element_id } , '{ text } ')" )
415453
416454 async def press (self , key : str ) -> None :
0 commit comments